#+TITLE: Computer-controlled Machining #+AUTHOR: Dylan Holmes #+ATTR_HTML: :width 300 [[./img/progress-2.jpg]] * Design This week, I designed a shelving unit out of wood. I used OpenSCAD for the design process. Particular challenges / milestones I achieved were that I: - Designed the individual modular components, consisting of a shelf and a vertical support. The components are highly parametric, allowing you to change the number of shelves or the stock/mill thickness at will. - Wrote code that would either spread out the components in a compact 2D form for printing, or extrude and assemble them into the completed 3D shelf. - Carefully designed the shelves according to a common scale so that the parts would fit perfectly together on a 2D board. This was intended to save on the number of cuts, though because of the fixturing (we didn't have a vaccuum board, so cut out pieces would tend to move around), it turns out that the recommended plan is allowing 1" clearance between separate pieces. - Incorporated post-processing adjustments, including so-called "dog bone" joins. Dog bone joins accomodate the diameter of the mill: when following the perimeter of a rectangle, a thick round mill will actually end up cutting a rounded rectangle. Adding a circular extrusion ("dog bone") incorporates and compensates for the thickness of the end mill, resulting in a clean sharp corner and a better press fit. #+ATTR_HTML: :width 300 [[./img/progress.jpg]] * Post-processing The benefits of parametric design (changing the =num_shelves= parameter.) [[./img/parametric.jpg]] The following figure shows a standard rectangle, a correct dog-bone rectangle, and an incorrect "mickey mouse" rectangle. [[./img/dogbone.jpg]] To be correct, dog bones should be a circle with radius proportional to your end mill radius. The circle must be *inset*, as in the second figure---the corner of the circle must coincide with the relevant corner of the rectangle. In the /incorrect/ version, the circle is simply centered on the rectangle's corner rather than inset. The result is protruding circles reminiscent of "mickey mouse" ears. For dog bones, I wrote a "dog-boned rectangle" function: #+BEGIN_SRC module square_dog_bone(size, radius=mill_radius) { offset = radius * sin(45); // move inward at a 45 degree angle union() { square(size); translate([offset,offset]) circle(radius); translate([size[0]-offset,offset]) circle(radius); translate([offset,size[1]-offset]) circle(radius); translate([size[0]-offset,size[1]-offset]) circle(radius); } } #+END_SRC * Code #+BEGIN_SRC in_to_mm = 25.4; board_thickness = 0.5 * in_to_mm; board_width_in = 96 * in_to_mm; board_height_in = 48 * in_to_mm; unit = 4 * in_to_mm; //shelf_width = 31.5 * in_to_mm; //shelf_depth = 19 * in_to_mm; margin = 1 * in_to_mm; notch_offset_horizontal=3*in_to_mm; //notch_width=2*in_to_mm; notch_width=board_thickness; //notch_height=4*in_to_mm; //hole_offset=3*in_to_mm; hole_height=3*in_to_mm; //back_foot_height = 2 * in_to_mm; back_foot_height = unit; peg_height = unit; hole_offset = unit; notch_height = unit; notch_chamfer_width = board_thickness/4; notch_chamfer_height = board_thickness/2; shelf_width = (6.5-0.1) * unit; shelf_depth = 4.5 * unit; num_shelves=5; cutting_offset= 1 * in_to_mm; mill_radius = 0.75/4 * in_to_mm; // new parameters // http://archive.fabacademy.org/archives/2017/fablaberfindergarden/students/260/fabacademy/week-7/ module square_dog_bone(size, radius=mill_radius) { offset = radius * sin(45); union() { square(size); translate([offset,offset]) circle(radius); translate([size[0]-offset,offset]) circle(radius); translate([offset,size[1]-offset]) circle(radius); translate([size[0]-offset,size[1]-offset]) circle(radius); } } module shelf(shelf_width, shelf_depth, notch_offset_horizontal, notch_width, notch_height, hole_offset, hole_height) { difference() { square(size=[shelf_width, shelf_depth]); // NOTCHES * translate([notch_offset_horizontal, 0, 0]) square_dog_bone(size=[notch_width, notch_height]);//square(size=[notch_width,notch_height]); translate([notch_offset_horizontal, 0,0]) polygon(points=[ [-notch_chamfer_width,0], [0,notch_chamfer_height], [0,notch_height], [notch_width,notch_height], [notch_width,notch_chamfer_height], [notch_width+notch_chamfer_width,0]]); * polygon(points=[[-notch_chamfer_width,0],[notch_width,0],[notch_width,notch_height],[0,notch_height]]); translate([(shelf_width-notch_width-notch_offset_horizontal), 0, 0]) polygon(points=[ [-notch_chamfer_width,0], [0,notch_chamfer_height], [0,notch_height], [notch_width,notch_height], [notch_width,notch_chamfer_height], [notch_width+notch_chamfer_width,0]]); //square(size=[notch_width,notch_height]); // HOLES translate([notch_offset_horizontal, shelf_depth-hole_height-hole_offset,0]) square_dog_bone(size=[notch_width,hole_height]); translate([shelf_width-notch_width-notch_offset_horizontal, shelf_depth-hole_height-hole_offset,0]) square_dog_bone(size=[notch_width,hole_height]); } } module default_shelf() { shelf(shelf_width, shelf_depth, notch_offset_horizontal= notch_offset_horizontal, notch_width=notch_width, notch_height=notch_height, hole_offset=hole_offset, hole_height=hole_height); } module spine(shelf_width, shelf_depth, notch_offset_horizontal, notch_width, notch_height, hole_offset, hole_height, back_foot_height, num_shelves=num_shelves) { module vertebra(n=num_shelves) { if(n > 0) { union() { // back-to-front rectangle square(size=[shelf_depth-hole_offset, peg_height]); // back tooth if(n == 1) { union() { translate([0,peg_height]) square(size=[notch_height,peg_height]); translate([0,peg_height*2,0]) polygon(points=[[0,0],[peg_height,0],[0,peg_height]]); } } else { translate([0,peg_height,0]) square(size=[notch_height,peg_height*2]); } // front tooth translate([shelf_depth-hole_offset-hole_height,peg_height,0]) square(size=[hole_height,peg_height]); translate([0,peg_height*3]) { vertebra(n-1); } } } } // foot polygon(points=[[0,0], [shelf_depth,0], [shelf_depth-hole_offset,back_foot_height], [0,back_foot_height]]); // bottom_shelf vertebra(); } module default_spine() { spine(shelf_width, shelf_depth, notch_offset_horizontal= notch_offset_horizontal, notch_width=notch_width, notch_height=notch_height, hole_offset=hole_offset, hole_height=hole_height, back_foot_height=back_foot_height); } module assembled_unit(num_shelves=num_shelves) { // place two spines translate([notch_offset_horizontal,0,0]) rotate([90,0,90]) union() { linear_extrude(height=board_thickness){ default_spine(); } translate([0,0,shelf_width-2*notch_offset_horizontal-board_thickness]) linear_extrude(height=board_thickness){ default_spine(); } } module shelves_iter(n=num_shelves) { if(n > 0) { linear_extrude(height=board_thickness){ default_shelf(); } translate([0,0,back_foot_height*3]) shelves_iter(n-1); } } // place shelves translate([0,0,back_foot_height]) shelves_iter(); } module 2D_assembled_unit() { module make_shelves_iter(n=num_shelves,cutting_offset=0) { if(n>0) { if(n%2==1) { default_shelf(); } else { # translate([0,shelf_depth]) scale([1,-1,1]) default_shelf(); } translate([0,shelf_depth]) make_shelves_iter(n-1); } } translate([0,0]) make_shelves_iter(cutting_offset=cutting_offset); //rotate([0,0,-90]) translate([-shelf_depth,0,0]) union() { translate([board_height_in-2*margin-shelf_depth,0]) union() { default_spine(); translate([0,0,0]) rotate([0,0,180]) translate([-shelf_depth,-(back_foot_height+num_shelves*peg_height*3)+peg_height]) % default_spine(); } } module 2D_assembled_unit_nooverlap() { module make_shelves_iter(n=num_shelves,cutting_offset=0) { if(n>0) { default_shelf(); translate([0,shelf_depth+margin]) make_shelves_iter(n-1); } } translate([0,0]) make_shelves_iter(cutting_offset=cutting_offset); //rotate([0,0,-90]) translate([-shelf_depth,0,0]) union() { translate([board_height_in-2*margin-shelf_depth,0]) union() { translate([shelf_depth+margin,0]) default_spine(); translate([0,0,0]) rotate([0,0,180]) translate([-shelf_depth,-(back_foot_height+num_shelves*peg_height*3)+peg_height]) % default_spine(); } } // THE UNDERLYING BOARD # translate([0,0,-10]) square(size=[board_height_in , board_width_in]); // THE ASSEMBLED SHELF assembled_unit(); // THE 2D VERSION translate([margin,margin]) 2D_assembled_unit_nooverlap(); #+END_SRC