diff --git a/gridfinity-rebuilt-holes.scad b/gridfinity-rebuilt-holes.scad index 382cd05..5aa3efd 100644 --- a/gridfinity-rebuilt-holes.scad +++ b/gridfinity-rebuilt-holes.scad @@ -235,8 +235,30 @@ module screw_hole(radius, height, supportless=false, chamfer_radius=0, chamfer_a * @param supportless If the magnet/screw hole should be printed in such a way that the screw hole does not require supports. */ function bundle_hole_options(refined_hole=false, magnet_hole=false, screw_hole=false, crush_ribs=false, chamfer=false, supportless=false) = + assert( + is_bool(refined_hole) && + is_bool(magnet_hole) && + is_bool(screw_hole) && + is_bool(crush_ribs) && + is_bool(chamfer) && + is_bool(supportless)) [refined_hole, magnet_hole, screw_hole, crush_ribs, chamfer, supportless]; +/** + * @summary Ensures that hole options are valid, and can be used. + */ +module assert_hole_options_valid(hole_options) { + assert(is_list(hole_options) && len(hole_options) == 6); + for(option=hole_options){ + assert(is_bool(option), "One or more hole options is not a boolean value!"); + } + refined_hole = hole_options[0]; + magnet_hole = hole_options[1]; + if(refined_hole) { + assert(!magnet_hole, "magnet_hole is not compatible with refined_hole"); + } +} + /** * @brief A single magnet/screw hole. To be cut out of the base. * @details Supports multiple options that can be mixed and matched. @@ -244,7 +266,8 @@ function bundle_hole_options(refined_hole=false, magnet_hole=false, screw_hole=f * @param o Offset */ module block_base_hole(hole_options, o=0) { - assert(is_list(hole_options)); + assert_hole_options_valid(hole_options); + assert(is_num(o)); // Destructure the options refined_hole = hole_options[0]; @@ -254,11 +277,6 @@ module block_base_hole(hole_options, o=0) { chamfer = hole_options[4]; supportless = hole_options[5]; - // Validate said options - if(refined_hole) { - assert(!magnet_hole, "magnet_hole is not compatible with refined_hole"); - } - screw_radius = SCREW_HOLE_RADIUS - (o/2); magnet_radius = MAGNET_HOLE_RADIUS - (o/2); magnet_inner_radius = MAGNET_HOLE_CRUSH_RIB_INNER_RADIUS - (o/2); diff --git a/gridfinity-rebuilt-utility.scad b/gridfinity-rebuilt-utility.scad index 22a27a1..9130e30 100644 --- a/gridfinity-rebuilt-utility.scad +++ b/gridfinity-rebuilt-utility.scad @@ -199,103 +199,122 @@ module cut_move(x, y, w, h) { // ===== 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] - ]); -} +/** + *@summary Create the base of a gridfinity bin, or use it for a custom object. + * @param length X,Y size of a single Gridfinity base. + */ +module gridfinityBase(gx, gy, length, dx, dy, hole_options=bundle_hole_options(), off=0, final_cut=true, only_corners=false) { + assert( + is_num(gx) && + is_num(gy) && + is_num(length) && + is_num(dx) && + is_num(dy) && + is_bool(final_cut) && + is_bool(only_corners) + ); -module gridfinityBase(gx, gy, l, dx, dy, hole_options=bundle_hole_options(), off=0, final_cut=true, only_corners=false) { 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; + dbnx = 1/(dx != 0 ? round(dx) : (len(dbnxt) > 0 ? dbnxt[0] : 1)); + dbny = 1/(dy != 0 ? round(dy) : (len(dbnyt) > 0 ? dbnyt[0] : 1)); - if (final_cut) - translate([0,0,h_base]) - rounded_rectangle(xx+0.002, yy+0.002, h_bot/1.5, r_fo1+0.001); + // Final size in number of bases + grid_size = [gx/dbnx, gy/dbny]; - intersection(){ - if (final_cut) - translate([0,0,-1]) - rounded_rectangle(xx+0.005, yy+0.005, h_base+h_bot/2*10, r_fo1+0.001); + // Per spec, there's a 0.5mm gap between each base, + // But that needs to be scaled based on everything else. + individual_base_size_mm = [dbnx, dbny] * BASE_SIZE; + base_center_distance_mm = [dbnx, dbny] * length; + gap_mm = base_center_distance_mm - individual_base_size_mm; - if(only_corners) { - difference(){ - pattern_linear(gx/dbnx, gy/dbny, dbnx*l, dbny*l) - block_base(gx, gy, l, dbnx, dbny, bundle_hole_options(), off); + // Final size of the base top. In mm. + grid_size_mm = [ + base_center_distance_mm.x * grid_size.x, + base_center_distance_mm.y * grid_size.y, + ] - gap_mm; - copy_mirror([0, 1, 0]) { - copy_mirror([1, 0, 0]) { - translate([ - (gx/2)*l_grid - d_hole_from_side, - (gy/2) * l_grid - d_hole_from_side, - 0 - ]) - block_base_hole(hole_options, off); - } + if (final_cut) { + translate([0, 0, h_base-TOLLERANCE]) + rounded_square([grid_size_mm.x, grid_size_mm.y, h_bot], BASE_OUTSIDE_RADIUS, center=true); + } + + if(only_corners) { + difference(){ + pattern_linear(grid_size.x, grid_size.y, base_center_distance_mm.x, base_center_distance_mm.y) + block_base(bundle_hole_options(), 0, individual_base_size_mm); + + copy_mirror([0, 1, 0]) { + copy_mirror([1, 0, 0]) { + translate([ + grid_size_mm.x/2 - HOLE_DISTANCE_FROM_BOTTOM_EDGE - BASE_PROFILE_MAX.x, + grid_size_mm.y/2 - HOLE_DISTANCE_FROM_BOTTOM_EDGE - BASE_PROFILE_MAX.x, + 0 + ]) + block_base_hole(hole_options, off); } } } - else { - pattern_linear(gx/dbnx, gy/dbny, dbnx*l, dbny*l) - block_base(gx, gy, l, dbnx, dbny, hole_options, off); - } + } + else { + pattern_linear(grid_size.x, grid_size.y, base_center_distance_mm.x, base_center_distance_mm.y) + block_base(hole_options, off, individual_base_size_mm); } } /** * @brief A single Gridfinity base. With holes (if set). - * @param gx - * @param gy - * @param l - * @param dbnx - * @param dbny * @param hole_options @see block_base_hole.hole_options * @param off + * @param size [x, y] size of a single base. Only set if deviating from the standard! */ -module block_base(gx, gy, l, dbnx, dbny, hole_options, off) { +module block_base(hole_options, off=0, size=[BASE_SIZE, BASE_SIZE]) { + assert(is_list(size) && len(size) == 2); + + // How far, in the +x direction, + // the profile 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 = BASE_OUTSIDE_RADIUS - BASE_PROFILE_MAX.x; + + outer_diameter = [2*BASE_OUTSIDE_RADIUS, 2*BASE_OUTSIDE_RADIUS]; + base_profile_size = size - outer_diameter; + base_bottom_size = base_profile_size + [2*translation_x, 2*translation_x]; + assert(base_profile_size.x > 0 && base_profile_size.y > 0, + str("Minimum size of a single base must be greater than ", outer_diameter) + ); + render(convexity = 2) difference() { - block_base_solid(dbnx, dbny, l, off); + union() { + sweep_rounded(base_profile_size.x, base_profile_size.y) + translate([translation_x, 0, 0]) + polygon(BASE_PROFILE); - pattern_circular(abs(l-d_hole_from_side/2)<0.001?1:4) - translate([l/2-d_hole_from_side, l/2-d_hole_from_side, 0]) - block_base_hole(hole_options, off); - } -} - -/** - * @brief A gridfinity base with no holes. - * @details Used as the "base" with holes removed from it later. - * @param dbnx - * @param dbny - * @param l - * @param o - */ -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); - rounded_rectangle(xx-2*r_c2+o, yy-2*r_c2+o, h_base-r_c1+oo, r_fo2); + rounded_square( + [ + base_bottom_size.x + TOLLERANCE, + base_bottom_size.y + TOLLERANCE, + BASE_PROFILE_MAX.y + ], + translation_x, + center=true + ); } - translate([0,0,oo]) - hull() { - rounded_rectangle(xx-2*r_c2+o, yy-2*r_c2+o, r_c2, r_fo2); - mirror([0,0,1]) - rounded_rectangle(xx+o, yy+o, h_bot/2+abs(10*o), r_fo1); + + // 4 holes + // Need this fancy code to support refined holes and non-square bases. + for(a=[0:90:270]){ + // i and j represent the 4 quadrants. + // The +1 is used to keep any values from being exactly 0. + j = sign(sin(a+1)); + i = sign(cos(a+1)); + translate([ + i * (base_bottom_size.x/2 - HOLE_DISTANCE_FROM_BOTTOM_EDGE), + j * (base_bottom_size.y/2 - HOLE_DISTANCE_FROM_BOTTOM_EDGE), + 0]) + rotate([0, 0, a]) + block_base_hole(hole_options, off); } } } diff --git a/gridfinity-spiral-vase.scad b/gridfinity-spiral-vase.scad index 22231f7..93ac20c 100644 --- a/gridfinity-spiral-vase.scad +++ b/gridfinity-spiral-vase.scad @@ -72,13 +72,20 @@ else gridfinityVase(); // Generate the bin // ===== CONSTRUCTION ===== // +//Deprecated Variables +r_fo2 = 3.2 / 2; // outside radii 2 +r_fo3 = 1.6 / 2; // outside radii 3 +r_c2 = 2.4; // upper base chamfer "radius" +d_hole = 26; // center-to-center distance between holes +//End Deprecated Variables + d_bottom = layer*(max(bottom_layer,1)); x_l = l_grid/2; dht = (gridz_define==0)?gridz*7 : (gridz_define==1)?h_bot+gridz+h_base : gridz-(enable_lip?3.8:0); d_height = (enable_zsnap?((abs(dht)%7==0)?dht:dht+7-abs(dht)%7):dht)-h_base; -d_fo1 = 2*r_fo1; +d_fo1 = 2*+BASE_OUTSIDE_RADIUS; f2c = sqrt(2)*(sqrt(2)-1); // fillet to chamfer ratio me = ((gridx*l_grid-0.5)/n_divx)-nozzle*4-d_fo1-12.7-4; @@ -161,7 +168,7 @@ module gridfinityBaseVase() { intersection() { block_base_blank(0); translate([0,0,-h_base-1]) - rounded_square([l_grid-0.5-0.005, l_grid-0.5-0.005, h_base*10], r_fo1+0.001, center=true); + rounded_square([l_grid-0.5-0.005, l_grid-0.5-0.005, h_base*10], BASE_OUTSIDE_RADIUS+0.001, center=true); } translate([0,0,0.01]) difference() { @@ -229,7 +236,7 @@ module block_base_blank(o = 0) { 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, center=true); + rounded_square(l_grid-o-0.05, BASE_OUTSIDE_RADIUS, center=true); } } } diff --git a/standard.scad b/standard.scad index f80f2c8..19289c8 100644 --- a/standard.scad +++ b/standard.scad @@ -1,25 +1,15 @@ -// height of the base -h_base = 5; // 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 = 7.5 / 2; -// outside radii 2 -r_fo2 = 3.2 / 2; -// outside radii 3 -r_fo3 = 1.6 / 2; // length of a grid unit l_grid = 42; // Outside rounded radius of bin // Per spec, matches radius of upper base section. -r_base = r_fo1; +r_base = 7.5 / 2; // Tollerance to make sure cuts don't leave a sliver behind, // and that items are properly connected to each other. @@ -35,11 +25,12 @@ SCREW_HOLE_RADIUS = 3 / 2; MAGNET_HOLE_RADIUS = 6.5 / 2; MAGNET_HOLE_DEPTH = MAGNET_HEIGHT + (LAYER_HEIGHT * 2); -// center-to-center distance between holes -d_hole = 26; // distance of hole from side of bin d_hole_from_side=8; +// Based on https://gridfinity.xyz/specification/ +HOLE_DISTANCE_FROM_BOTTOM_EDGE = 4.8; + // Meassured diameter in Fusion360. // Smaller than the magnet to keep it squeezed. REFINED_HOLE_RADIUS = 5.86 / 2; @@ -62,6 +53,7 @@ CHAMFER_ANGLE = 45; BASEPLATE_SCREW_COUNTERSINK_ADDITIONAL_RADIUS = 5/2; BASEPLATE_SCREW_COUNTERBORE_RADIUS = 5.5/2; BASEPLATE_SCREW_COUNTERBORE_HEIGHT = 3; + // **************************************** // top edge fillet radius @@ -109,6 +101,34 @@ stacking_lip_support_wall_height_mm = 1.2; stacking_lip_support_height_mm = stacking_lip_support_wall_height_mm + d_wall2; +// **************************************** +// Base constants +// Based on https://gridfinity.xyz/specification/ +// **************************************** +BASE_OUTSIDE_RADIUS = r_base; + +BASE_PROFILE = [ + [0, 0], // Innermost bottom point + [0.8, 0.8], // Up and out at a 45 degree angle + [0.8, (0.8+1.8)], // Straight up + [(0.8+2.15), (0.8+1.8+2.15)], // Up and out at a 45 degree angle + [0, (0.8+1.8+2.15)], // Go in to form a solid polygon + [0, 0] //Back to start +]; + +// Maximum [x,y] values/size of the base. +BASE_PROFILE_MAX = BASE_PROFILE[3]; + +// Each unit's base is 41.5mm x 41.5mm +// Leaving 0.5mm gap with an l_grid of 42 +BASE_SIZE = 41.5; + + +/** + * @summary Height of the raw base + */ +h_base = BASE_PROFILE_MAX.y; + // **************************************** // Baseplate constants // Based on https://gridfinity.xyz/specification/ @@ -159,8 +179,6 @@ bp_rcut_depth = 2; // **************************************** -// Baseplate clearance offset -bp_xy_clearance = 0.5; // radius of cutout for skeletonized baseplate r_skel = 2; // minimum baseplate thickness (when skeletonized)