From 388cb8dbab1b250aae549f22cb1d97f7aa0da732 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Tue, 30 Apr 2024 23:48:34 -0400 Subject: [PATCH 1/5] Fix baseplate not rendering Simplified profile_skeleton() by using `offset` instead of `minovsky`. This is much less computationally expensive, and easier to understand. --- gridfinity-rebuilt-baseplate.scad | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/gridfinity-rebuilt-baseplate.scad b/gridfinity-rebuilt-baseplate.scad index 202dbcb..e9ce842 100644 --- a/gridfinity-rebuilt-baseplate.scad +++ b/gridfinity-rebuilt-baseplate.scad @@ -182,19 +182,24 @@ module cutter_counterbore(){ } } +/** + * @brief 2d Cutter to skeletonize the baseplate. + * @example difference(){ + * cube(large_number); + * linear_extrude(large_number+TOLLERANCE) + * profile_skeleton(); + * } + */ module profile_skeleton() { l = l_grid-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([l_grid/2-d_hole_from_side,l_grid/2-d_hole_from_side,0]) - minkowski() { - square([l,l]); - circle(MAGNET_HOLE_RADIUS+r_skel+2); - } - } - circle(r_skel); + + offset(r_skel) + difference() { + square(l-2*r_skel+2*d_clear, center = true); + + hole_pattern() + offset(MAGNET_HOLE_RADIUS+r_skel+2) + square([l,l]); } } From 7aebad420c60c2684ede69c3565379284910d3e3 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Thu, 2 May 2024 00:26:54 -0400 Subject: [PATCH 2/5] Re-worked generic-helpers.rounded_square * Now behaves more like rounded_rectangle, but creates 2d or 3d items. * Deprecated rounded_rectangle. * Added significant input checks to ensure values are valid. --- generic-helpers.scad | 51 +++++++++++++++++++++++++++++++------ gridfinity-spiral-vase.scad | 12 ++++++--- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index 43a32b7..0a1f0ec 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -7,15 +7,50 @@ function clp(x,a,b) = min(max(x,a),b); function is_even(number) = (number%2)==0; -module rounded_rectangle(length, width, height, rad) { - linear_extrude(height) - offset(rad) - offset(-rad) - square([length,width], center = true); +/** + * @brief Create `square`, with rounded corners. + * @param size Same as `square`. See details for differences. + * @param radius Radius of the corners. 0 is the same as just calling `square` + * @param center Same as `square`. + * @details "size" accepts both the standard number or a 2d vector the same as `square`. + * However, if passed a 3d vector, this will apply a `linear_extrude` to the resulting shape. + */ +module rounded_square(size, radius, center = true) { + assert(is_num(size) || + (is_list(size) && ( + (len(size) == 2 && is_num(size.x) && is_num(size.y)) || + (len(size) == 3 && is_num(size.x) && is_num(size.y) && is_num(size.z)) + )) + ); + assert(is_num(radius) && radius >= 0 && is_bool(center)); + + // Make sure something is produced. + if (is_num(size)) { + assert((size/2) > radius); + } else { + assert((size.x/2) > radius && (size.y/2 > radius)); + if (len(size) == 3) { + assert(size.z > 0); + } + } + + if (is_list(size) && len(size) == 3) { + linear_extrude(size.z) + offset(radius) + offset(-radius) + square([size.x, size.y], center = center); + } else { + offset(radius) + offset(-radius) + square(size, center = center); + } } -module rounded_square(length, height, rad) { - rounded_rectangle(length, length, height, rad); +/** + * @deprecated Use rounded_square(...) + */ +module rounded_rectangle(length, width, height, rad) { + rounded_square([length, width, height], rad); } module copy_mirror(vec=[0,1,0]) { @@ -56,7 +91,7 @@ unity_matrix = [ * @param vector A 2d or 3d vectorm * @returns Magnitude of the vector. */ - function vector_magnitude(vector) = +function vector_magnitude(vector) = sqrt(vector.x^2 + vector.y^2 + (len(vector) == 3 ? vector.z^2 : 0)); /** diff --git a/gridfinity-spiral-vase.scad b/gridfinity-spiral-vase.scad index e03ebd7..f8b13ea 100644 --- a/gridfinity-spiral-vase.scad +++ b/gridfinity-spiral-vase.scad @@ -219,13 +219,17 @@ module block_magnet_blank(o = 0, half = true) { module block_base_blank(o = 0) { mirror([0,0,1]) { hull() { - rounded_square(l_grid-o-0.05-2*r_c2-2*r_c1, h_base, r_fo3); - rounded_square(l_grid-o-0.05-2*r_c2, h_base-r_c1, r_fo2); + linear_extrude(h_base) + rounded_square(l_grid-o-0.05-2*r_c2-2*r_c1, r_fo3); + linear_extrude(h_base-r_c1) + rounded_square(l_grid-o-0.05-2*r_c2, r_fo2); } hull() { - rounded_square(l_grid-o-0.05-2*r_c2, r_c2, r_fo2); + linear_extrude(r_c2) + rounded_square(l_grid-o-0.05-2*r_c2, r_fo2); mirror([0,0,1]) - rounded_square(l_grid-o-0.05, d_bottom, r_fo1); + linear_extrude(d_bottom) + rounded_square(l_grid-o-0.05, r_fo1); } } } From 54f0f4f93590c3178b41809e35a7e923e04b7cc2 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Thu, 2 May 2024 00:46:41 -0400 Subject: [PATCH 3/5] Simplify rounded_square and make defaults match `square` The default for square is center=false. --- generic-helpers.scad | 22 ++++++++++++++++------ gridfinity-spiral-vase.scad | 12 ++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index 0a1f0ec..cd3302c 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -15,7 +15,7 @@ function is_even(number) = (number%2)==0; * @details "size" accepts both the standard number or a 2d vector the same as `square`. * However, if passed a 3d vector, this will apply a `linear_extrude` to the resulting shape. */ -module rounded_square(size, radius, center = true) { +module rounded_square(size, radius, center = false) { assert(is_num(size) || (is_list(size) && ( (len(size) == 2 && is_num(size.x) && is_num(size.y)) || @@ -36,13 +36,23 @@ module rounded_square(size, radius, center = true) { if (is_list(size) && len(size) == 3) { linear_extrude(size.z) + _internal_rounded_square_2d(size, radius, center); + } else { + _internal_rounded_square_2d(size, radius, center); + } +} + +/** + * @brief Internal module. Do not use. May be changed/removed at any time. + */ +module _internal_rounded_square_2d(size, radius, center) { + diameter = 2*radius; + if (is_list(size)) { offset(radius) - offset(-radius) - square([size.x, size.y], center = center); + square([size.x-diameter, size.y-diameter], center = center); } else { offset(radius) - offset(-radius) - square(size, center = center); + square(size-diameter, center = center); } } @@ -50,7 +60,7 @@ module rounded_square(size, radius, center = true) { * @deprecated Use rounded_square(...) */ module rounded_rectangle(length, width, height, rad) { - rounded_square([length, width, height], rad); + rounded_square([length, width, height], rad, center=true); } module copy_mirror(vec=[0,1,0]) { diff --git a/gridfinity-spiral-vase.scad b/gridfinity-spiral-vase.scad index f8b13ea..d4e09fb 100644 --- a/gridfinity-spiral-vase.scad +++ b/gridfinity-spiral-vase.scad @@ -161,7 +161,7 @@ module gridfinityBaseVase() { intersection() { block_base_blank(0); translate([0,0,-h_base-1]) - rounded_rectangle(l_grid-0.5-0.005, l_grid-0.5-0.005, h_base*10, r_fo1+0.001); + rounded_square([l_grid-0.5-0.005, l_grid-0.5-0.005, h_base*10], r_fo1+0.001, center=true); } translate([0,0,0.01]) difference() { @@ -220,16 +220,16 @@ module block_base_blank(o = 0) { mirror([0,0,1]) { hull() { linear_extrude(h_base) - rounded_square(l_grid-o-0.05-2*r_c2-2*r_c1, r_fo3); + rounded_square(l_grid-o-0.05-2*r_c2-2*r_c1, r_fo3, center=true); linear_extrude(h_base-r_c1) - rounded_square(l_grid-o-0.05-2*r_c2, r_fo2); + rounded_square(l_grid-o-0.05-2*r_c2, r_fo2, center=true); } hull() { linear_extrude(r_c2) - rounded_square(l_grid-o-0.05-2*r_c2, r_fo2); + rounded_square(l_grid-o-0.05-2*r_c2, r_fo2, center=true); mirror([0,0,1]) linear_extrude(d_bottom) - rounded_square(l_grid-o-0.05, r_fo1); + rounded_square(l_grid-o-0.05, r_fo1, center=true); } } } @@ -527,7 +527,7 @@ module transform_scoop() { module block_vase(h = d_height*2) { translate([0,0,-0.1]) - rounded_rectangle(gridx*l_grid-0.5-nozzle, gridy*l_grid-0.5-nozzle, h, r_base+0.01-nozzle/2); + rounded_square([gridx*l_grid-0.5-nozzle, gridy*l_grid-0.5-nozzle, h], r_base+0.01-nozzle/2, center=true); } module profile_x(x_f = 3) { From 8bfd05be8e9d2488296c75063c438ca7e894c5a9 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Thu, 2 May 2024 04:07:40 -0400 Subject: [PATCH 4/5] Generate Baseplate from profile isntead of as a negative Previous implementation was off by 0.5mm, and required creating a gridfinity base. This is much more flexible, and easier to understand. --- generic-helpers.scad | 2 + gridfinity-rebuilt-baseplate.scad | 156 ++++++++++++++++++++++++------ standard.scad | 42 +++++++- 3 files changed, 166 insertions(+), 34 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index cd3302c..fbab526 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -166,6 +166,8 @@ function affine_translate(vector) = [ * Centered on origin. */ module sweep_rounded(width=10, length=10) { + assert(width > 0 && length > 0); + half_width = width/2; half_length = length/2; path_points = [ diff --git a/gridfinity-rebuilt-baseplate.scad b/gridfinity-rebuilt-baseplate.scad index e9ce842..de3802e 100644 --- a/gridfinity-rebuilt-baseplate.scad +++ b/gridfinity-rebuilt-baseplate.scad @@ -18,9 +18,9 @@ $fs = 0.25; /* [General Settings] */ // number of bases along x-axis -gridx = 5; +gridx = 1; // number of bases along y-axis -gridy = 5; +gridy = 1; /* [Screw Together Settings - Defaults work for M3 and 4-40] */ // screw diameter @@ -48,11 +48,11 @@ fity = 0; // [-1:0.1:1] /* [Styles] */ // baseplate styles -style_plate = 0; // [0: thin, 1:weighted, 2:skeletonized, 3: screw together, 4: screw together minimal] +style_plate = 2; // [0: thin, 1:weighted, 2:skeletonized, 3: screw together, 4: screw together minimal] // hole styles -style_hole = 2; // [0:none, 1:countersink, 2:counterbore] +style_hole = 0; // [0:none, 1:countersink, 2:counterbore] /* [Magnet Hole] */ // Baseplate will have holes for 6mm Diameter x 2mm high magnets. @@ -69,7 +69,6 @@ hole_options = bundle_hole_options(refined_hole=false, magnet_hole=enable_magnet color("tomato") gridfinityBaseplate(gridx, gridy, l_grid, distancex, distancey, style_plate, hole_options, style_hole, fitx, fity); - // ===== CONSTRUCTION ===== // module gridfinityBaseplate(gridx, gridy, length, dix, diy, sp, hole_options, sh, fitx, fity) { @@ -87,44 +86,62 @@ module gridfinityBaseplate(gridx, gridy, length, dix, diy, sp, hole_options, sh, offsetx = dix < dx ? 0 : (gx*length-bp_xy_clearance-dix)/2*fitx*-1; offsety = diy < dy ? 0 : (gy*length-bp_xy_clearance-diy)/2*fity*-1; + screw_together = sp == 3 || sp == 4; + minimal = sp == 0 || sp == 4; + difference() { - translate([offsetx,offsety,h_base]) - mirror([0,0,1]) - rounded_rectangle(dx, dy, h_base+off, r_base); - - gridfinityBase(gx, gy, length, 1, 1, bundle_hole_options(), 0.5, false); - - translate([offsetx,offsety,h_base-0.6]) - rounded_rectangle(dx*2, dy*2, h_base*2, r_base); - pattern_linear(gx, gy, length) { - render(convexity = 6) { + difference() { + if (minimal) { + square_baseplate_lip(off); + } else { + solid_square_baseplate(off); + } - if (sp == 1) - translate([0,0,-off]) + // Bottom/through pattern for the solid baseplates. + if (sp == 1) { cutter_weight(); - else if (sp == 2 || sp == 3) - linear_extrude(10*(h_base+off), center = true) + } else if (sp == 2 || sp == 3) { + translate([0,0,-TOLLERANCE]) + linear_extrude(off+2*TOLLERANCE) profile_skeleton(); - else if (sp == 4) - translate([0,0,-5*(h_base+off)]) - rounded_square(length-2*r_c2-2*r_c1, 10*(h_base+off), r_fo3); - + } + // Add holes to the solid baseplates. hole_pattern(){ + // Manget hole + translate([0, 0, off+TOLLERANCE]) mirror([0, 0, 1]) block_base_hole(hole_options); - translate([0,0,-off-TOLLERANCE]) - if (sh == 1) cutter_countersink(); - else if (sh == 2) cutter_counterbore(); + translate([0,0,-TOLLERANCE]) + if (sh == 1) { + cutter_countersink(); + } else if (sh == 2) { + cutter_counterbore(); + } } + } } - screw_together = sp == 3 || sp == 4; - if (screw_together) cutter_screw_together(gx, gy, off); - } + // Round the outside corners + corner_center_distance = length/2; + copy_mirror([0, 1, 0]) + copy_mirror([1, 0, 0]) + translate([ + (gx*length/2) - BASEPLATE_OUTSIDE_RADIUS, + (gy*length/2) - BASEPLATE_OUTSIDE_RADIUS, + -TOLLERANCE + ]) + scale([1+TOLLERANCE, 1+TOLLERANCE, 1+2*TOLLERANCE]) + square_baseplate_corner(off); + + if (screw_together) { + translate([0, 0, off]) + cutter_screw_together(gx, gy, off); + } + } } function calculate_offset(style_plate, enable_magnet, style_hole) = @@ -182,20 +199,95 @@ module cutter_counterbore(){ } } +/** + * @brief Added or removed from the baseplate to square off or round the corners. + * @param height Baseplate's height excluding lip and clearance height. + */ +module square_baseplate_corner(height=0) { + assert(height >= 0); + linear_extrude(height + BASEPLATE_LIP_MAX.y) + difference() { + square(BASEPLATE_OUTSIDE_RADIUS, center=false); + circle(r=BASEPLATE_OUTSIDE_RADIUS-TOLLERANCE); + } +} + +/** + * @brief Outer edge/lip of the baseplate. + * @details Includes clearance to ensure the base touches the lip + * instead of the bottom. + * @param height Baseplate's height excluding lip and clearance height. + * @param width How wide a single baseplate is. Only set if deviating from the standard! + * @param length How long a single baseplate is. Only set if deviating from the standard! + */ +module baseplate_lip(height=0, width=l_grid, length=l_grid) { + assert(height >= 0); + + // How far, in the +x direction, + // the lip needs to be from it's [0, 0] point + // such that when swept by 90 degrees to produce a corner, + // the outside edge has the desired radius. + translation_x = BASEPLATE_OUTSIDE_RADIUS - BASEPLATE_LIP_MAX.x; + + additional_height = height + BASEPLATE_CLEARANCE_HEIGHT; + + sweep_rounded(width-2*BASEPLATE_OUTSIDE_RADIUS, length-2*BASEPLATE_OUTSIDE_RADIUS) + translate([translation_x, additional_height, 0]) + polygon(concat(BASEPLATE_LIP, [ + [0, -additional_height], + [BASEPLATE_LIP_MAX.x, -additional_height], + [BASEPLATE_LIP_MAX.x, 0] + ])); +} + +/** + * @brief Outer edge/lip of the baseplate, with square corners. + * @details Needed to prevent gaps when joining multiples together. + * @param height Baseplate's height excluding lip and clearance height. + * @param size Width/Length of a single baseplate. Only set if deviating from the standard! + */ +module square_baseplate_lip(height=0, size = l_grid) { + assert(height >= 0 && size/2 >= BASEPLATE_OUTSIDE_RADIUS); + corner_center_distance = size/2 - BASEPLATE_OUTSIDE_RADIUS; + union() { + baseplate_lip(height, size, size); + pattern_circular(4) + translate([corner_center_distance, corner_center_distance, 0]) + square_baseplate_corner(height); + } +} + +/** + * @brief A single baseplate with square corners, a solid inner section, lip and the set clearance height. + * @param height Baseplate's height excluding lip and clearance height. + * @details A height of zero is the equivalent of just calling square_baseplate_lip() + */ +module solid_square_baseplate(height=0, length = l_grid) { + assert(height >= 0); + union() { + square_baseplate_lip(height, length); + if (height > 0) { + linear_extrude(height) + square(length - BASEPLATE_OUTSIDE_RADIUS, center=true); + } + } +} + /** * @brief 2d Cutter to skeletonize the baseplate. + * @param size Width/Length of a single baseplate. Only set if deviating from the standard! * @example difference(){ * cube(large_number); * linear_extrude(large_number+TOLLERANCE) * profile_skeleton(); * } */ -module profile_skeleton() { - l = l_grid-2*r_c2-2*r_c1; +module profile_skeleton(size=l_grid) { + l = size - 2*BASEPLATE_LIP_MAX.x; offset(r_skel) difference() { - square(l-2*r_skel+2*d_clear, center = true); + square(l-2*r_skel, center = true); hole_pattern() offset(MAGNET_HOLE_RADIUS+r_skel+2) diff --git a/standard.scad b/standard.scad index 1ceb504..f80f2c8 100644 --- a/standard.scad +++ b/standard.scad @@ -88,8 +88,11 @@ h_lip = 3.548; d_wall2 = r_base-r_c1-d_clear*sqrt(2); d_magic = -2*d_clear-2*d_wall+d_div; -// Stacking Lip + +// **************************************** +// Stacking Lip Constants // Based on https://gridfinity.xyz/specification/ +// **************************************** stacking_lip_inner_slope_height_mm = 0.7; stacking_lip_wall_height_mm = 1.8; stacking_lip_outer_slope_height_mm = 1.9; @@ -106,8 +109,40 @@ stacking_lip_support_wall_height_mm = 1.2; stacking_lip_support_height_mm = stacking_lip_support_wall_height_mm + d_wall2; - +// **************************************** // Baseplate constants +// Based on https://gridfinity.xyz/specification/ +// **************************************** +BASEPLATE_OUTSIDE_RADIUS = 8 / 2; + +// Polygon describing the raw baseplate lip. +// Does NOT include clearance height. +BASEPLATE_LIP = [ + [0, 0], // Innermost bottom point + [0.7, 0.7], // Up and out at a 45 degree angle + [0.7, (0.7+1.8)], // Straight up + [(0.7+2.15), (0.7+1.8+2.15)], // Up and out at a 45 degree angle + [(0.7+2.15), 0], // Straight down + [0, 0] //Back to start +]; + +// Height of the baseplate lip. +// This ads clearance height to the polygon +// that ensures the base makes contact with the baseplate lip. +BASEPLATE_LIP_HEIGHT = 5; + +// The minimum height between the baseplate lip and anything below it. +// Needed to make sure the base always makes contact with the baseplate lip. +BASEPLATE_CLEARANCE_HEIGHT = BASEPLATE_LIP_HEIGHT - BASEPLATE_LIP[3].y; +assert(BASEPLATE_CLEARANCE_HEIGHT > 0, "Negative clearance doesn't make sense."); + +// Maximum [x,y] values/size of the baseplate lip. +// Includes clearance height! +BASEPLATE_LIP_MAX = [BASEPLATE_LIP[3].x, BASEPLATE_LIP_HEIGHT]; + +// **************************************** +// Weighted Baseplate +// **************************************** // Baseplate bottom part height (part added with weigthed=true) bp_h_bot = 6.4; @@ -121,6 +156,9 @@ bp_rcut_width = 8.5; bp_rcut_length = 4.25; // Baseplate bottom cutout rounded thingy depth bp_rcut_depth = 2; + +// **************************************** + // Baseplate clearance offset bp_xy_clearance = 0.5; // radius of cutout for skeletonized baseplate From 3f10786861ff5d3b8d58b7e6cc5ab9b3368e229a Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sat, 15 Jun 2024 15:00:59 -0400 Subject: [PATCH 5/5] Baseplate: Fix "Fit To Drawer" functionality. No tests added yet. --- gridfinity-rebuilt-baseplate.scad | 222 +++++++++++++++++++++--------- 1 file changed, 158 insertions(+), 64 deletions(-) diff --git a/gridfinity-rebuilt-baseplate.scad b/gridfinity-rebuilt-baseplate.scad index de3802e..d850272 100644 --- a/gridfinity-rebuilt-baseplate.scad +++ b/gridfinity-rebuilt-baseplate.scad @@ -48,7 +48,7 @@ fity = 0; // [-1:0.1:1] /* [Styles] */ // baseplate styles -style_plate = 2; // [0: thin, 1:weighted, 2:skeletonized, 3: screw together, 4: screw together minimal] +style_plate = 3; // [0: thin, 1:weighted, 2:skeletonized, 3: screw together, 4: screw together minimal] // hole styles @@ -67,79 +67,161 @@ hole_options = bundle_hole_options(refined_hole=false, magnet_hole=enable_magnet // ===== IMPLEMENTATION ===== // color("tomato") -gridfinityBaseplate(gridx, gridy, l_grid, distancex, distancey, style_plate, hole_options, style_hole, fitx, fity); +gridfinityBaseplate([gridx, gridy], l_grid, [distancex, distancey], style_plate, hole_options, style_hole, [fitx, fity]); // ===== CONSTRUCTION ===== // -module gridfinityBaseplate(gridx, gridy, length, dix, diy, sp, hole_options, sh, fitx, fity) { +/** + * @brief Create a baseplate. + * @param grid_size_bases Number of Gridfinity bases. + * 2d Vector. [x, y]. + * Set to [0, 0] to auto calculate using min_size_mm. + * @param length X,Y size of a single Gridfinity base. + * @param min_size_mm Minimum size of the baseplate. [x, y] + * Extra space is filled with solid material. + * Enables "Fit to Drawer." + * @param sp Baseplate Style + * @param hole_options + * @param sh Style of screw hole allowing the baseplate to be mounted to something. + * @param fit_offset Determines where padding is added. + */ +module gridfinityBaseplate(grid_size_bases, length, min_size_mm, sp, hole_options, sh, fit_offset = [0, 0]) { - assert(gridx > 0 || dix > 0, "Must have positive x grid amount!"); - assert(gridy > 0 || diy > 0, "Must have positive y grid amount!"); + assert(is_list(grid_size_bases) && len(grid_size_bases) == 2, + "grid_size_bases must be a 2d list"); + assert(is_list(min_size_mm) && len(min_size_mm) == 2, + "min_size_mm must be a 2d list"); + assert(is_list(fit_offset) && len(fit_offset) == 2, + "fit_offset must be a 2d list"); + assert(grid_size_bases.x > 0 || min_size_mm.x > 0, + "Must have positive x grid amount!"); + assert(grid_size_bases.y > 0 || min_size_mm.y > 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-bp_xy_clearance, dix); - dy = max(gy*length-bp_xy_clearance, diy); + additional_height = calculate_offset(sp, hole_options[1], sh); - off = calculate_offset(sp, hole_options[1], sh); + // Final height of the baseplate. In mm. + baseplate_height_mm = additional_height + BASEPLATE_LIP_MAX.y; - offsetx = dix < dx ? 0 : (gx*length-bp_xy_clearance-dix)/2*fitx*-1; - offsety = diy < dy ? 0 : (gy*length-bp_xy_clearance-diy)/2*fity*-1; + // Final size in number of bases + grid_size = [for (i = [0:1]) + grid_size_bases[i] == 0 ? floor(min_size_mm[i]/length) : grid_size_bases[i]]; + + // Final size of the base before padding. In mm. + grid_size_mm = concat(grid_size * length, [baseplate_height_mm]); + + // Final size, including padding. In mm. + size_mm = [ + max(grid_size_mm.x, min_size_mm.x), + max(grid_size_mm.y, min_size_mm.y), + baseplate_height_mm + ]; + + // Amount of padding needed to fit to a specific drawer size. In mm. + padding_mm = size_mm - grid_size_mm; + + is_padding_needed = padding_mm != [0, 0, 0]; + + //Convert the fit offset to percent of how much will be added to the positive axes. + // -1 : 1 -> 0 : 1 + fit_percent_positive = [for (i = [0:1]) (fit_offset[i] + 1) / 2]; + + padding_start_point = -grid_size_mm/2 - + [ + padding_mm.x * (1 - fit_percent_positive.x), + padding_mm.y * (1 - fit_percent_positive.y), + -grid_size_mm.z/2 + ]; + + corner_points = [ + padding_start_point + [size_mm.x, size_mm.y, 0], + padding_start_point + [0, size_mm.y, 0], + padding_start_point, + padding_start_point + [size_mm.x, 0, 0], + ]; + + echo(str("Number of Grids per axes (X, Y)]: ", grid_size)); + echo(str("Final size (in mm): ", size_mm)); + if (is_padding_needed) { + echo(str("Padding +X (in mm): ", padding_mm.x * fit_percent_positive.x)); + echo(str("Padding -X (in mm): ", padding_mm.x * (1 - fit_percent_positive.x))); + echo(str("Padding +Y (in mm): ", padding_mm.y * fit_percent_positive.y)); + echo(str("Padding -Y (in mm): ", padding_mm.y * (1 - fit_percent_positive.y))); + } screw_together = sp == 3 || sp == 4; minimal = sp == 0 || sp == 4; difference() { - pattern_linear(gx, gy, length) { - difference() { - if (minimal) { - square_baseplate_lip(off); - } else { - solid_square_baseplate(off); - } + union() { + // Baseplate itself + pattern_linear(grid_size.x, grid_size.y, length) { + // Single Baseplate piece + difference() { + if (minimal) { + square_baseplate_lip(additional_height); + } else { + solid_square_baseplate(additional_height); + } - // Bottom/through pattern for the solid baseplates. - if (sp == 1) { - cutter_weight(); - } else if (sp == 2 || sp == 3) { - translate([0,0,-TOLLERANCE]) - linear_extrude(off+2*TOLLERANCE) - profile_skeleton(); - } + // Bottom/through pattern for the solid baseplates. + if (sp == 1) { + cutter_weight(); + } else if (sp == 2 || sp == 3) { + translate([0,0,-TOLLERANCE]) + linear_extrude(additional_height + (2 * TOLLERANCE)) + profile_skeleton(); + } - // Add holes to the solid baseplates. - hole_pattern(){ - // Manget hole - translate([0, 0, off+TOLLERANCE]) - mirror([0, 0, 1]) - block_base_hole(hole_options); + // Add holes to the solid baseplates. + hole_pattern(){ + // Manget hole + translate([0, 0, additional_height+TOLLERANCE]) + mirror([0, 0, 1]) + block_base_hole(hole_options); - translate([0,0,-TOLLERANCE]) - if (sh == 1) { - cutter_countersink(); - } else if (sh == 2) { - cutter_counterbore(); + translate([0,0,-TOLLERANCE]) + if (sh == 1) { + cutter_countersink(); + } else if (sh == 2) { + cutter_counterbore(); + } } } + } + // Padding + if (is_padding_needed) { + render() + difference() { + translate(padding_start_point) + cube(size_mm); + + translate([ + -grid_size_mm.x/2, + -grid_size_mm.y/2, + 0 + ]) + cube(grid_size_mm); + } } } - // Round the outside corners - corner_center_distance = length/2; - copy_mirror([0, 1, 0]) - copy_mirror([1, 0, 0]) - translate([ - (gx*length/2) - BASEPLATE_OUTSIDE_RADIUS, - (gy*length/2) - BASEPLATE_OUTSIDE_RADIUS, - -TOLLERANCE - ]) - scale([1+TOLLERANCE, 1+TOLLERANCE, 1+2*TOLLERANCE]) - square_baseplate_corner(off); + // Round the outside corners (Including Padding) + for(i = [0:len(corner_points) - 1]) { + point = corner_points[i]; + translate([ + point.x + (BASEPLATE_OUTSIDE_RADIUS * -sign(point.x)), + point.y + (BASEPLATE_OUTSIDE_RADIUS * -sign(point.y)), + 0 + ]) + rotate([0, 0, i*90]) + square_baseplate_corner(additional_height, true); + } if (screw_together) { - translate([0, 0, off]) - cutter_screw_together(gx, gy, off); + translate([0, 0, additional_height/2]) + cutter_screw_together(grid_size.x, grid_size.y, length); } } } @@ -201,14 +283,21 @@ module cutter_counterbore(){ /** * @brief Added or removed from the baseplate to square off or round the corners. - * @param height Baseplate's height excluding lip and clearance height. + * @param height Baseplate's height, excluding lip and clearance height. + * @param subtract If the corner should be scaled to allow subtraction. */ -module square_baseplate_corner(height=0) { +module square_baseplate_corner(height=0, subtract=false) { assert(height >= 0); - linear_extrude(height + BASEPLATE_LIP_MAX.y) + assert(is_bool(subtract)); + + subtract_ammount = subtract ? TOLLERANCE : 0; + + translate([0, 0, -subtract_ammount]) + linear_extrude(height + BASEPLATE_LIP_MAX.y + (2 * subtract_ammount)) difference() { - square(BASEPLATE_OUTSIDE_RADIUS, center=false); - circle(r=BASEPLATE_OUTSIDE_RADIUS-TOLLERANCE); + square(BASEPLATE_OUTSIDE_RADIUS + subtract_ammount , center=false); + // TOLLERANCE needed to prevent a gap + circle(r=BASEPLATE_OUTSIDE_RADIUS - TOLLERANCE); } } @@ -248,7 +337,10 @@ module baseplate_lip(height=0, width=l_grid, length=l_grid) { */ module square_baseplate_lip(height=0, size = l_grid) { assert(height >= 0 && size/2 >= BASEPLATE_OUTSIDE_RADIUS); + corner_center_distance = size/2 - BASEPLATE_OUTSIDE_RADIUS; + + render(convexity = 2) // Fixes ghosting in preview union() { baseplate_lip(height, size, size); pattern_circular(4) @@ -260,15 +352,17 @@ module square_baseplate_lip(height=0, size = l_grid) { /** * @brief A single baseplate with square corners, a solid inner section, lip and the set clearance height. * @param height Baseplate's height excluding lip and clearance height. + * @param size Width/Length of a single baseplate. Only set if deviating from the standard! * @details A height of zero is the equivalent of just calling square_baseplate_lip() */ -module solid_square_baseplate(height=0, length = l_grid) { - assert(height >= 0); +module solid_square_baseplate(height=0, size = l_grid) { + assert(height >= 0 && size > 0); + union() { - square_baseplate_lip(height, length); + square_baseplate_lip(height, size); if (height > 0) { linear_extrude(height) - square(length - BASEPLATE_OUTSIDE_RADIUS, center=true); + square(size - BASEPLATE_OUTSIDE_RADIUS, center=true); } } } @@ -295,7 +389,7 @@ module profile_skeleton(size=l_grid) { } } -module cutter_screw_together(gx, gy, off) { +module cutter_screw_together(gx, gy, size = l_grid) { screw(gx, gy); rotate([0,0,90]) @@ -303,10 +397,10 @@ module cutter_screw_together(gx, gy, off) { module screw(a, b) { copy_mirror([1,0,0]) - translate([a*l_grid/2, 0, -off/2]) - pattern_linear(1, b, 1, l_grid) + translate([a*size/2, 0, 0]) + pattern_linear(1, b, 1, size) pattern_linear(1, n_screws, 1, d_screw_head + screw_spacing) rotate([0,90,0]) - cylinder(h=l_grid/2, d=d_screw, center = true); + cylinder(h=size/2, d=d_screw, center = true); } }