Computer-controlled Machining

Table of Contents

progress-2.jpg

1 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.

progress.jpg

2 Post-processing

The benefits of parametric design (changing the num_shelves parameter.)

parametric.jpg

The following figure shows a standard rectangle, a correct dog-bone rectangle, and an incorrect "mickey mouse" rectangle.

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:

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);

    }
}

3 Code

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();



Author: Dylan Holmes

Created: 2018-10-17 Wed 11:37

Validate