Last active
September 24, 2020 13:08
-
-
Save hrgdavor/a55e27701ae3080e75998944a9f471ff to your computer and use it in GitHub Desktop.
OpenJSCAD example, spoon organizers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var funcs = {}; | |
function getParameterDefinitions() { | |
var defs = []; | |
defs.push({ | |
name:'piece', | |
caption:'Piece', type:'choice', | |
values: [ | |
'funcs.Spoon_Small', | |
'funcs.Spoon_Small_Long', | |
'funcs.Spoon_Mid', | |
'funcs.Spoon_Big', | |
'funcs.Fork_Big', | |
'funcs.Fork_Small', | |
'funcs.Knives', | |
'funcs.Knivec_Ceramic', | |
'funcs.mali_nozevi', | |
'funcs.Chop_Sticks_Korean', | |
'funcs.Scissors', | |
'funcs.Spacer', | |
].map(p=>p.substring(6))}); | |
return defs; | |
} | |
function main(params){ | |
return funcs[params.piece](); | |
} | |
// maxH = 68 | |
funcs.Spoon_Small = function(params){ | |
return _zlica({ | |
h: 60, | |
w1: 26.5, | |
w2: 8.5, | |
depth: 30, | |
depth2: 20, | |
len: 138, | |
th:2.5, | |
}); | |
} | |
funcs.Spoon_Mid = function(params){ | |
return _zlica({ | |
h: 40, | |
w1: 29, | |
w2: 9.5, | |
depth: 35, | |
depth2: 20, | |
len: 180, | |
th:2, | |
}); | |
} | |
funcs.Fork_Small = function(params){ | |
return _zlica({ | |
h: 60, | |
w1: 22, | |
w2: 10, | |
depth: 36, | |
depth2: 20, | |
len: 173, | |
th:2.5 | |
}); | |
} | |
funcs.Spoon_Big = function(params){ | |
return _zlica({ | |
h: 65, | |
w1: 42, | |
w2: 13, | |
depth: 45, | |
depth2: 20, | |
len: 206, | |
th:3, | |
}); | |
} | |
funcs.Fork_Big = function(params){ | |
return _zlica({ | |
h: 65, | |
w1: 25, | |
w2: 13, | |
depth: 45, | |
depth2: 20, | |
len: 206, | |
th:3, | |
}); | |
} | |
function _zlica({h= 45, w1= 26.5, w2= 8.5, depth= 40, depth2=20, len= 140, th= 2.5}){ | |
return [ | |
difference( | |
bottomCube({size:[w1 + th*2, len + th*2, th+1]}), | |
bottomCube({size:[w1, len, th], tz:1}) | |
), | |
translate([0,-len/2 + depth/2 - th,0],difference(...[ | |
bottomCube({size:[w1 + th*2, depth, h], tz:1}), | |
bottomCube({size:[w1, depth, h], ty:th, tz:1}), | |
])), | |
translate([0,len/2 - depth2/2 + th,0],difference(...[ | |
bottomCube({size:[w2 + th*2, depth2, h], tz:1}), | |
bottomCube({size:[w2, depth2, h], ty:-th, tz:1}), | |
])) | |
]; | |
} | |
funcs.Knives = function({h=55, w2=13, w3=22,depth2=20, len= 232, th=3}={}){ | |
var p = new P2DPoints(0,3); | |
p.x(depth2+3).y(-3).x(-depth2); | |
p.y(-6).add(PointUtils.curve,{y:5, mirror:1}).m(50,-17).y(-3) | |
p.xTo(3).y(-w2).x(depth2).y(-3).x(-depth2 - 3); | |
var w1 = 39+4; | |
return [ | |
difference( | |
bottomCube({size:[len + th*2, w1 + th*2,th+2]}), | |
bottomCube({size:[len, w1, th+2], tz:1}) | |
), | |
translate([-len/2-th,w1/2,0],...p.build(h)), | |
rotate([0,0,180],translate([-len/2-th,w1/2,0],...p.build(h))) | |
]; | |
} | |
funcs.mali_nozevi = function(){ | |
var h=20; | |
var lPinch = 40; | |
var wKnife =16; | |
return [ | |
bottomCube({size:[lPinch,wKnife+6.4,1], tx:-lPinch/2}), | |
sideBox({h,len:lPinch-2,w:4, th:1.2, yzero:'middle', ty: wKnife/2}), | |
sideBox({h,len:lPinch-2,w:4, th:1.2, yzero:'middle', ty:-wKnife/2}), | |
]; | |
} | |
funcs.Knivec_Ceramic = function(){ | |
var wKnife = 18; | |
var lPinch = 27; | |
var bottom = 1; | |
var h=25; | |
var p = new P2DPoints() | |
// top parts | |
p.y(wKnife).x(45) | |
p.y(wKnife).x(30) | |
p.y(wKnife).x(lPinch) | |
// diagonal back | |
p.y(-wKnife) | |
p.m(-30, -wKnife) | |
p.m(-45, -wKnife) | |
var p2 = new P2DPoints(); | |
p2.x(-5).y(190 - lPinch - 10).x(2); | |
var outline = p2.build(h,{w:2, closed:false}); | |
p2.m(20,-30).y(-100) | |
return [ | |
// bottomCube({size:[lPinch, wKnife, bottom]}), | |
// bottomCube({size:[lPinch, wKnife, bottom], ty:wKnife, tx:-50}), | |
// bottomCube({size:[lPinch, wKnife, bottom], ty:wKnife*2, tx:-50-40}), | |
// translate([130,65,0],rotate([0,0,110], | |
// ...p2.build(1), | |
// ...outline | |
// )), | |
...p.build(1,{tx:lPinch/2, ty:-wKnife/2}), | |
sideBox({h,len:lPinch-2,w:4,tx:lPinch/2, ty:wKnife/2-4.3-1.5, th:1.2, xzero:'right'}), | |
sideBox({h,len:lPinch-2,w:4,tx:lPinch/2+45, ty:wKnife/2-4.3-1.5+wKnife, th:1.2, xzero:'right'}), | |
sideBox({h,len:lPinch-2,w:4,tx:lPinch/2+45+30, ty:wKnife/2-4.3-1.5+wKnife+wKnife, th:1.2, xzero:'right'}), | |
]; | |
} | |
funcs.Chop_Sticks_Korean = function({h=40, w2=13, w3=22,depth2=20, len= 225, th=2}={}){ | |
var w1 = 52.5; | |
var stickW1 = 5; | |
var stickW2 = 8; | |
return [ | |
difference( | |
bottomCube({size:[len + th*2, w1 + th*2, th+2]}), | |
bottomCube({size:[len, w1, th+2], tz:1}) | |
), | |
sideBox({h,len:20, w:5, tx:len/2+th, ty:w1/2+th-2}), | |
sideBox({h,len:20, w:7.5, tx:-len/2-th, ty:w1/2+th, xzero:'right'}), | |
sideBox({h,len:40, w:43, tx:len/2+th, ty:-w1/2-th, yzero:'top'}), | |
sideBox({h,len:20, w:12, tx:-len/2-th, ty:3, xzero:'right'}), | |
]; | |
} | |
funcs.Spoon_Small_Long = function({h=45, w2=9, w2x=17, w3=32, depth2=20, len= 216, th=2}={}){ | |
var w1 = 32; | |
return [ | |
difference( | |
bottomCube({size:[len + th*2, w1 + th*2, th+2]}), | |
bottomCube({size:[len, w1, th+2], tz:1}) | |
), | |
sideBox({h,len:9, w:w2, tx:len/2+th, yzero:'middle'}), | |
sideBox({h,len:40, w:w3, tx:-len/2-th, xzero:'right', yzero:'middle'}), | |
bottomCube({size:[15,th,h], tx:len/2+th-18, ty:th/2+w2x/2}), | |
bottomCube({size:[15,th,h], tx:len/2+th-18, ty:-th/2-w2x/2}), | |
bottomCube({size:[th, w2x/2-w2/2,h], tx:len/2+th-10, ty:th/2+w2-1.5}), | |
bottomCube({size:[th, w2x/2-w2/2,h], tx:len/2+th-10, ty:-th/2-w2+1.5}), | |
]; | |
} | |
funcs.Scissors = function(){ | |
var p = new P2DPoints(); | |
p.y(-80).x(60).m(150,80) | |
return [ | |
...p.build(1), | |
...p.build(55,{w:2,closed:false}), | |
] | |
} | |
funcs.Spacer = function(){ | |
var w = 40; | |
var len = 150; | |
var h = 50; | |
var th = 2; | |
var p = new P2DPoints(); | |
var corner = th*3; | |
p.y(-corner).x(corner).add(PointUtils.curve,{y:1.3}).m(-corner,corner); | |
return [ | |
translate([w/2-corner-th,len/2,1],rotate([0,-90,90],...p.build(len))), | |
bottomCube({size:[w,len,1]}), | |
bottomCube({size:[th,len,h], tx:w/2-th/2}), | |
] | |
} | |
function sideBox({h=45,len=20, w=10, th=2, yzero='bottom', xzero='left', tx=0, ty=0, tz=0}={}){ | |
var _tx = -(len+th)/2; | |
var _ty = -(w/2 + th); | |
var dx = -th/2; | |
if(yzero == 'top') _ty *= -1; | |
if(yzero == 'middle') _ty = 0; | |
if(xzero == 'right'){ | |
dx *= -1; | |
_tx *= -1; | |
} | |
return difference( | |
bottomCube({size:[len + th, w + 2*th, h], tx:_tx+tx, ty:_ty+ty,tz}), | |
bottomCube({size:[len, w, h], tx:_tx+tx+dx, ty:_ty + ty,tz}) | |
) | |
} | |
var PointUtils = {}; | |
PointUtils.arc = function(len, {y=10, segments=4}){ | |
var p = []; | |
var x = len/2; | |
var r = y + ((x*x - y*y)/2/y * (y > x ? 1:1)); | |
var rad = Math.atan2(r-y, x)* (y > x ? 1:1); | |
console.log(rad); | |
var step = (Math.PI/2 - rad ) / segments; | |
p.push([x,y]); | |
for(var i=segments-1; i>=0; i--){ | |
var y2 = r * Math.sin(rad+step*i); | |
var x2 = r * Math.cos(rad+step*i); | |
p.push([x+x2,y2-r+y]); | |
p.unshift([x-x2,y2-r+y]); | |
} | |
return p; | |
} | |
PointUtils.curve = function(len, {i=2, px=0.3, py=0.2, y=0}){ | |
var sqr = x=>x*x | |
if(y) py = y/len; | |
var p = []; | |
var segments = 4; | |
var top = len*py; | |
var dx = len*px; | |
for(var i=0; i<segments; i++){ | |
p.push([dx/segments*i, top-sqr(1/segments*(segments-i))*top]) | |
} | |
p.push([len*px,top]); | |
var dx2 = len-dx; | |
for(var i=1; i<segments; i++){ | |
p.push([dx+dx2/segments*i, top-sqr(1/segments*i)*top]) | |
} | |
return p; | |
} | |
PointUtils.bump = function({d=2,w=2}={}){ | |
return new P2DPoints(0,0).y(d).x(w).y(-d); | |
} | |
PointUtils.flipPoints = function(points){ | |
points.forEach(p=>{ | |
let tmp = p[0]; | |
p[0] = p[1]; | |
p[1] = tmp; | |
}); | |
} | |
PointUtils.scalePoints = function(points, sx, sy){ | |
if(sx == 1 && sy == 1) return; | |
points.forEach(p=>{ | |
p[0] = p[0] * sx; | |
p[1] = p[1] * sy; | |
}); | |
} | |
PointUtils.rotatePoints = function(points, rad){ | |
if(rad == 0) return; | |
points.forEach(p=>{ | |
var x = p[0], y=p[1]; | |
p[0] = x * Math.cos(rad) - y * Math.sin(rad); | |
p[1] = y * Math.cos(rad) + x * Math.sin(rad); | |
}); | |
} | |
PointUtils.translatePoints = function(points, tx, ty){ | |
points.forEach(p=>{ | |
p[0] = p[0] + tx; | |
p[1] = p[1] + ty; | |
}); | |
} | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
// COPY / PASTE UTILITY | |
function P2DPoints(x=0, y=0){ | |
this.pos = {x,y}; | |
this.points = [[x,y]]; | |
} | |
var proto = P2DPoints.prototype; | |
proto.add = function(shape,options={}){ | |
this.points.push({shape,options}); | |
return this; | |
} | |
proto.at = function(x=0,y=0){ | |
this.points.push([this.pos.x = x, this.pos.y = y]); | |
return this; | |
} | |
proto.m = function(x=0, y=0){ | |
return this.at(this.pos.x + x, this.pos.y + y); | |
} | |
proto.x = function(x=0){ | |
return this.m(x, 0); | |
} | |
proto.y = function(y){ | |
return this.m(0, y); | |
} | |
proto.xTo = function(x=0){ | |
return this.at(x, this.pos.y); | |
} | |
proto.yTo = function(y=0){ | |
return this.at(this.pos.x, y); | |
} | |
proto.aRad = function(rad, r){ | |
return this.m(r * Math.cos(rad), r * Math.sin(rad)); | |
} | |
proto.aDeg = function(deg, r){ | |
return this.aRad(deg/180 * Math.PI, r); | |
} | |
proto.xRad = function(rad, x){ | |
return this.m(x, x * Math.sin(rad) / Math.cos(rad)); | |
} | |
proto.xDeg = function(deg, x){ | |
return this.xRad(deg/180 * Math.PI, x); | |
} | |
proto.yRad = function(rad, y){ | |
return this.m(y * Math.cos(rad) / Math.sin(rad), y); | |
} | |
proto.yDeg = function(deg, y){ | |
return this.xRad(deg/180 * Math.PI, y); | |
} | |
proto.calcPoints = function(){ | |
this.points = this.buildPoints(); | |
} | |
proto.mirrorY = function({a=1,b=0, m0=-1, m1=1}={}){ | |
return this.mirrorX({a,b,m0,m1}); | |
} | |
proto.mirrorX = function({a=0, b=1, m0=1, m1=-1}={}){ | |
var points = this.points; | |
// remove start vertical | |
var p0 = points[0]; | |
var p1 = points[1]; | |
if(!p1.shape && p0[a] == p1[a]) points.shift(); | |
// remove end vertical | |
var pEnd0 = points[points.length -1]; | |
var pEnd1 = points[points.length -2]; | |
if(!pEnd1.shape && pEnd0[a] == pEnd1[a]) points.pop(); | |
var count = points.length; | |
for(var i=count-1; i>=0;i--){ | |
var p = points[i]; | |
if(p.shape){ | |
points.push(p); | |
}else{ | |
points.push([p[0]*m0, p[1]*m1]); | |
} | |
} | |
} | |
proto.buildPoints = function(){ | |
var sqr = x=>x*x; | |
var ret = []; | |
var points = this.points; | |
const myFix2 = n=>parseFloat(n.toFixed(2)) | |
const isEqPoint = (p1,p2)=> myFix2(p1[0]) == myFix2(p2[0]) && myFix2(p1[1]) == myFix2(p2[1]); | |
for(var i=0; i<points.length; i++){ | |
var point = points[i]; | |
if(point.shape){ | |
var prev = points[i-1]; | |
var next = points[i+1]; | |
var {options, shape} = point; | |
var tx=0, ty=0, distance=0, rad=0; | |
var deltaX = next[0] - prev[0]; | |
var deltaY = next[1] - prev[1]; | |
distance = Math.sqrt(sqr(next[0] - prev[0]) + sqr(next[1] - prev[1])); | |
if(typeof shape == 'function') shape = shape(distance, options); | |
var toAdd = shape.buildPoints ? shape.buildPoints():shape; | |
var rad = Math.atan2(deltaY, deltaX); | |
if(options.mirror) PointUtils.scalePoints(toAdd,1,-1); | |
if(options.start != undefined) tx = options.start; | |
if(options.center || options.end != undefined){ | |
var len = 0; | |
toAdd.forEach(p=>{if(p[0] > len) len = p[0]}); | |
if(options.center){ | |
tx = distance/2 - len/2 + (options.start || 0); | |
} | |
if(options.end != undefined){ | |
tx = distance - len - options.end; | |
} | |
} | |
if(rad && tx) { | |
// use tx to calc ty before it is changed | |
ty = tx * Math.sin(rad) | |
tx = tx * Math.cos(rad) | |
} | |
if(rad) PointUtils.rotatePoints(toAdd, rad) | |
if(tx) PointUtils.translatePoints(toAdd,tx,ty); | |
var pStart = points[0]; | |
//PointUtils.translatePoints(toAdd, prev[0]-pStart[0], prev[1]-pStart[1]) | |
PointUtils.translatePoints(toAdd, prev[0], prev[1]) | |
while(isEqPoint(prev,toAdd[0])) toAdd.shift(); | |
while(isEqPoint(next,toAdd[toAdd.length-1])) toAdd.pop(); | |
ret.push(...toAdd); | |
}else{ | |
ret.push([...point]) | |
} | |
} | |
while(isEqPoint(ret[0], ret[ret.length-1])) ret.pop(); | |
return ret; | |
} | |
function myFix2(n){ return parseFloat(n.toFixed(2))} | |
proto.build = function(h=0, {preview=0, closed=true, tx=0, ty=0, sx=1,sy=1, w=0}={}){ | |
var points = this.buildPoints(); | |
if(sx != 1 || sy != 1) PointUtils.scalePoints(points, sx,sy); | |
if(tx != 0 || ty != 0) PointUtils.translatePoints(points, tx,ty); | |
if(!preview) try{ | |
var shape = CAG.fromPoints(points); | |
if(w) return [rectangular_extrude(points, {w , h, closed})] | |
if(h) return [linear_extrude({height: h}, shape)] | |
return [shape]; | |
}catch(e){ | |
console.log(e.message,e); | |
} | |
preview = preview || 0.1; | |
var tmp = this.points.filter(p=>!p.shape); | |
var ret = [rectangular_extrude(points, {w:preview , h:preview, closed})]; | |
if(tmp.length != this.points.length){ | |
ret.push(color('yellow',translate([0,0,-preview], rectangular_extrude(tmp, {w:preview , h:preview, closed})))) | |
} | |
for(var i=0; i<points.length; i++){ | |
var p = points[i]; | |
ret.push( | |
color('blue', | |
translate([p[0],p[1],preview], | |
cylinder({r:preview/2, h:preview}) | |
) | |
) | |
); | |
} | |
return ret; | |
} | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
//************************************************************************************************************* | |
function bottomCube(options){ | |
console.log('toBottom',options.toBottom, options.size[2]); | |
options.center = true; | |
ret = translate([options.tx || 0, options.ty || 0, options.size[2]/2 + (options.toBottom || options.tz || 0) ], cube(options)); | |
if(options.dir) ret = dirRotate(options.dir,[ret]); | |
return ret; | |
} | |
function cornerCube(options){ | |
options.center = true; | |
ret = translate([ | |
options.size[0]/2 + (options.tx || 0), | |
options.size[1]/2 + (options.ty || 0), | |
options.size[2]/2 + (options.toBottom || options.tz || 0) ], cube(options)); | |
if(options.dir) ret = dirRotate(options.dir,[ret]); | |
return ret; | |
} | |
function bottomCylinder(options){ | |
options.center = true; | |
ret = translate([options.tx || 0, options.ty || 0, options.h/2 + (options.toBottom || options.tz || 0) ], cylinder(options)); | |
if(options.dir) ret = dirRotate(options.dir,[ret]); | |
return ret; | |
} | |
function dirRotate(dir,elems){ | |
if(dir == 'B') return rotate([180,0,0],...elems); | |
if(dir == 'S') return rotate([90,90,0],...elems); | |
if(dir == 'N') return rotate([-90,90,0],...elems); | |
if(dir == 'E') return rotate([0,90,0],...elems); | |
if(dir == 'W') return rotate([0,-90,0],...elems); | |
return union(...elems); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment