Last active
August 17, 2016 16:33
-
-
Save ryansturmer/94549b2c4e766a15a1e353522ca9cdd4 to your computer and use it in GitHub Desktop.
Parametric Baby Chair
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
// Walter's Chair | |
var woodColor = [0.93,0.83,0.67]; | |
function getParameterDefinitions() { | |
return [ | |
{ name: 'seatDepth', type: 'float', initial: 12, caption: "Seat Depth" }, | |
{ name: 'seatWidth', type: 'float', initial: 12, caption: "Seat Width" }, | |
{ name: 'chairHeight', type: 'float', initial: 12, caption: "Chair Height" }, | |
{ name: 'seatHeight', type: 'float', initial: 7, caption: "Seat Height" }, | |
{ name: 'sideRadius', type: 'float', initial: 1.75, caption: "Side Radius" }, | |
{ name: 'sideTabSize', type: 'float', initial:2.0, caption: "Tab Size"}, | |
{ name: 'materialThickness', type: 'float', initial: 0.5, caption: "Material Thickness" }, | |
{ name: 'slotFitAllowance', type: 'float', initial:0.015, caption: "Fit Allowance"}, | |
{ name: 'boltCount', type: 'int', initial:2, caption: "Screws per Side"}, | |
{ name: 'handles', | |
type : 'choice', | |
values : [0,1,2], | |
caption : "Handles", | |
captions: ['No Handles', 'Top Only', 'Top and Bottom'], | |
initial : 2 | |
} , | |
{ name: 'part', | |
caption: 'Display Part', | |
type : 'choice', | |
values : ['assembled', 'all', 'left', 'right', 'seat', 'back'], | |
captions: ['Assembled', 'All', 'Left', 'Right', 'Seat', 'Back'], | |
initial : 'assembled' | |
} | |
]; | |
} | |
function main(params) { | |
parts = makeStaticSeatParts(params); | |
switch(params.part) { | |
case 'assembled': | |
return parts.parts; | |
break; | |
case 'all': | |
var pieces = [ | |
{name:'seat'}, | |
{name:'back'}, | |
{name:'left',flip:true}, | |
{name:'right'} | |
] | |
t = 0; | |
retval = []; | |
pieces.forEach(function(piece) { | |
// Flatten Part | |
var movedPart = | |
parts[piece.name]. | |
lieFlat().center(true); | |
// Flip it over if desired | |
if(piece.flip) { | |
movedPart = movedPart | |
.rotateY(180) | |
.lieFlat().center(true); | |
} | |
movedPart = movedPart.translate([t,0]) | |
retval.push(movedPart); | |
t = t + 14; | |
}); | |
return retval; | |
break; | |
default: | |
return parts[params.part].lieFlat(); | |
break; | |
} | |
return parts.parts; | |
} | |
function makeStaticSeatParts(params) { | |
var seatWidth = params.seatWidth; | |
var seatDepth = params.seatDepth; | |
var chairHeight = params.chairHeight; | |
var seatHeight = params.seatHeight; | |
var seatOffset = -chairHeight/2.0+seatHeight; | |
var thickness = params.materialThickness; | |
var sideRadius = params.sideRadius; | |
var handles = parseInt(params.handles); | |
console.log(handles) | |
// Create the seat | |
var seat = makeSeat({ | |
width : seatWidth, | |
depth : seatDepth, | |
thickness : thickness, | |
fitAllowance : params.slotFitAllowance, | |
tabDepth : thickness*0.333, | |
boltCount : params.boltCount | |
}); | |
// Move the seat (and joints) to the appropriate height | |
seat = seat.translate([0,0,seatOffset]); | |
// Make side panel | |
var left = makeSide({ | |
depth : seatDepth, | |
height : chairHeight, | |
thickness : thickness, | |
radius : sideRadius, | |
handles : handles, | |
fitAllowance : params.slotFitAllowance, | |
tabSize : params.sideTabSize | |
}).rotateX(90) | |
.rotateZ(90) | |
.translate([-seatWidth/2.0-thickness/2.0,0,0]); | |
// Make second side panel from the first one | |
var right = left.translate([seatWidth+thickness, 0,0]); | |
// Cut tab slots in side panel | |
left = difference(left, seat.properties.slot, seat.properties.bolts) | |
right = difference(right, seat.properties.slot, seat.properties.bolts) | |
// Make back panel | |
var back = makeBack({ | |
width : seatWidth, | |
height : chairHeight, | |
thickness : thickness, | |
handles : handles | |
}); | |
// Move back panel into position | |
back = back.rotateX(90).translate( | |
[0,seatDepth/2.0 + thickness/2.0,0] | |
); | |
// Cut slots in back panel to mate with side panels and seat | |
back = difference( | |
back, | |
seat.properties.slot, | |
left.properties.slots, | |
left.properties.bolts, | |
right.properties.slots, | |
right.properties.bolts, | |
seat.properties.bolts | |
); | |
// Build final list of parts | |
parts = []; | |
// Panels | |
var panels = [ color(darken(woodColor, 0.7), seat), | |
left, | |
right, | |
color(darken(woodColor, 0.85), back) | |
]; | |
panels.forEach(function(panel) {parts.push(panel)}); | |
return { | |
left : left, | |
right : right, | |
back : back, | |
seat : seat, | |
panels : panels, | |
parts : parts | |
} | |
} | |
function darken(c, factor) { | |
factor = factor || 0.8; | |
return [c[0]*factor, c[1]*factor, c[2]*factor]; | |
} | |
function makeRoundedCube(options) { | |
options = options || {}; | |
var size = options.size || [1,1,1]; | |
var radius = options.radius || 0.25; | |
var thickness = size[2] || 0.5; | |
var width = size[0]; | |
var height = size[1]; | |
return difference( | |
cube({ | |
size: [width, height, thickness], | |
center: true | |
}), | |
makeFillet(radius, thickness).rotateZ(0).translate([width/2,height/2]), | |
makeFillet(radius, thickness).rotateZ(90).translate([-width/2,height/2]), | |
makeFillet(radius, thickness).rotateZ(-90).translate([width/2,-height/2]), | |
makeFillet(radius, thickness).rotateZ(180).translate([-width/2,-height/2]) | |
); | |
} | |
function makeHandle(options) { | |
options = options || {}; | |
var majorDiameter = options.majorDiameter || 1.0; | |
var minorDiameter = options.minorDiameter || majorDiameter/2.0; | |
var majorRadius = majorDiameter/2.0; | |
var minorRadius = minorDiameter/2.0; | |
var thickness = options.thickness || 0.5; | |
var count = options.count || 4; | |
// "Majors" (places where fingers go) | |
majors = []; | |
major = cylinder({ | |
d : majorDiameter, | |
h : thickness, | |
center : true | |
}); | |
for(var i=0; i<count; i++) { | |
majors.push(major.translate([(-(count-1)*majorDiameter/2)+(i*majorDiameter),0])); | |
} | |
var b = sqrt(pow(majorRadius+minorRadius,2)-pow(majorRadius,2)); | |
var theta = atan(b/majorRadius); | |
// "Minors" (arcs between places where fingers go) | |
minors = [] | |
var minor = cylinder({ | |
d : minorDiameter, | |
h : 0.5, | |
center : true | |
}).translate([0,b]); | |
for(var i=0; i<count-1; i++) { | |
minors.push(minor.translate([(-(count-2)*majorDiameter/2)+(i*majorDiameter),0])); | |
} | |
// The hole is the bulk of the handle | |
hole = union( | |
cube({ | |
size:[(count-1)*majorDiameter, 2*majorRadius*sin(theta), thickness], | |
center: true | |
}), | |
cube({ | |
size:[count*majorDiameter, majorRadius*2, thickness], | |
center : true | |
}).translate([0,-majorRadius*2/2]) | |
); | |
fillet = makeFillet(minorDiameter, thickness); | |
hole = difference( | |
hole, | |
fillet.rotateZ(180).translate([-count*majorDiameter/2,-majorRadius*2]), | |
fillet.rotateZ(-90).translate([count*majorDiameter/2,-majorRadius*2]), | |
union(minors) | |
) | |
return union(union(majors), hole); | |
} | |
function makeFillet(radius, thickness) { | |
var round = cylinder({ | |
r : radius, | |
h : 2*thickness, | |
center : true | |
}); | |
var corner = cube({ | |
size : [radius, radius, thickness] | |
}).translate([0,0,-thickness/2]); | |
return difference(corner, round).translate([-radius,-radius]) | |
} | |
function makeSeat(options) { | |
var width = options.width; | |
var depth = options.depth; | |
var thickness = options.thickness; | |
var fitAllowance = options.fitAllowance || 0.010; | |
var setBack = options.setBack || 0.5; | |
var tabDepth = options.tabDepth || thickness*0.3333333; | |
var boltCount = options.boltCount || 3; | |
var panel = cube({ | |
size: [width+tabDepth*2,depth+tabDepth-setBack,thickness], | |
center: true | |
}); | |
panel = panel.translate([0,tabDepth/2 + setBack/2]) | |
var slot = cube({ | |
size: [width+tabDepth*2+fitAllowance,depth-setBack+tabDepth+fitAllowance,thickness+fitAllowance], | |
center: true | |
}); | |
slot = slot.translate([0,tabDepth/2 + setBack/2]) | |
bounds = panel.getBounds(); | |
var inset = 1.5 | |
depth = (bounds[1].y - bounds[0].y)-2*inset; | |
var bolt = cylinder({ | |
d : 0.125, | |
h : 2*thickness | |
}).rotateY(90).translate([0,bounds[1].y-inset]); | |
var bolts = bolt; | |
var spacing = -depth/(boltCount-1); | |
for(var i=0; i<boltCount; i++) { | |
bolts = union(bolts, bolt); | |
panel = union(panel, bolt) | |
bolt = bolt.translate([0,spacing]); | |
} | |
bolts = union( | |
bolts.translate([bounds[1].x,0]), | |
bolts.rotateZ(90).translate([0,bounds[1].y]), | |
bolts.rotateZ(180).translate([bounds[0].x,0]) | |
); | |
panel.properties.slot = slot; | |
panel.properties.bolts = bolts; | |
return color(woodColor, panel); | |
} | |
function makeSide(options) { | |
options = options || {} | |
var depth = options.depth; | |
var height = options.height; | |
var thickness = options.thickness; | |
var filletRadius = options.radius || height/3; | |
var handles = options.handles || 0; | |
var fitAllowance = options.fitAllowance || 0.010; | |
var tabSize = options.tabSize || 2.0; | |
var panel = union( | |
cube({ | |
size: [depth, height, thickness], | |
center: true | |
}), | |
cube({ | |
size: [depth + thickness, tabSize, thickness], | |
center: true | |
}).translate([thickness/2, height/4.0, 0]), | |
cube({ | |
size: [depth + thickness, tabSize, thickness], | |
center: true | |
}).translate([thickness/2, -height/4.0, 0]) | |
); | |
panel.properties.slots = union( | |
cube({ | |
size: [depth + thickness, tabSize+fitAllowance, thickness], | |
center: true | |
}).translate([thickness/2, height/4.0, 0]), | |
cube({ | |
size: [depth + thickness, tabSize+fitAllowance, thickness], | |
center: true | |
}).translate([thickness/2, -height/4.0, 0]) | |
) | |
var inset = 1.0 | |
var bolt = cylinder({ | |
d : 0.125, | |
h : thickness*2 | |
}).rotateY(90).translate([depth/2,0]); | |
bolts = union( | |
bolt.translate([0,height/2 - inset]), | |
bolt.translate([0,-height/2 + inset]) | |
); | |
panel.properties.bolts = bolts; | |
var handleLength = min(depth/3.2, 3.75); | |
var handleDepth = handleLength/2.5; | |
var handle1 = makeRoundedCube({ | |
size:[handleLength,handleDepth,handleLength/1.875], | |
radius: handleDepth/2.5 | |
}).translate([0,height/2.0-handleDepth*1.1]); | |
var handle2 = handle1.rotateZ(180); | |
switch(handles) { | |
case 0: | |
handles = []; | |
break; | |
case 1: | |
handles = [handle1]; | |
break; | |
case 2: | |
handles = [handle1, handle2]; | |
break; | |
default: | |
handles = []; | |
break; | |
} | |
largeFillet = makeFillet(filletRadius, thickness) | |
.rotateZ(90) | |
.translate([-depth/2, height/2]); | |
smallFillet = makeFillet(filletRadius, thickness) | |
.translate([depth/2, height/2]) | |
.rotateZ(180); | |
handles.forEach(function(handle) { | |
panel = difference(panel, handle); | |
}); | |
panel = difference(panel, largeFillet, smallFillet); | |
return color(woodColor, panel); | |
} | |
function makeBack(options) { | |
options = options || {}; | |
var width = options.width; | |
var height = options.height; | |
var thickness = options.thickness; | |
var handles = options.handles || 0; | |
var panel = cube({ | |
size : [width + 2*thickness, height, thickness], | |
center : true | |
}); | |
handleDiameter = min(width/12.0,1.0); | |
var handle1 = makeHandle({ | |
majorDiameter : handleDiameter | |
}).translate([0,height/2.0-(handleDiameter*1.25)]); | |
var handle2 = handle1.rotateZ(180); | |
var handleList = []; | |
switch(handles) { | |
case 1: | |
handleList = [handle1]; | |
break; | |
case 2: | |
handleList = [handle1,handle2]; | |
break; | |
default: | |
break | |
} | |
handleList.forEach(function(handle) { | |
panel = difference(panel, handle); | |
}); | |
return color(woodColor,panel); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment