$fn=256; mode = "view"; // [view, laser, clip, adapter] // mechanical tolerance between box and adapter in vertical direction thick_tol = 0.5; // mechanical tolerance between box and adapter horizontal direction tol = 0.5; // height of the tube, measured from the box bottom adapter_length = 20; // inner diameter of the bottom end of the tube adapter_bottom_diam_inner_ = 32; /* [Bajonet locking tabs] */ locking_tab_angle = 60; locking_tab_extra = 3; locking_tab_angle_tol = 5; /* [CNC mount geometry] */ // box dimensions xlen = 140; // box dimensions ylen = 73; // box dimensions zlen = 30; // cutout position measured from the outer corner cutout_x = 50; // cutout position measured from the outer corner cutout_y = 37; // cutout diameter cutout_diam = 45; // notch depth for the clip clip_notch = 2; // desired width for the clip clip_width = 20; // distance from the box's upper edge to where the holding notch begins notch_height = 18; /* [Brush] */ // center of brush cutout location measured from the outer corner brush_x = 10000; // always disable generation of a circular brush around the adapter inserts never_brush_adapter = false; // extra distance between outer tube and brush brush_extra_distance = 0; // height (zlen) of the brush mount brush_base_len = 7; // width of the brush mount (add some tolerance here!) brush_base_thick = 6.5 + 0.5; // minimum distance between "start of the actual brushs" and "end of the tube". if this length cannot be achieved, do not generate a brush at all. brush_min_length = 5; // desired distance between "start of the actual brushs" and "end of the tube". may be reduced to up to brush_min_length. brush_desired_length = 10; /* [Laser and material settings] */ // acryl thickness thick = 3; // beam width compensation. this shall be "laser beam width minus desired tolerance" beam = 0; /* [3D printing strength] */ clip_thick = 3; adapter_tab_thick = 3; adapter_tab_chamfer = 3; adapter_wall_thick = 2; ring_thick = 3; module fnord() {} adapter_bottom_diam_inner = min(adapter_bottom_diam_inner_, cutout_diam - 2*adapter_wall_thick); if (adapter_bottom_diam_inner_ > cutout_diam - 2*adapter_wall_thick) { echo("Warning: clamping bottom tube's inner diameter"); } eps= $preview ? 1e-2 : 1e-5; module tabs(num, width, height, positive) { m = 2*num+1; translate([-width/2, -height/2]) for (i = [0:1:m-1]) { if ((i%2 == 0) == positive) { translate([i/m*width - beam/2, -beam/2]) square([1/m*width+beam, height+beam]); } } } module sector(radius, angles) { fn = $fn; r = 2*radius; step = -360 / fn; points = concat([[0, 0]], [for(a = [angles[0] : step : angles[1] - 360]) [r * cos(a), r * sin(a)] ], [[r * cos(angles[1]), r * sin(angles[1])]] ); difference() { circle(radius, $fn = fn); polygon(points); } } module box_bottom() { linear_extrude(thick, center=false) { difference() { square([xlen, ylen], center=true); translate([0, ylen/2 - thick/2]) tabs(5, xlen, 3, false); translate([0, -ylen/2 + thick/2]) tabs(5, xlen, 3, false); translate([xlen/2 - thick/2,0]) rotate(90) tabs(3, ylen, 3, false); translate([-xlen/2 + thick/2,0]) rotate(90) tabs(3, ylen, 3, false); translate([brush_x-xlen/2, 0]) square([brush_base_thick, ylen], center=true); translate([cutout_x-xlen/2, cutout_y-ylen/2]) { circle(d = cutout_diam - beam); for (i=[1:1:3]) { rotate(120*i) { sector(cutout_diam/2+locking_tab_extra, [-locking_tab_angle/2, locking_tab_angle/2]); } } } } } } module box_frontback() { rotate([-90,0,0]) linear_extrude(thick, center=true) { difference() { translate([0, -zlen/2]) square([xlen, zlen], center=true); translate([0, -thick/2]) tabs(5, xlen, 3, true); translate([-xlen/2+thick/2, -zlen/2]) rotate(90) tabs(1, zlen, 3, true); translate([xlen/2-thick/2, -zlen/2]) rotate(90) tabs(1, zlen, 3, true); translate([brush_x-xlen/2, -brush_base_len/2]) square([brush_base_thick, brush_base_len], center=true); } } } module box_sides() { rotate([0,90,0]) linear_extrude(thick, center=true) difference() { translate([-zlen/2,0]) square([zlen, ylen], center=true); translate([-thick/2,0]) rotate(90) tabs(3, ylen, 3, true); translate([-zlen/2, -ylen/2+thick/2]) tabs(3, ylen, 3, false); translate([-zlen/2, ylen/2-thick/2]) tabs(3, ylen, 3, false); } } module clip() { clip_len = notch_height + zlen - brush_base_len; difference() { union() { translate([0,-clip_width/2,0]) cube([clip_thick, clip_width, clip_len+eps]); translate([-clip_notch, -clip_width/2, clip_len]) cube([clip_notch+clip_thick, clip_width, clip_thick]); } translate([clip_thick/4 - clip_notch,0,clip_len+clip_thick-clip_thick/4]) rotate([0,-45,0]) translate([-500,-clip_width,0]) cube([999,2*clip_width,999]); } } module adapter(height) { tube_diam = cutout_diam - 2*tol; solid_ring_y = thick+thick_tol; inward_height = adapter_tab_thick+thick+thick_tol; enable_brush = (height >= brush_base_len + brush_min_length) && !never_brush_adapter; brush_base_distance = max(0, height - (brush_base_len + brush_desired_length)); difference() { union() { translate([0,0,-ring_thick]) cylinder( ring_thick, d = cutout_diam+ 2*(locking_tab_extra+1) ); if (enable_brush) mirror([0,0,1]) { difference() { cylinder( brush_base_distance + brush_base_len, d = tube_diam + 2*brush_extra_distance + 2*brush_base_thick + 2 * adapter_wall_thick ); translate([0,0,brush_base_distance+eps]) difference() { cylinder( brush_base_len, d = tube_diam + 2*brush_extra_distance + 2*brush_base_thick ); translate([0,0,-eps]) cylinder( brush_base_len + 2*eps, d = tube_diam + 2*brush_extra_distance ); } } } // locking tabs translate([0,0,thick+thick_tol]) { linear_extrude(adapter_tab_thick) { circle(1); for (i=[1:1:3]) { rotate(120*i) { sector( tube_diam/2+locking_tab_extra, [ -(locking_tab_angle-locking_tab_angle_tol)/2, (locking_tab_angle-locking_tab_angle_tol)/2 ] ); } } } } // locking tab end stops translate([0,0,0]) { linear_extrude(adapter_tab_thick + thick+thick_tol+eps) { circle(1); for (i=[1:1:3]) { rotate(120*i) { sector( tube_diam/2+locking_tab_extra, [ -(locking_tab_angle-locking_tab_angle_tol)/2, -(locking_tab_angle-locking_tab_angle_tol)/2 + 5 ] ); } } } } translate([0,0,-height]) cylinder(height+eps, d1=adapter_bottom_diam_inner+2*adapter_wall_thick, d2=tube_diam); cylinder(inward_height-eps, d=tube_diam); } translate([0,0,-eps]) cylinder(d = tube_diam - 2*adapter_wall_thick, h=9999); mirror([0,0,1]) cylinder(adapter_length+eps, d1 = tube_diam-2*adapter_wall_thick, d2 = adapter_bottom_diam_inner); } } if (mode == "view") { box_bottom(); color("red") { translate([0, (ylen-thick)/2, 0]) box_frontback(); translate([0, -(ylen-thick)/2, 0]) box_frontback(); } color("blue") { translate([-(xlen-thick)/2, 0, 0]) box_sides(); translate([(xlen-thick)/2, 0, 0]) box_sides(); } color("pink") { translate([xlen/2, 0, brush_base_len]) clip(); mirror([1,0,0]) translate([xlen/2, 0, brush_base_len]) clip(); } color("green") translate([-xlen/2+cutout_x, 0, 0]) adapter(adapter_length); } else if (mode == "adapter") { rotate([0,180,0]) adapter(adapter_length); } else if (mode == "clip") { rotate([0,90,0]) clip(); } else if (mode == "laser") { projection() box_bottom(); translate([0, ylen/2+2]) projection() rotate([-90,0,0]) box_frontback(); translate([0, ylen/2+zlen+4]) projection() rotate([-90,0,0]) box_frontback(); translate([xlen/2+2,0]) projection() rotate([0,90,0]) box_sides(); translate([xlen/2+2,ylen+2]) projection() rotate([0,90,0]) box_sides(); }