include ; // https://github.com/revarbat/BOSL2 include ; include ; include ; $fn=20; part="both"; // [bottom::Bottom Only,side:Side Only,female_tabs:Holes only from side,male_tabs:Tabs only from bottom,view_fit:Debugging view to confirm tab fit,both:Bottom + Side] /* [Cubby dimensions] */ cubby_width=40; cubby_depth=120; cubby_height=90; // thickness of bottom and side thickness=5; // thickness of struts (used in both bottom and side) strut_thickness=5; // If set (to non zero), this is used in the max_bridge paramater for the // sparse_struct BOSL 2 function (which generates the wireframe rectangles). By // default, it is calculated as max_bridge=(cubby_height + cubby_width)/5. max_bridge=0; /* [Tab Dimensions] */ // width of each tab tab_width=10; // material between tabs tab_padding=5; // this value will be added to the mortises for additional tolerance tab_tolerance=0.5; /* [Screw Tabs] */ screw_tabs=true; /* [Hidden] */ tab_depth=thickness; tab_height=thickness; default_max_bridge=(cubby_height + cubby_width) / 5; if ( part == "both" ) { both(); } else if ( part =="bottom") { cubby_bottom(cubby_width, cubby_depth); } else if ( part == "side" ) { cubby_side(cubby_depth, cubby_height, screw_tabs); } else if ( part == "view_fit") { view_tab_fit(cubby_width, cubby_depth, cubby_height); } else if ( part == "male_tabs" ) { male_tabs(cubby_width, cubby_depth); } else if ( part == "female_tabs" ) { female_tabs(cubby_depth, cubby_height); } function assert_cubby_width() = let (min_width = strut_thickness + 1) // I'm not sure why I need the +1 assert(cubby_width >= min_width, str("Cubby width must be atleast ", min_width, "mm")); function assert_cubby_depth() = let (min_depth = tab_width * 2) assert(cubby_depth >= min_depth, str("Cubby width must be atleast ", min_depth, "mm")); function assert_cubby_height() = let (min_height = cubby_side_bracket_height()) assert(cubby_height >= min_height, str("Cubby width must be atleast ", min_height, "mm")); function double (x) = x * 2; function half (x) = x / 2; module both () { gap=5; cubby_bottom(cubby_width, cubby_depth); left(((cubby_width + cubby_height) / 2) + (tab_height * 2) + gap) cubby_side(cubby_depth, cubby_height, screw_tabs); } module cubby_bottom (width, depth) { let (_=assert_cubby_width()); let (_=assert_cubby_depth()); rot(90) yrot(90) color("blue") bottom_wall() yrot(90) down(tab_height / 2) { move_dist=(width / 2) + (tab_depth / 2); fwd(move_dist) alternating_tabs(false, "male", depth, tab_width, tab_depth, tab_height, tab_padding, tab_tolerance); back(move_dist) alternating_tabs(true, "male", depth, tab_width, tab_depth, tab_height, tab_padding, tab_tolerance); } children(); module bottom_wall () { strut_wall(width, depth) children(); } } // this calculation needs to be referenced by the view_tab_fit function function cubby_side_bracket_height() = double(tab_height) + tab_tolerance; module cubby_side (depth, height, screw_tabs=true) { let (_=assert_cubby_depth()); let (_=assert_cubby_height()); screw_tab_width=15; screw_tab_height=20; screw_tab_thickness=thickness; yrot(-90) color("red") side_wall() { if ( screw_tabs ) { position(TOP+FRONT+LEFT) screw_tab(); up(screw_tab_height) position(BOTTOM+FRONT+LEFT) screw_tab(); } attach(BOTTOM, TOP) zrot(90) tab_bracket(); attach(TOP, BOTTOM) zrot(90) tab_bracket(); } children(); module side_wall () { strut_wall(depth, height - cubby_side_bracket_height() * 2) children(); } module tab_bracket () { difference() { cuboid([depth,thickness,cubby_side_bracket_height()]); alternating_tabs(true, "female", depth, tab_width, tab_depth, tab_height, tab_padding, tab_tolerance); alternating_tabs(false, "female", depth, tab_width, tab_depth, tab_height, tab_padding, tab_tolerance); } children(); } module screw_tab () { difference() { cuboid([screw_tab_width, screw_tab_thickness,screw_tab_height], anchor=LEFT+TOP); right(screw_tab_width * (2 / 3)) zrot(90) yrot(90) { right(screw_tab_height * (1 / 3)) cyl(l=15, r=2, anchor=RIGHT); right(screw_tab_height * (2 / 3)) cyl(l=15, r=2, anchor=LEFT); } } children(); } } module strut_wall(w, h) { bridge=max_bridge ? max_bridge : default_max_bridge; sparse_strut( l=w, h=h, thick=thickness, maxang=45, strut=5, max_bridge=bridge ) children(); } module male_tabs (w, d) { keep=(w / 2) - thickness; left_half(x=-keep) cubby_bottom(cubby_width, cubby_depth); } module female_tabs (d, h) { // text inscribed (when printing tolerances) text_inscription_depth=0.6; text_size=thickness * 0.9; text=str(tab_tolerance); text_pos=[-h/2 + (thickness * 0.95), -d / 2 + (thickness / 2), (thickness / 2)]; xrot(180) left_half(x=(h / -2 + (thickness * 3))) cubby_side(d, h, screw_tabs=false); translate(text_pos) zrot(90) linear_extrude(text_inscription_depth) color("green") text(size=text_size, text, font="Bitstream Vera Sans:style=Bold"); } module view_tab_fit (w, d, h) { // position for testing tab fit module position_fit_test (left=true) { cubby_bottom(w, d); dist=(w / 2) + (thickness / -2) + tab_depth - 2; down=half(h) - half(thickness) - tab_height - half(tab_tolerance); yflip() yrot(90) down(dist * (left ? 1 : -1)) left(down) cubby_side(d, h, screw_tabs); } position_fit_test(left=true); yrot(180) up(h) position_fit_test(left=false); } module alternating_tabs (first, gender, length, tab_width, tab_depth, tab_height, tab_padding, tolerance) { dist=length - (tab_padding * 2); total_tabs=dist / (tab_width + tab_padding + tolerance); half_tabs=total_tabs / 2; tab_qty = total_tabs % 2 == 0 ? half_tabs : first ? ceil(half_tabs) : floor(half_tabs); start = first ? tab_padding : (tab_padding * 2) + tab_width; spacing = (tab_width + tab_padding) * 2; xcopies(spacing, tab_qty, dist, sp=((dist / -2) + start)) dovetail(gender, angle=0, slide=add_tol(tab_depth), width=add_tol(tab_width), height=add_tol(tab_height)); function add_tol (x) = gender == "male" ? x : x + tolerance; }