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 hidden or 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