OpenJsCad demo: Parametric Grille
Source code
Below is the OpenJsCad script for this demo. To build your own models, create a .jscad script and use the
OpenJsCad parser
. For more information see the
OpenJsCad documentation
.
// Here we define the user editable parameters: function getParameterDefinitions() { return [ { name: 'outerwidth', caption: 'Outer width of grille:', type: 'float', default: 190 }, { name: 'outerheight', caption: 'Outer height of grille:', type: 'float', default: 120 }, { name: 'outerdepth', caption: 'Outer depth of grille:', type: 'float', default: 12 }, { name: 'thickness', caption: 'Wall thickness:', type: 'float', default: 2.5 }, { name: 'innerdistance', caption: 'Inner standoff distance:', type: 'float', default: 2 }, { name: 'bladescale', caption: 'Relative size of blades (1.0 is default):', type: 'float', default: 1 }, { name: 'numdividers', caption: 'Number of vertical dividers:', type: 'int', default: 2 }, { name: 'addlooseners', type: 'choice', caption: 'Add loops (for easy removal):', values: [0, 1], captions: ["No", "Yes"], default: 1, }, { name: 'show', type: 'choice', caption: 'Show:', values: ["all", "grille", "holders"], captions: ["All", "Grille (for printing)", "Holders (for printing)"], default: "all", }, { name: 'mouseears', type: 'choice', caption: 'Add mouse ears:', values: [0, 1], captions: ["No", "Yes"], default: 1, }, { name: 'quality', type: 'choice', caption: 'Quality:', values: [0, 1], captions: ["Draft", "Final"], default: 0, }, ]; } function main(params) { var outerwidth = params.outerwidth; var outerheight = params.outerheight; var thickness = params.thickness; var outerdepth = params.outerdepth; var innerdistance = params.innerdistance; var bladescale = params.bladescale; var draft = params.quality == 0; var marginleftright = 21; var margintopbottom = 15; var bladedistance = 12 * bladescale; var frontroundradius = 5; frontroundradius = Math.max(frontroundradius, thickness+0.2); var outerroundradius = 3; outerroundradius = Math.max(outerroundradius, thickness+0.2); outerdepth = Math.max(outerdepth, outerroundradius); outerdepth = Math.max(outerdepth, innerdistance+thickness); var frontextend = innerdistance + bladescale*12 + thickness - outerdepth; frontextend = Math.max(frontextend, 1.5); var bladewidth = outerwidth - 2*marginleftright; var bladesheight = outerheight - 2*margintopbottom; var numblades = Math.ceil(bladesheight / bladedistance) + 1; var topnotchsize = new CSG.Vector3D([20, 8, 3]); var topnotchpos = new CSG.Vector3D([outerwidth/2-thickness - topnotchsize.x/2, outerheight/2-thickness - topnotchsize.y/2, topnotchsize.z/2]); var bottomnotchsize = new CSG.Vector3D([12, 4, topnotchsize.z]); var bottomnotchpos = new CSG.Vector3D([outerwidth/2-thickness - 4 - bottomnotchsize.x/2, -outerheight/2+thickness + bottomnotchsize.y/2, bottomnotchsize.z/2]); var roundresolution = draft? 4 : 16; var result = new CSG(); if(params.show != "holders") { // build the shell: var z0plane = CSG.Plane.fromNormalAndPoint([0, 0, -1], [0, 0, 0]); var outershell = CSG.roundedCube({center: [0,0,0], radius: [outerwidth/2, outerheight/2, outerdepth], roundradius: outerroundradius, resolution: roundresolution}); outershell = outershell.cutByPlane(z0plane); var innershell = CSG.roundedCube({center: [0,0,0], radius: [outerwidth/2-thickness, outerheight/2-thickness, outerdepth-thickness], roundradius: outerroundradius-thickness, resolution: roundresolution}); innershell = innershell.cutByPlane(z0plane); var shell = outershell.subtract(innershell); var frontextendoutershell = CSG.roundedCube({center: [0,0,0], radius: [bladewidth/2+thickness, bladesheight/2+thickness, outerdepth+frontextend+frontroundradius+thickness], roundradius: frontroundradius, resolution: roundresolution}); var frontextendinnershell = CSG.roundedCube({center: [0,0,0], radius: [bladewidth/2, bladesheight/2, outerdepth+frontextend+frontroundradius+thickness+100], roundradius: frontroundradius-thickness, resolution: roundresolution}); frontextendinnershell = frontextendinnershell.cutByPlane(z0plane); var plane3 = CSG.Plane.fromNormalAndPoint([0, 0, 1], [0, 0, outerdepth+frontextend+100]); frontextendinnershell = frontextendinnershell.cutByPlane(plane3); var plane1 = CSG.Plane.fromNormalAndPoint([0, 0, 1], [0, 0, outerdepth+frontextend]); frontextendoutershell = frontextendoutershell.cutByPlane(plane1); var plane2 = CSG.Plane.fromNormalAndPoint([0, 0, -1], [0, 0, innerdistance]); frontextendoutershell = frontextendoutershell.cutByPlane(plane2); shell = shell.subtract(frontextendinnershell); var frontextendshell = frontextendoutershell.subtract(frontextendinnershell); shell = shell.union(frontextendshell); // build a blade: var curvedpath = CSG.Path2D.arc({ center: [0,0,0], radius: 15 * bladescale, startangle: 20, endangle: 80, resolution: draft? 8:32, }); var blade = curvedpath.rectangularExtrude(thickness, bladewidth, draft? 4:16, true); var bladecenter = blade.getBounds()[0].plus(blade.getBounds()[1]).times(0.5); blade = blade.translate(bladecenter.negated()); blade = blade.rotateY(-90); blade = blade.translate([0, 0, -blade.getBounds()[0].z+innerdistance]); var bladesize = blade.getBounds()[1].minus(blade.getBounds()[0]); // add the blades to the shell: var blades = new CSG(); for(var i = 0; i < numblades; i++) { var topy = bladesheight/2 + thickness - i*bladedistance; var translatedblade = blade.translate([0, topy-bladesize.y/2, 0]); blades = blades.union(translatedblade); } blades = blades.intersect(frontextendinnershell); var grille = shell; grille = shell.union(blades); // add the dividers: var dividers = new CSG(); if(params.numdividers > 0) { var w1 = (bladewidth - params.numdividers * thickness)/(params.numdividers+1); for(var i = 0; i < params.numdividers; i++) { var x = -(params.numdividers-1)*(w1+thickness)/2 + i*(w1+thickness); var z1 = outerdepth+frontextend; var divider = CSG.cube({center: [x, 0, (z1+innerdistance)/2], radius: [thickness/2, bladesheight/2, (z1-innerdistance)/2]}); dividers = dividers.union(divider); } } grille = grille.union(dividers); // create the notches: var notches = new CSG(); var topnotch1 = CSG.cube({center: topnotchpos, radius: topnotchsize.times(0.5) }); notches = notches.union(topnotch1); var topnotch2 = CSG.cube({center: [-topnotchpos.x, topnotchpos.y, topnotchpos.z], radius: topnotchsize.times(0.5) }); notches = notches.union(topnotch2); var bottomnotch1 = CSG.cube({center: bottomnotchpos, radius: bottomnotchsize.times(0.5) }); notches = notches.union(bottomnotch1); var bottomnotch2 = CSG.cube({center: [-bottomnotchpos.x, bottomnotchpos.y, bottomnotchpos.z], radius: bottomnotchsize.times(0.5) }); notches = notches.union(bottomnotch2); notches = notches.intersect(outershell); grille = grille.union(notches); result = result.union(grille); // create the looseners: if(params.addlooseners != 0) { var loosenerinnerwidth = 5; var loosenerinnerheight = 2; var loosenerdepth = 4; var loosener = CSG.cube({center: [0, -outerheight/2 - loosenerinnerheight/2, loosenerdepth/2], radius: [loosenerinnerwidth/2+thickness, loosenerinnerheight/2+thickness, loosenerdepth/2]}); loosener = loosener.subtract(CSG.cube({center: [0, -outerheight/2 - loosenerinnerheight/2, loosenerdepth/2], radius: [loosenerinnerwidth/2, loosenerinnerheight/2, loosenerdepth/2]})); var loosenerx = -outerwidth/2 + loosenerinnerwidth/2 + 5 + thickness; var looseners = loosener.translate([loosenerx, 0, 0]); looseners = looseners.union(loosener.translate([-loosenerx, 0, 0])); result = result.union(looseners); } if(params.mouseears != 0) { for(var i = 0; i < 4; i++) { var xpos=outerwidth/2-10; var ypos=outerheight/2; if(i&1) xpos = -xpos; if(i&2) ypos = -ypos; var cylinder = CSG.cylinder({start: [xpos, ypos, 0], end: [xpos, ypos, 0.5], radius: 15}); result = result.union(cylinder); } for(var i = 0; i < 4; i++) { var xpos=bladewidth/2 + thickness/2; var ypos=bladesheight/2 + thickness/2; if(i&1) xpos = -xpos; if(i&2) ypos = -ypos; var cyl1 = CSG.cylinder({start: [xpos, ypos, 0], end: [xpos, ypos, 0.5], radius: 15}); var cyl2 = CSG.cylinder({start: [xpos, ypos, 0], end: [xpos, ypos, innerdistance], radius: 5}); result = result.union(cyl1.union(cyl2)); } } } if(params.show != "grille") { // create the holders: var holderyoffset = 0.5; var holderzoffset = 1; var holderwidth = 10; var holderthickness = 3; var holdertopclipheight = 4; var holderbottomclipheight = 2; var holderscrewholeradius = 2; var holderscrewholedistance = 6; var holderx; if(params.show == "holders") { // just the holders: holderx = holderwidth/2+2; } else { holderx = bottomnotchpos.x + bottomnotchsize.x/2 - holderwidth/2; } var holdery1 = topnotchpos.y - topnotchsize.y/2 - holderyoffset; var holdery2 = bottomnotchpos.y + bottomnotchsize.y/2 + holderyoffset; var holdery0 = holdery1+holdertopclipheight; var holdery3 = holdery2-holderbottomclipheight; var holder = CSG.cube({center: [0, (holdery1 + holdery2)/2 , holderthickness/2], radius: [holderwidth/2, (holdery1-holdery2)/2, holderthickness/2]}); var d1 = 7; var holdery1a = holdery1 - d1; var holdery2a = holdery2 + d1; var holderz1 = topnotchsize.z + holderzoffset; var holderz2 = holderz1 + holderthickness; holder = holder.union(CSG.cube({ center: [0, (holdery1a+holdery1)/2, holderz2/2], radius: [holderwidth/2, (holdery1-holdery1a)/2, holderz2/2] })); holder = holder.union(CSG.cube({ center: [0, (holdery2a+holdery2)/2, holderz2/2], radius: [holderwidth/2, (holdery2a-holdery2)/2, holderz2/2] })); holder = holder.union(CSG.cube({ center: [0, (holdery0+holdery1a)/2, (holderz2+holderz1)/2], radius: [holderwidth/2, (holdery0-holdery1a)/2, (holderz2-holderz1)/2] })); holder = holder.union(CSG.cube({ center: [0, (holdery2a+holdery3)/2, (holderz2+holderz1)/2], radius: [holderwidth/2, (holdery2a-holdery3)/2, (holderz2-holderz1)/2] })); var screwhole = CSG.cylinder({start: [0,0,0], end: [0, 0, holderthickness], radius: holderscrewholeradius, resolution: 16}); holder = holder.subtract(screwhole.translate([0, holdery1a-holderscrewholedistance, 0])); holder = holder.subtract(screwhole.translate([0, holdery2a+holderscrewholedistance, 0])); holder = holder.setColor(0, 1, 0); var holders = holder.translate([holderx,0,0]); holders = holders.union(holder.translate([-holderx,0,0])); if(params.show == "holders") { holders = holders.rotateZ(90); } result = result.union(holders); } result = result.rotateZ(90); return result; }