From b4061d45d923c935ee7a14b39e7cb9141f317caf Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sun, 28 Apr 2024 13:07:12 -0400 Subject: [PATCH 1/4] Caculate stacking lip support based on an angle Removes the need for a magic constant. --- standard.scad | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/standard.scad b/standard.scad index 19289c8..1a7b772 100644 --- a/standard.scad +++ b/standard.scad @@ -80,7 +80,6 @@ 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 Constants // Based on https://gridfinity.xyz/specification/ @@ -96,10 +95,19 @@ stacking_lip_height = stacking_lip_wall_height_mm + stacking_lip_outer_slope_height_mm; -// Extracted from `profile_wall_sub_sub`. +// Height of the innermost section. +// Used to keep the innermost lip from just being a triangle. +// Spec implicitly expects wall width to equal stacking lip depth, +// so does not define this. stacking_lip_support_wall_height_mm = 1.2; + +// Support so the stacking lip is not floating in mid air +// when wall width is less than stacking lip depth. +stacking_lip_support_angle = 45; + stacking_lip_support_height_mm = - stacking_lip_support_wall_height_mm + d_wall2; + stacking_lip_support_wall_height_mm + + tan(90 - stacking_lip_support_angle) * stacking_lip_depth; // **************************************** // Base constants From a0f7ad83bd29b893cf601792f2d06cff459b21a6 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sun, 28 Apr 2024 13:25:07 -0400 Subject: [PATCH 2/4] Add an image explaining the variables for stacking lip --- images/stacking_lip_variables.jpg | Bin 0 -> 43052 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/stacking_lip_variables.jpg diff --git a/images/stacking_lip_variables.jpg b/images/stacking_lip_variables.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e9296b2e859ba74cfa2bddc8649aa3efb09dd8e6 GIT binary patch literal 43052 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 Date: Mon, 29 Apr 2024 20:56:56 -0400 Subject: [PATCH 3/4] Add an affine_scale command --- generic-helpers.scad | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/generic-helpers.scad b/generic-helpers.scad index fbab526..d8e3a25 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -161,6 +161,18 @@ function affine_translate(vector) = [ [0, 0, 0, 1] ]; +/** + * @brief Affine transformation matrix equivalent of `scale` + * @param vector @see `scale` + * @returns An affine transformation matrix for use with `multmatrix()` + */ +function affine_scale(vector) = [ + [vector.x, 0, 0, 0], + [0, vector.y, 0, 0], + [0, 0, vector.z, 0], + [0, 0, 0, 1] +]; + /** * @brief Create a rectangle with rounded corners by sweeping a 2d object along a path. * Centered on origin. From c34de9bff0515f7592cca3a5009c071039029ace Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Mon, 2 Sep 2024 01:39:13 -0400 Subject: [PATCH 4/4] Switch stacking lip constants to the same format as baseplate constants This increases readability and usability, while also being more compact. Also updated documentation in gridfinity-rebuilt-bins to explain how heights are calculated. --- docs/constants.md | 2 +- gridfinity-rebuilt-bins.scad | 18 ++++--- gridfinity-rebuilt-utility.scad | 91 ++++++++++++++++++--------------- standard.scad | 73 +++++++++++++++++--------- 4 files changed, 109 insertions(+), 75 deletions(-) diff --git a/docs/constants.md b/docs/constants.md index 3c69989..c00a90c 100644 --- a/docs/constants.md +++ b/docs/constants.md @@ -17,7 +17,7 @@ r_hole2| magnet hole radius d_hole| center-to-center distance between holes h_hole| magnet hole depth h_slit| slit depth (printer layer height) -r_f1| top edge fillet radius +STACKING_LIP_FILLET_RADIUS| top edge fillet radius r_f2 | internal fillet radius d_div | width of divider between compartments d_wall| minimum wall thickness diff --git a/gridfinity-rebuilt-bins.scad b/gridfinity-rebuilt-bins.scad index 3b36dba..32740b6 100644 --- a/gridfinity-rebuilt-bins.scad +++ b/gridfinity-rebuilt-bins.scad @@ -9,14 +9,18 @@ include comments like ' //.5' after variables are intentional and used by the customizer 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 + #BIN HEIGHT + The original gridfinity bins had the overall height defined by 7mm increments. + A bin would be 7*u millimeters tall with a stacking lip at the top of the bin (4.4mm) 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 + * Z unit 2 -> 7*2 + 4.4 -> 18.4mm + * Z unit 3 -> 7*3 + 4.4 -> 25.4mm + * Z unit 6 -> 7*6 + 4.4 -> 46.4mm + + ## Note: + The stacking lip provided here has a 0.6mm fillet instead of coming to a sharp point. + Which has a height of 3.55147mm instead of the specified 4.4mm. + This **has no impact on stacking height, and can be ignored.** https://github.com/kennetek/gridfinity-rebuilt-openscad diff --git a/gridfinity-rebuilt-utility.scad b/gridfinity-rebuilt-utility.scad index 9130e30..1436a1e 100644 --- a/gridfinity-rebuilt-utility.scad +++ b/gridfinity-rebuilt-utility.scad @@ -20,7 +20,7 @@ use * @returns The final value in mm. */ function fromGridfinityUnits(gridfinityUnit, includeLipHeight = false) = - gridfinityUnit*7 + (includeLipHeight ? h_lip : 0); + gridfinityUnit*7 + (includeLipHeight ? STACKING_LIP_SIZE.y : 0); /** * @Summary Height in mm including fixed heights. @@ -30,7 +30,7 @@ function fromGridfinityUnits(gridfinityUnit, includeLipHeight = false) = * @returns The final value in mm. */ function includingFixedHeights(mmHeight, includeLipHeight = false) = - mmHeight + h_bot + h_base + (includeLipHeight ? h_lip : 0); + mmHeight + h_bot + h_base + (includeLipHeight ? STACKING_LIP_SIZE.y : 0); /** * @brief Three Functions in One. For height calculations. @@ -42,14 +42,13 @@ function includingFixedHeights(mmHeight, includeLipHeight = false) = function hf (z, gridz_define, style_lip) = gridz_define==0 ? fromGridfinityUnits(z, style_lip==2) : gridz_define==1 ? includingFixedHeights(z, style_lip==2) : - z + ( // Just use z (possibly adding/subtracting lip) - style_lip==1 ? -h_lip : - style_lip==2 ? h_lip : 0 - ) + gridz_define==2 ? z + (style_lip==2 ? STACKING_LIP_SIZE.y : 0) : + assert(false, "gridz_define must be 0, 1, or 2.") ; /** * @brief Calculates the proper height for bins. Three Functions in One. + * @Details Critically, this does not include the baseplate height. * @param z Height value * @param d gridz_define as explained in gridfinity-rebuilt-bins.scad * @param l style_lip as explained in gridfinity-rebuilt-bins.scad @@ -324,47 +323,55 @@ module block_base(hole_options, off=0, size=[BASE_SIZE, BASE_SIZE]) { * @details Also includes a support base. */ module stacking_lip() { - // Technique: Descriptive constant names are useful, but can be unweildy. - // Use abbreviations if they are going to be re-used repeatedly in a small piece of code. - inner_slope = stacking_lip_inner_slope_height_mm; - wall_height = stacking_lip_wall_height_mm; - - support_wall = stacking_lip_support_wall_height_mm; - s_total = stacking_lip_support_height_mm; - - polygon([ - [0, 0], // Inner tip - [inner_slope, inner_slope], // Go out 45 degrees - [inner_slope, inner_slope+wall_height], // Vertical increase - [stacking_lip_depth, stacking_lip_height], // Go out 45 degrees - [stacking_lip_depth, -s_total], // Down to support bottom - [0, -support_wall], // Up and in - [0, 0] // Close the shape. Tehcnically not needed. - ]); + polygon(STACKING_LIP); } /** - * @brief Stacking lip with a with a chamfered (rounded) top. + * @brief Stacking lip with a with a filleted (rounded) top. * @details Based on https://gridfinity.xyz/specification/ * Also includes a support base. */ -module stacking_lip_chamfered() { - radius_center_y = h_lip - r_f1; +module stacking_lip_filleted() { + // Replace 2D edge with a radius. + // Method used: tangent, tangent, radius algorithm + // See: https://math.stackexchange.com/questions/797828/calculate-center-of-circle-tangent-to-two-lines-in-space + before_fillet = STACKING_LIP[2]; + to_fillet = STACKING_LIP[3]; // tip, Point to Chamfer + after_fillet = STACKING_LIP[4]; + + fillet_vectors = [ + to_fillet - before_fillet, + after_fillet - to_fillet, + ]; + + to_fillet_angle = 180 + atan2( + cross(fillet_vectors[0], fillet_vectors[1]), + fillet_vectors[0] * fillet_vectors[1] + ); + half_angle = to_fillet_angle / 2; + + // Distance from tip to the center point of the circle. + distance_from_edge = STACKING_LIP_FILLET_RADIUS / sin(half_angle); + + // Circle's center point + fillet_center_vector = distance_from_edge * [sin(half_angle), cos(half_angle)]; + fillet_center_point = to_fillet - fillet_center_vector; + + // Exact point edges intersect the circle + intersection_distance = fillet_center_vector.y; + +// echo(final_lip_height=fillet_center_point.y + STACKING_LIP_FILLET_RADIUS); union() { - // Create rounded top - intersection() { - translate([0, radius_center_y, 0]) - square([stacking_lip_depth, stacking_lip_height]); - offset(r = r_f1) - offset(delta = -r_f1) - stacking_lip(); - } - // Remove pointed top + // Rounded top + translate(concat(fillet_center_point, [0])) + circle(r = STACKING_LIP_FILLET_RADIUS); + + // Stacking lip with cutout for circle to fit in difference(){ - stacking_lip(); - translate([0, radius_center_y, 0]) - square([stacking_lip_depth*2, stacking_lip_height*2]); + polygon(STACKING_LIP); + translate(concat(to_fillet, [0])) + circle(r = intersection_distance); } } } @@ -375,10 +382,10 @@ module stacking_lip_chamfered() { */ module profile_wall(height_mm) { assert(is_num(height_mm)) - translate([r_base - stacking_lip_depth, 0, 0]){ + translate([r_base - STACKING_LIP_SIZE.x, 0, 0]){ translate([0, height_mm, 0]) - stacking_lip_chamfered(); - translate([stacking_lip_depth-d_wall/2, 0, 0]) + stacking_lip_filleted(); + translate([STACKING_LIP_SIZE.x-d_wall/2, 0, 0]) square([d_wall/2, height_mm]); } } @@ -415,7 +422,7 @@ module block_cutter(x,y,w,h,t,s,tab_width=d_tabw,tab_height=d_tabh) { v_len_tab = tab_height; v_len_lip = d_wall2-d_wall+1.2; - v_cut_tab = tab_height - (2*r_f1)/tan(a_tab); + v_cut_tab = tab_height - (2*STACKING_LIP_FILLET_RADIUS)/tan(a_tab); v_cut_lip = d_wall2-d_wall-d_clear; v_ang_tab = a_tab; v_ang_lip = 45; diff --git a/standard.scad b/standard.scad index 1a7b772..c294098 100644 --- a/standard.scad +++ b/standard.scad @@ -56,8 +56,6 @@ BASEPLATE_SCREW_COUNTERBORE_HEIGHT = 3; // **************************************** -// top edge fillet radius -r_f1 = 0.6; // internal fillet radius r_f2 = 2.8; @@ -74,8 +72,6 @@ d_tabh = 15.85; d_tabw = 42; // angle of tab a_tab = 36; -// lip height -h_lip = 3.548; d_wall2 = r_base-r_c1-d_clear*sqrt(2); d_magic = -2*d_clear-2*d_wall+d_div; @@ -83,31 +79,58 @@ d_magic = -2*d_clear-2*d_wall+d_div; // **************************************** // Stacking Lip Constants // Based on https://gridfinity.xyz/specification/ +// Also includes a support base. // **************************************** -stacking_lip_inner_slope_height_mm = 0.7; -stacking_lip_wall_height_mm = 1.8; -stacking_lip_outer_slope_height_mm = 1.9; -stacking_lip_depth = - stacking_lip_inner_slope_height_mm + - stacking_lip_outer_slope_height_mm; -stacking_lip_height = - stacking_lip_inner_slope_height_mm + - stacking_lip_wall_height_mm + - stacking_lip_outer_slope_height_mm; -// Height of the innermost section. -// Used to keep the innermost lip from just being a triangle. -// Spec implicitly expects wall width to equal stacking lip depth, -// so does not define this. -stacking_lip_support_wall_height_mm = 1.2; +/** + * @Summary Fillet so the stacking lip does not come to a sharp point. + */ +STACKING_LIP_FILLET_RADIUS = 0.6; -// Support so the stacking lip is not floating in mid air -// when wall width is less than stacking lip depth. -stacking_lip_support_angle = 45; +/** + * @Summary Height of the innermost section. In mm. + * @Details Used to keep the innermost lip from just being a triangle. + * Spec implicitly expects wall width to equal stacking lip depth, so does not define this. + */ +STACKING_LIP_SUPPORT_HEIGHT = 1.2; -stacking_lip_support_height_mm = - stacking_lip_support_wall_height_mm - + tan(90 - stacking_lip_support_angle) * stacking_lip_depth; +/** + * @Summary Stacking lip as defined in the spec. No support. + */ +RAW_STACKING_LIP = [ + [0, 0], // Inner tip + [0.7, 0.7], // Go out 45 degrees + [0.7, (0.7+1.8)], // Vertical increase + [(0.7+1.9), (0.7+1.8+1.9)], // Go out 45 degrees +]; + +/** + * @Summary Size of the stacking lip. + * @Details "x": How deep the stacking lip protrudes into the bin. + * Including wall thickness. + * "y": The height of the stacking lip. + */ +STACKING_LIP_SIZE = RAW_STACKING_LIP[3]; + +_stacking_lip_support_angle = 45; + +/** + * @Summary Calculated value for the overall height of the stacking lip. + * Including support. + */ +_stacking_lip_support_height_mm = + STACKING_LIP_SUPPORT_HEIGHT + + tan(90 - _stacking_lip_support_angle) * STACKING_LIP_SIZE.x; + +/** + * @Summary Stacking lip with a support. + * @Details Support is so the stacking lip is not floating in mid air when wall width is less than stacking lip depth. + */ +STACKING_LIP = concat(RAW_STACKING_LIP, [ + [STACKING_LIP_SIZE.x, -_stacking_lip_support_height_mm], // Down to support bottom + [0, -STACKING_LIP_SUPPORT_HEIGHT], // Up and in (to bottom inner support) + [0, 0] // Close the shape. Technically not needed. +]); // **************************************** // Base constants