// Create a tenon with chamfered edges on the front face. The tenon is facing // the positive Y direction. It is offset by -0.01 in Y to make it easy to // ensure that it intersects with the main body it is attached to. module tenon(dims) { translate([0, -0.01, 0]) difference() { cube([dims[0], dims[1] + 0.01, dims[2]]); // chamfers color("blue") union() { translate([-0.5, dims[1] + 0.1, dims[2] - 1.1]) rotate([45, 0, 0]) cube([dims[0] + 1, 2, 2]); translate([-0.5, dims[1] + 0.1, -1.7]) rotate([45, 0, 0]) cube([dims[0] + 1, 2, 2]); translate([-0.4, dims[1]-1, -0.5]) rotate([0, 0, 45]) cube([2, 2, dims[2] + 1]); translate([dims[0]+0.4, dims[1]-1, -0.5]) rotate([0, 0, 45]) cube([2, 2, dims[2] + 1]); } } } module morticeBlank(dims, clearance = 0) { translate([-clearance, -clearance-0.01, -clearance]) cube([dims[0] + 2 * clearance, dims[1] + 2 * clearance+0.01, dims[2] + 2 * clearance]); } module rectDowel(dim = [6, 16, 3]) { // rectangular dowel translate([-dim[0]/2, -dim[1]/2, -dim[2]/2]) cube(dim); } // Create a cube with a mortice cut out of the center for a snap fit tenon. // The mortice is aligned on the Y-axis: the dimension in X determines the // width of the piece and mortice, the dimension in Y determines the depth or // length of the piece, and the dimension in Z determines the height thickness. // // The morticeDim dimensions will be enlarged slightly to allow for adequate // fitment of the tenon based on the clearance (defaults to 0.2). module morticeWithSnap( dim = [10, 20, 5], morticeDim = [6, 8, 3], snapDepth = 2, snapHeight = 1, clearance = 0.3) { mDim = [morticeDim[0] + 2*clearance, morticeDim[1] + clearance, morticeDim[2] + clearance]; difference() { // block translate([-dim[0]/2, 0, -dim[2]/2]) cube(dim); // cutout translate([-mDim[0]/2, -clearance, -mDim[2]/2]) union() { cube(mDim); // mortice cutout // snap fit cutout translate([0, morticeDim[1] - snapDepth, morticeDim[2] + clearance - 0.01]) cube([morticeDim[0] + clearance, snapDepth + clearance, snapHeight + clearance + 0.01]); } } } // Create a cube with a tenon and cantilevered snap fit on one side. // The tenon is aligned on the Y-axis: the dimension in X determines the // width of the piece and tenon, the dimension in Y determines the depth or // length of the piece, and the dimension in Z determines the height thickness. module tenonWithSnap( dim = [10, 20, 5], tenonDim = [6, 8, 3], snapDepth = 2, snapHeight = 1) { // block translate([-dim[0]/2, 0, -dim[2]/2]) cube(dim); // tenon translate([-tenonDim[0]/2, dim[1] - 0.01, -tenonDim[2]/2]) union() { // main tenon body tenonFlexArmThickness = min(tenonDim[2] / 2, 1); color("blue") translate([0, 0, tenonDim[2] - tenonFlexArmThickness]) cube([tenonDim[0], tenonDim[1], tenonFlexArmThickness]); // fillet on front edge of tenon /* translate([0, tenonDim[1] / 4 - 0.05, tenonDim[2] / 4 + 0.03]) difference() { color("blue") cube([tenonDim[0], tenonDim[1] / 8, tenonDim[2] / 4]); color("green") translate([tenonDim[0] /2, tenonDim[1]/8 - tenonDim[2]/16, 0]) rotate([0, 90, 0]) cylinder(r = tenonDim[2] / 4, h = tenonDim[0] + 0.02, center=true); } */ // 45-degree chamfer on top edges of tenon base (alignment feature) difference() { cube([tenonDim[0], tenonDim[1]*0.4, tenonDim[2]]); translate([-0.01, tenonDim[1]*0.4, -tenonDim[2]/2]) rotate([45, 0, 0]) cube([tenonDim[0] + 0.02, tenonDim[1] / 4, tenonDim[2] / 2]); } // cantilevered snap fit translate([0, tenonDim[1] - snapDepth, tenonDim[2] - 0.01]) difference() { cube([tenonDim[0], snapDepth, snapHeight + 0.01]); color("red") union() { // front-edge chamfer translate([-0.01, -snapDepth, snapHeight*0.66]) rotate([-10, 0, 0]) cube([tenonDim[0] + 0.02, snapDepth, snapHeight + 0.01]); // back-edge chamfer translate([-0.01, snapDepth, snapHeight / 8]) rotate([15, 0, 0]) cube([tenonDim[0] + 0.02, snapDepth, snapHeight + 0.01]); // side chamfers translate([-tenonDim[0]/4, 0, snapHeight*0.66]) rotate([0, 10, 0]) cube([tenonDim[0] / 4, snapDepth + 0.02, snapHeight]); translate([tenonDim[0], 0, snapHeight*0.60]) rotate([0, -10, 0]) cube([tenonDim[0] / 4, snapDepth + 0.02, snapHeight]); } } } } // Creates the tail of a dovetail for a dovetail joint. The tail is oriented in // the positive Y direction. It is exactly (tailHeight + 0.01) tall (in the Y // direction) and positioned so that its base is at Y=-0.01 to make it easy to // overlap with the main body it is attached to. module dovetail(tailHeight, tailWidthMin, tailWidthMax, depth) { angle = atan((tailWidthMax - tailWidthMin) / (2 * tailHeight)); translate([0, tailHeight, 0]) mirror([0, 1, 0]) difference() { cube([tailWidthMax, tailHeight + 0.01, depth + 0.01]); color("blue") translate([tailWidthMax, 0, -0.1]) rotate([0, 0, angle]) cube([tailWidthMax, tailHeight*2, depth+0.2]); color("blue") rotate([0, 0, -angle]) translate([-tailWidthMax, 0, -0.1]) cube([tailWidthMax, tailHeight*2, depth+0.2]); } }