diff --git a/MicroscopeLenseHolder.FCStd b/MicroscopeLenseHolder.FCStd new file mode 100644 index 0000000..12ea5fc Binary files /dev/null and b/MicroscopeLenseHolder.FCStd differ diff --git a/Parametric_Gridfinity-Bin.fcstd b/Parametric_Gridfinity-Bin.fcstd new file mode 100644 index 0000000..a6df2ee Binary files /dev/null and b/Parametric_Gridfinity-Bin.fcstd differ diff --git a/gridfinity-constants.scad b/gridfinity-constants.scad new file mode 100644 index 0000000..90672dd --- /dev/null +++ b/gridfinity-constants.scad @@ -0,0 +1,85 @@ + +// height of the base +h_base = 5; +// outside rounded radius of bin +r_base = 4; +// lower base chamfer "radius" +r_c1 = 0.8; +// upper base chamfer "radius" +r_c2 = 2.4; +// bottom thiccness of bin +h_bot = 2.2; +// outside radii 1 +r_fo1 = 8.5; +// outside radii 2 +r_fo2 = 3.2; +// outside radii 3 +r_fo3 = 1.6; + +// screw hole radius +r_hole1 = 1.5; +// magnet hole radius +r_hole2 = 3.25; +// center-to-center distance between holes +d_hole = 26; +// magnet hole depth +h_hole = 2.4; +// slit depth (printer layer height) +h_slit = 0.2; + +// top edge fillet radius +r_f1 = 0.6; +// internal fillet radius +r_f2 = 2.8; + +// width of divider between compartments +d_div = 1.2; +// minimum wall thickness +d_wall = 0.95; +// tolerance fit factor +d_clear = 0.25; + +// height of tab (yaxis, measured from inner wall) +d_tabh = 15.85; +// maximum width of tab +d_tabw = 42; +// angle of tab +a_tab = 36; + +d_wall2 = r_base-r_c1-d_clear*sqrt(2); +d_magic = -2*d_clear-2*d_wall+d_div; + +// Baseplate constants + +// Baseplate bottom part height (part added with weigthed=true) +bp_h_bot = 6.4; + +// Baseplate bottom cutout rectangle size +bp_cut_size = 21.4; + +// Baseplate bottom cutout rectangle depth +bp_cut_depth = 4; + +// Baseplate bottom cutout rounded thingy width +bp_rcut_width = 8.5; + +// Baseplate bottom cutout rounded thingy left +bp_rcut_length = 4.25; + +// Baseplate bottom cutout rounded thingy depth +bp_rcut_depth = 2; + +// countersink diameter for baseplate +d_cs = 2.5; + +// radius of cutout for skeletonized baseplate +r_skel = 2; + +// baseplate counterbore radius +r_cb = 2.75; + +// baseplate counterbore depth +h_cb = 3; + +// minimum baseplate thickness (when skeletonized) +h_skel = 1; diff --git a/gridfinity-rebuilt-baseplate.scad b/gridfinity-rebuilt-baseplate.scad new file mode 100644 index 0000000..eb0b595 --- /dev/null +++ b/gridfinity-rebuilt-baseplate.scad @@ -0,0 +1,147 @@ +include + +// ===== INFORMATION ===== // +/* + IMPORTANT: rendering will be better for analyzing the model if fast-csg is enabled. As of writing, this feature is only available in the development builds and not the official release of OpenSCAD, but it makes rendering only take a couple seconds, even for comically large bins. Enable it in Edit > Preferences > Features > fast-csg + +https://github.com/kennetek/gridfinity-rebuilt-openscad + +*/ + +// ===== PARAMETERS ===== // + +/* [Setup Parameters] */ +$fa = 8; +$fs = 0.25; + +/* [General Settings] */ +// number of bases along x-axis +gridx = 4; +// number of bases along y-axis +gridy = 4; +// base unit +length = 42; + +/* [Fit to Drawer] */ +// minimum length of baseplate along x (leave zero to ignore, will automatically fill area if gridx is zero) +distancex = 0; +// minimum length of baseplate along y (leave zero to ignore, will automatically fill area if gridy is zero) +distancey = 0; + +/* [Styles] */ + +// baseplate styles +style_plate = 2; // [0: thin, 1:weighted, 2:skeletonized] + +// enable magnet hole +enable_magnet = true; + +// hole styles +style_hole = 2; // [0:none, 1:contersink, 2:counterbore] + + +// ===== IMPLEMENTATION ===== // + +color("tomato") +gridfinityBaseplate(gridx, gridy, length, distancex, distancey, style_plate, enable_magnet, style_hole); + + + +// ===== CONSTRUCTION ===== // + +module gridfinityBaseplate(gridx, gridy, length, dix, diy, sp, sm, sh) { + + assert(gridx > 0 || dx > 0, "Must have positive x grid amount!"); + assert(gridy > 0 || dy > 0, "Must have positive y grid amount!"); + gx = gridx == 0 ? floor(dix/length) : gridx; + gy = gridy == 0 ? floor(diy/length) : gridy; + dx = max(gx*length-0.5, dix); + dy = max(gy*length-0.5, diy); + off = (sp==0?0:sp==1?bp_h_bot:h_skel+(sm?h_hole:0)+(sh==0?0:sh==1?d_cs:h_cb)); + + difference() { + translate([0,0,h_base]) + mirror([0,0,1]) + rounded_rectangle(dx, dy, h_base+off, r_base); + + gridfinityBase(gx, gy, length, 1, 1, 0, 0.5, false); + + translate([0,0,h_base-0.6]) + rounded_rectangle(dx*2, dy*2, h_base*2, r_base); + + pattern_linear(gx, gy, length) { + if (sm) block_base_hole(1); + + if (sp == 1) + translate([0,0,-off]) + cutter_weight(); + else if (sp == 2) + linear_extrude(10*(h_base+off), center = true) + profile_skeleton(); + + translate([0,0,-off]) { + if (sh == 1) cutter_countersink(); + else if (sh == 2) cutter_counterbore(); + } + } + } +} + +module cutter_weight() { + union() { + linear_extrude(bp_cut_depth*2,center=true) + square(bp_cut_size, center=true); + pattern_circular(4) + translate([0,10,0]) + linear_extrude(bp_rcut_depth*2,center=true) + union() { + square([bp_rcut_width, bp_rcut_length], center=true); + translate([0,bp_rcut_length/2,0]) + circle(d=bp_rcut_width); + } + } +} + +module cutter_countersink() { + pattern_circular(4) + translate([d_hole/2, d_hole/2, 0]) { + cylinder(r = r_hole1+d_clear, h = 100*h_base, center = true); + + translate([0,0,d_cs]) + mirror([0,0,1]) + hull() { + cylinder(h = d_cs+10, r=r_hole1+d_clear); + translate([0,0,d_cs]) + cylinder(h=d_cs+10, r=r_hole1+d_clear+d_cs); + } + } +} + +module cutter_counterbore() { + pattern_circular(4) + translate([d_hole/2,d_hole/2,0]) { + cylinder(h=100*h_base, r=r_hole1+d_clear, center=true); + difference() { + cylinder(h = 2*(h_cb+0.2), r=r_cb, center=true); + copy_mirror([0,1,0]) + translate([-1.5*r_cb,r_hole1+d_clear+0.1,h_cb-h_slit]) + cube([r_cb*3,r_cb*3, 10]); + } + } +} + +module profile_skeleton() { + l = length-2*r_c2-2*r_c1; + minkowski() { + difference() { + square([l-2*r_skel+2*d_clear,l-2*r_skel+2*d_clear], center = true); + pattern_circular(4) + translate([d_hole/2,d_hole/2,0]) + minkowski() { + square([l,l]); + circle(r_hole2+r_skel+2); + } + } + circle(r_skel); + } +} \ No newline at end of file diff --git a/gridfinity-rebuilt-bins.scad b/gridfinity-rebuilt-bins.scad new file mode 100644 index 0000000..640b707 --- /dev/null +++ b/gridfinity-rebuilt-bins.scad @@ -0,0 +1,158 @@ +include + +// ===== INFORMATION ===== // +/* + IMPORTANT: rendering will be better for analyzing the model if fast-csg is enabled. As of writing, this feature is only available in the development builds and not the official release of OpenSCAD, but it makes rendering only take a couple seconds, even for comically large bins. Enable it in Edit > Preferences > Features > fast-csg + the magnet holes can have an extra cut in them to make it easier to print without supports + tabs will automatically be disabled when gridz is less than 3, as the tabs take up too much space + base functions can be found in "gridfinity-rebuilt-utility.scad" + examples at end of file + + BIN HEIGHT + the original gridfinity bins had the overall height defined by 7mm increments + a bin would be 7*u millimeters tall + the lip at the top of the bin (3.8mm) added onto this height + The stock bins have unit heights of 2, 3, and 6: + Z unit 2 -> 7*2 + 3.8 -> 17.8mm + Z unit 3 -> 7*3 + 3.8 -> 24.8mm + Z unit 6 -> 7*6 + 3.8 -> 45.8mm + +https://github.com/kennetek/gridfinity-rebuilt-openscad + +*/ + +// ===== PARAMETERS ===== // + +/* [Setup Parameters] */ +$fa = 8; +$fs = 0.25; + +/* [General Settings] */ +// number of bases along x-axis +gridx = 2; +// number of bases along y-axis +gridy = 3; +// bin height. See bin height information and "gridz_define" below. +gridz = 42; +// base unit +length = 42; + +/* [Compartments] */ +// number of X Divisions +divx = 1; +// number of y Divisions +divy = 1; + +/* [Toggles] */ +// internal fillet for easy part removal +enable_scoop = true; +// snap gridz height to nearest 7mm increment +enable_zsnap = false; +// enable upper lip for stacking other bins +enable_lip = true; + +/* [Other] */ +// determine what the variable "gridz" applies to based on your use case +gridz_define = 2; // [0:gridz is the height of bins in units of 7mm increments - Zack's method,1:gridz is the internal height in millimeters, 2:gridz is the overall external height of the bin in millimeters] +// the type of tabs +style_tab = 1; //[0:Full,1:Auto,2:Left,3:Center,4:Right,5:None] + +// overrides internal block height of bin (for solid containers). Leave zero for default height. Units: mm +height_internal = 0; + +/* [Base] */ +style_hole = 3; // [0:no holes, 1:magnet holes only, 2: magnet and screw holes - no printable slit, 3: magnet and screw holes - printable slit] +// number of divisions per 1 unit of base along the X axis. (default 1, only use integers. 0 means automatically guess the right division) +div_base_x = 0; +// number of divisions per 1 unit of base along the Y axis. (default 1, only use integers. 0 means automatically guess the right division) +div_base_y = 0; + + + +// ===== IMPLEMENTATION ===== // + +color("tomato") { +gridfinityInit(gridx, gridy, height(gridz, gridz_define, enable_lip, enable_zsnap), height_internal, length) { + + cutEqual(n_divx = divx, n_divy = divy, style_tab = style_tab, enable_scoop = enable_scoop); +} +gridfinityBase(gridx, gridy, length, div_base_x, div_base_y, style_hole); + +} + + +// ===== EXAMPLES ===== // + +// 3x3 even spaced grid +/* +gridfinityInit(3, 3, height(6), 0, 42) { + cutEqual(n_divx = 3, n_divy = 3, style_tab = 0, enable_scoop = true); +} +gridfinityBase(3, 3, 42, 0, 0, 1); +*/ + +// Compartments can be placed anywhere (this includes non-integer positions like 1/2 or 1/3). The grid is defined as (0,0) being the bottom left corner of the bin, with each unit being 1 base long. Each cut() module is a compartment, with the first four values defining the area that should be made into a compartment (X coord, Y coord, width, and height). These values should all be positive. t is the tab style of the compartment (0:full, 1:auto, 2:left, 3:center, 4:right, 5:none). s is a toggle for the bottom scoop. +/* +gridfinityInit(3, 3, height(6), 0, 42) { + cut(x=0, y=0, w=1.5, h=0.5, t=5, s=false); + cut(0, 0.5, 1.5, 0.5, 5, false); + cut(0, 1, 1.5, 0.5, 5, false); + + cut(0,1.5,0.5,1.5,5,false); + cut(0.5,1.5,0.5,1.5,5,false); + cut(1,1.5,0.5,1.5,5,false); + + cut(1.5, 0, 1.5, 5/3, 2); + cut(1.5, 5/3, 1.5, 4/3, 4); +} +gridfinityBase(3, 3, 42, 0, 0, 1); +*/ + +// Compartments can overlap! This allows for weirdly shaped compartments, such as this "2" bin. +/* +gridfinityInit(3, 3, height(6), 0, 42) { + cut(0,2,2,1,5,false); + cut(1,0,1,3,5); + cut(1,0,2,1,5); + cut(0,0,1,2); + cut(2,1,1,2); +} +gridfinityBase(3, 3, 42, 0, 0, 1); +*/ + +// Areas without a compartment are solid material, where you can put your own cutout shapes. using the cut_move() function, you can select an area, and any child shapes will be moved from the origin to the center of that area, and subtracted from the block. For example, a pattern of three cylinderical holes. +/* +gridfinityInit(3, 3, height(6), 0, 42) { + cut(x=0, y=0, w=2, h=3); + cut(x=0, y=0, w=3, h=1, t=5); + cut_move(x=2, y=1, w=1, h=2) + pattern_linear(x=1, y=3, sx=42/2) + cylinder(r=5, h=1000, center=true); +} +gridfinityBase(3, 3, 42, 0, 0, 1); +*/ + +// You can use loops as well as the bin dimensions to make different parametric functions, such as this one, which divides the box into columns, with a small 1x1 top compartment and a long vertical compartment below +/* +gx = 3; +gy = 3; +gridfinityInit(gx, gy, height(6), 0, 42) { + for(i=[0:gx-1]) { + cut(i,0,1,gx-1); + cut(i,gx-1,1,1); + } +} +gridfinityBase(gx, gy, 42, 0, 0, 1); +*/ + +// Pyramid scheme bin +/* +gx = 4.5; +gy = 4; +gridfinityInit(gx, gy, height(6), 0, 42) { + for (i = [0:gx-1]) + for (j = [0:i]) + cut(j*gx/(i+1),gy-i-1,gx/(i+1),1,0); +} +gridfinityBase(gx, gy, 42, 0, 0, 1); +*/ \ No newline at end of file diff --git a/gridfinity-rebuilt-lite.scad b/gridfinity-rebuilt-lite.scad new file mode 100644 index 0000000..68e2400 --- /dev/null +++ b/gridfinity-rebuilt-lite.scad @@ -0,0 +1,93 @@ +include + +// ===== INFORMATION ===== // +/* + IMPORTANT: rendering will be better for analyzing the model if fast-csg is enabled. As of writing, this feature is only available in the development builds and not the official release of OpenSCAD, but it makes rendering only take a couple seconds, even for comically large bins. Enable it in Edit > Preferences > Features > fast-csg + +https://github.com/kennetek/gridfinity-rebuilt-openscad + +*/ + +// ===== PARAMETERS ===== // + +/* [Setup Parameters] */ +$fa = 8; +$fs = 0.25; + +/* [General Settings] */ +// number of bases along x-axis +gridx = 3; +// number of bases along y-axis +gridy = 3; +// bin height. See bin height information and "gridz_define" below. +gridz = 6; +// base unit +length = 42; + +/* [Compartments] */ +// number of X Divisions +divx = 2; +// number of y Divisions +divy = 2; + +/* [Toggles] */ +// snap gridz height to nearest 7mm increment +enable_zsnap = false; +// enable upper lip for stacking other bins +enable_lip = true; + +/* [Other] */ +// determine what the variable "gridz" applies to based on your use case +gridz_define = 0; // [0:gridz is the height of bins in units of 7mm increments - Zack's method,1:gridz is the internal height in millimeters, 2:gridz is the overall external height of the bin in millimeters] +// the type of tabs +style_tab = 1; //[0:Full,1:Auto,2:Left,3:Center,4:Right,5:None] + +/* [Base] */ +style_hole = 0; // [0:no holes, 1:magnet holes only, 2: magnet and screw holes - no printable slit, 3: magnet and screw holes - printable slit] +// number of divisions per 1 unit of base along the X axis. (default 1, only use integers. 0 means automatically guess the right division) +div_base_x = 0; +// number of divisions per 1 unit of base along the Y axis. (default 1, only use integers. 0 means automatically guess the right division) +div_base_y = 0; + + +// ===== IMPLEMENTATION ===== // + +// Input all the cutter types in here +color("tomato") +gridfinityLite(gridx, gridy, gridz, gridz_define, enable_lip, enable_zsnap, length, div_base_x, div_base_y, style_hole) { + cutEqual(n_divx = divx, n_divy = divy, style_tab = style_tab, enable_scoop = false); +} + +// ===== CONSTRUCTION ===== // + + +module gridfinityLite(gridx, gridy, gridz, gridz_define, enable_lip, enable_zsnap, length, div_base_x, div_base_y, style_hole) { + difference() { + union() { + gridfinityInit(gridx, gridy, height(gridz, gridz_define, enable_lip, enable_zsnap), 0, length) + children(); + gridfinityBase(gridx, gridy, length, div_base_x, div_base_y, style_hole); + } + + difference() { + union() { + intersection() { + difference() { + gridfinityBase(gridx, gridy, length, div_base_x, div_base_y, style_hole, -d_wall*2, false); + translate([-gridx*length/2,-gridy*length/2,2*h_base]) + cube([gridx*length,gridy*length,1000]); + } + translate([0,0,-1]) + rounded_rectangle(gridx*length-0.5005-d_wall*2, gridy*length-0.5005-d_wall*2, 1000, r_f2); + } + translate([0,0,h_base+d_clear]) + rounded_rectangle(gridx*length-0.5005-d_wall*2, gridy*length-0.5005-d_wall*2, h_base, r_f2); + } + + translate([0,0,-4*h_base]) + gridfinityInit(gridx, gridy, height(20,0), 0, length) + children(); + } + + } +} \ No newline at end of file diff --git a/gridfinity-rebuilt-utility.scad b/gridfinity-rebuilt-utility.scad new file mode 100644 index 0000000..50d4b10 --- /dev/null +++ b/gridfinity-rebuilt-utility.scad @@ -0,0 +1,444 @@ +// UTILITY FILE, DO NOT EDIT +// EDIT OTHER FILES IN REPO FOR RESULTS + +include + +// ===== User Modules ===== // + +// functions to convert gridz values to mm values +function hf (z, d, l) = (d==0)?z*7:(d==1)?h_bot+z+h_base:z-(l?3.8:0); +function height (z,d=0,l=true,s=true) = (s?((abs(hf(z,d,l))%7==0)?hf(z,d,l):hf(z,d,l)+7-abs(hf(z,d,l))%7):hf(z,d,l))-h_base; + +// Creates equally divided cutters for the bin +// +// n_divx: number of x compartments (ideally, coprime w/ gridx) +// n_divy: number of y compartments (ideally, coprime w/ gridy) +// set n_div values to 0 for a solid bin +// style_tab: tab style for all compartments. see cut() +// enable_scoop: scoop toggle for all compartments. see cut() +module cutEqual(n_divx=1, n_divy=1, style_tab=1, enable_scoop=true) { + for (i = [1:n_divx]) + for (j = [1:n_divy]) + cut((i-1)*$gxx/n_divx,(j-1)*$gyy/n_divy, $gxx/n_divx, $gyy/n_divy, style_tab, enable_scoop); +} + +// initialize gridfinity +module gridfinityInit(gx, gy, h, h0 = 0, l) { + $gxx = gx; + $gyy = gy; + $dh = h; + $dh0 = h0; + color("tomato") { + difference() { + color("firebrick") + block_bottom(h0==0?$dh-0.1:h0, gx, gy, l); + children(); + } + color("royalblue") + block_wall(gx, gy, l) { + if (enable_lip) profile_wall(); + else profile_wall2(); + } + } +} +// Function to include in the custom() module to individually slice bins +// Will try to clamp values to fit inside the provided base size +// +// x: start coord. x=1 is the left side of the bin. +// y: start coord. y=1 is the bottom side of the bin. +// w: width of compartment, in # of bases covered +// h: height of compartment, in # of basese covered +// t: tab style of this specific compartment. +// alignment only matters if the compartment size is larger than d_tabw +// 0:full, 1:auto, 2:left, 3:center, 4:right, 5:none +// Automatic alignment will use left tabs for bins on the left edge, right tabs for bins on the right edge, and center tabs everywhere else. +// s: toggle the rounded back corner that allows for easy removal +module cut(x=0, y=0, w=1, h=1, t=1, s=true) { + translate([0,0,-$dh-h_base]) + cut_move(x,y,w,h) + block_cutter(clp(x,0,$gxx), clp(y,0,$gyy), clp(w,0,$gxx-x), clp(h,0,$gyy-y), t, s); +} + +// Translates an object from the origin point to the center of the requested compartment block, can be used to add custom cuts in the bin +// See cut() module for parameter descriptions +module cut_move(x, y, w, h) { + translate([0,0,$dh0==0?$dh+h_base:$dh0+h_base]) + cut_move_unsafe(clp(x,0,$gxx), clp(y,0,$gyy), clp(w,0,$gxx-x), clp(h,0,$gyy-y)) + children(); +} + +// ===== Modules ===== // + +module profile_base() { + polygon([ + [0,0], + [0,h_base], + [r_base,h_base], + [r_base-r_c2,h_base-r_c2], + [r_base-r_c2,r_c1], + [r_base-r_c2-r_c1,0] + ]); +} + +module gridfinityBase(gx, gy, l, dx, dy, style_hole, off=0, final_cut=true) { + dbnxt = [for (i=[1:5]) if (abs(gx*i)%1 < 0.001 || abs(gx*i)%1 > 0.999) i]; + dbnyt = [for (i=[1:5]) if (abs(gy*i)%1 < 0.001 || abs(gy*i)%1 > 0.999) i]; + dbnx = 1/(dx==0 ? len(dbnxt) > 0 ? dbnxt[0] : 1 : round(dx)); + dbny = 1/(dy==0 ? len(dbnyt) > 0 ? dbnyt[0] : 1 : round(dy)); + xx = gx*l-0.5; + yy = gy*l-0.5; + + if (final_cut) + translate([0,0,h_base]) + rounded_rectangle(xx+0.002, yy+0.002, h_bot/1.5, r_fo1/2+0.001); + + intersection(){ + if (final_cut) + translate([0,0,-1]) + rounded_rectangle(xx+0.005, yy+0.005, h_base+h_bot/2*10, r_fo1/2+0.001); + + render() + difference() { + pattern_linear(gx/dbnx, gy/dbny, dbnx*l, dbny*l) + block_base_solid(dbnx, dbny, l, off); + + if (style_hole > 0) + pattern_linear(gx, gy, l) + block_base_hole(style_hole, off); + } + } +} + +module block_base_solid(dbnx, dbny, l, o) { + xx = dbnx*l-0.05; + yy = dbny*l-0.05; + oo = (o/2)*(sqrt(2)-1); + translate([0,0,h_base]) + mirror([0,0,1]) + union() { + hull() { + rounded_rectangle(xx-2*r_c2-2*r_c1+o, yy-2*r_c2-2*r_c1+o, h_base+oo, r_fo3/2); + rounded_rectangle(xx-2*r_c2+o, yy-2*r_c2+o, h_base-r_c1+oo, r_fo2/2); + } + translate([0,0,oo]) + hull() { + rounded_rectangle(xx-2*r_c2+o, yy-2*r_c2+o, r_c2, r_fo2/2); + mirror([0,0,1]) + rounded_rectangle(xx+o, yy+o, h_bot/2+abs(10*o), r_fo1/2); + } + } +} + +module block_base_hole(style_hole, o=0) { + r1 = r_hole1-o/2; + r2 = r_hole2-o/2; + pattern_circular(abs(d_hole)<0.001?1:4) + translate([d_hole/2, d_hole/2, 0]) + union() { + difference() { + cylinder(h = 2*(h_hole-o+(style_hole==3?h_slit:0)), r=r2, center=true); + + if (style_hole==3) + copy_mirror([0,1,0]) + translate([-1.5*r2,r1+0.1,h_hole-o]) + cube([r2*3,r2*3, 10]); + } + if (style_hole > 1) + cylinder(h = 2*h_base-o, r = r1, center=true); + } +} + +module profile_wall_sub_sub() { + polygon([ + [0,0], + [d_wall/2,0], + [d_wall/2,$dh-1.2-d_wall2+d_wall/2], + [d_wall2-d_clear,$dh-1.2], + [d_wall2-d_clear,$dh+h_base], + [0,$dh+h_base] + ]); +} + +module profile_wall_sub() { + difference() { + profile_wall_sub_sub(); + color("red") + offset(delta = d_clear) + translate([r_base-d_clear,$dh,0]) + mirror([1,0,0]) + profile_base(); + square([d_wall,0]); + } +} + +module profile_wall() { + translate([r_base,0,0]) + mirror([1,0,0]) + difference() { + profile_wall_sub(); + difference() { + translate([0, $dh+h_base-d_clear*sqrt(2), 0]) + circle(r_base/2); + offset(r = r_f1) + offset(delta = -r_f1) + profile_wall_sub(); + } + } +} + +// lipless profile +module profile_wall2() { + translate([r_base,0,0]) + mirror([1,0,0]) + square([d_wall,$dh]); +} + +module block_wall(gx, gy, l) { + translate([0,0,h_base]) + sweep_rounded(gx*l-2*r_base-0.5-0.001, gy*l-2*r_base-0.5-0.001) + children(); +} + +module block_bottom( h = 2.2, gx, gy, l ) { + translate([0,0,h_base+0.1]) + rounded_rectangle(gx*l-0.5-d_wall/4, gy*l-0.5-d_wall/4, h, r_base+0.01); +} + +module cut_move_unsafe(x, y, w, h) { + xx = ($gxx*length+d_magic); + yy = ($gyy*length+d_magic); + translate([(x)*xx/$gxx,(y)*yy/$gyy,0]) + translate([(-xx+d_div)/2,(-yy+d_div)/2,0]) + translate([(w*xx/$gxx-d_div)/2,(h*yy/$gyy-d_div)/2,0]) + children(); +} + +module block_cutter(x,y,w,h,t,s) { + + v_len_tab = d_tabh; + v_len_lip = d_wall2-d_wall+1.2; + v_cut_tab = d_tabh - (2*r_f1)/tan(a_tab); + v_cut_lip = d_wall2-d_wall-d_clear; + v_ang_tab = a_tab; + v_ang_lip = 45; + + ycutfirst = y == 0 && enable_lip; + ycutlast = abs(y+h-$gyy)<0.001 && enable_lip; + xcutfirst = x == 0 && enable_lip; + xcutlast = abs(x+w-$gxx)<0.001 && enable_lip; + zsmall = ($dh+h_base)/7 < 3; + + ylen = h*($gyy*length+d_magic)/$gyy-d_div; + xlen = w*($gxx*length+d_magic)/$gxx-d_div; + + height = $dh; + extent = (s && ycutfirst ? d_wall2-d_wall-d_clear : 0); + tab = (zsmall || t == 5) ? (ycutlast?v_len_lip:0) : v_len_tab; + ang = (zsmall || t == 5) ? (ycutlast?v_ang_lip:0) : v_ang_tab; + cut = (zsmall || t == 5) ? (ycutlast?v_cut_lip:0) : v_cut_tab; + style = (t > 1 && t < 5) ? t-3 : (x == 0 ? -1 : xcutlast ? 1 : 0); + + translate([0,ylen/2,h_base+h_bot]) + rotate([90,0,-90]) { + + if (!zsmall && xlen - d_tabw > 4*r_f2 && t != 0) { + fillet_cutter(3,"bisque") + difference() { + transform_tab(style, xlen, ((xcutfirst&&style==-1)||(xcutlast&&style==1))?v_cut_lip:0) + translate([ycutlast?v_cut_lip:0,0]) + profile_cutter(height-h_bot, ylen/2, s); + + if (xcutfirst) + translate([0,0,(xlen/2-r_f2)-v_cut_lip]) + cube([ylen,height,v_cut_lip*2]); + + if (xcutlast) + translate([0,0,-(xlen/2-r_f2)-v_cut_lip]) + cube([ylen,height,v_cut_lip*2]); + } + if (t != 0 && t != 5) + fillet_cutter(2,"indigo") + difference() { + transform_tab(style, xlen, ((xcutfirst&&style==-1)||(xcutlast&&style==1))?v_cut_lip:0) + difference() { + intersection() { + profile_cutter(height-h_bot, ylen-extent, s); + profile_cutter_tab(height-h_bot, v_len_tab, v_ang_tab); + } + if (ycutlast) profile_cutter_tab(height-h_bot, v_len_lip, 45); + } + + if (xcutfirst) + translate([ylen/2,0,xlen/2]) + rotate([0,90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + + if (xcutlast) + translate([ylen/2,0,-xlen/2]) + rotate([0,-90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + } + } + + fillet_cutter(1,"seagreen") + translate([0,0,xcutlast?v_cut_lip/2:0]) + translate([0,0,xcutfirst?-v_cut_lip/2:0]) + transform_main(xlen-(xcutfirst?v_cut_lip:0)-(xcutlast?v_cut_lip:0)) + translate([cut,0]) + profile_cutter(height-h_bot, ylen-extent-cut-(!s&&ycutfirst?v_cut_lip:0), s); + + fillet_cutter(0,"hotpink") + difference() { + transform_main(xlen) + difference() { + profile_cutter(height-h_bot, ylen-extent, s); + + if (!((zsmall || t == 5) && !ycutlast)) + profile_cutter_tab(height-h_bot, tab, ang); + + if (!s && y == 0) + translate([ylen-extent,0,0]) + mirror([1,0,0]) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + } + + if (xcutfirst) + color("indigo") + translate([ylen/2+0.001,0,xlen/2+0.001]) + rotate([0,90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + + if (xcutlast) + color("indigo") + translate([ylen/2+0.001,0,-xlen/2+0.001]) + rotate([0,-90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + } + + } +} + +module transform_main(xlen) { + translate([0,0,-(xlen-2*r_f2)/2]) + linear_extrude(xlen-2*r_f2) + children(); +} + +module transform_tab(type, xlen, cut) { + mirror([0,0,type==1?1:0]) + copy_mirror([0,0,-(abs(type)-1)]) + translate([0,0,-(xlen)/2]) + translate([0,0,r_f2]) + linear_extrude((xlen-d_tabw-abs(cut))/(1-(abs(type)-1))-2*r_f2) + children(); +} + +module fillet_cutter(t = 0, c = "goldenrod") { + color(c) + minkowski() { + children(); + sphere(r = r_f2-t/1000); + } +} + +module profile_cutter(h, l, s) { + scoop = s ? (length*(($dh-2)/7+1)/12-r_f2) : 0; + translate([r_f2,r_f2]) + hull() { + if (l-scoop-2*r_f2 > 0) + square(0.1); + if (scoop < h) { + translate([l-2*r_f2,h-r_f2/2]) + mirror([1,1]) + square(0.1); + + translate([0,h-r_f2/2]) + mirror([0,1]) + square(0.1); + } + difference() { + translate([l-scoop-2*r_f2, scoop]) + if (scoop != 0) { + intersection() { + circle(scoop); + mirror([0,1]) square(2*scoop); + } + } else mirror([1,0]) square(0.1); + translate([l-scoop-2*r_f2,-1]) + square([-(l-scoop-2*r_f2),2*h]); + + translate([0,h]) + square([2*l,scoop]); + } + } +} + +module profile_cutter_tab(h, tab, ang) { + if (tab > 0) + color("blue") + offset(delta = r_f2) + polygon([[0,h],[tab,h],[0,h-tab*tan(ang)]]); + +} + +// ==== Utilities ===== + +function clp(x,a,b) = min(max(x,a),b); + +module rounded_rectangle(length, width, height, rad) { + linear_extrude(height) + offset(rad) + offset(-rad) + square([length,width], center = true); +} + +module rounded_square(length, height, rad) { + rounded_rectangle(length, length, height, rad); +} + +module copy_mirror(vec=[0,1,0]) { + children(); + if (vec != [0,0,0]) + mirror(vec) + children(); +} + +module pattern_linear(x = 1, y = 1, sx = 0, sy = 0) { + yy = sy <= 0 ? sx : sy; + translate([-(x-1)*sx/2,-(y-1)*yy/2,0]) + for (i = [1:ceil(x)]) + for (j = [1:ceil(y)]) + translate([(i-1)*sx,(j-1)*yy,0]) + children(); +} + +module pattern_circular(n=2) { + for (i = [1:n]) + rotate(i*360/n) + children(); +} + +module sweep_rounded(w=10, h=10) { + union() pattern_circular(2) { + copy_mirror([1,0,0]) + translate([w/2,h/2,0]) + rotate_extrude(angle = 90, convexity = 4) + children(); + + translate([w/2,0,0]) + rotate([90,0,0]) + linear_extrude(height = h, center = true) + children(); + + rotate([0,0,90]) + translate([h/2,0,0]) + rotate([90,0,0]) + linear_extrude(height = w, center = true) + children(); + } +} +