Skip to content

Instantly share code, notes, and snippets.

@hrgdavor
Last active September 24, 2020 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hrgdavor/a55e27701ae3080e75998944a9f471ff to your computer and use it in GitHub Desktop.
Save hrgdavor/a55e27701ae3080e75998944a9f471ff to your computer and use it in GitHub Desktop.
OpenJSCAD example, spoon organizers
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