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/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. 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/images/stacking_lip_variables.jpg b/images/stacking_lip_variables.jpg new file mode 100644 index 0000000..e9296b2 Binary files /dev/null and b/images/stacking_lip_variables.jpg differ diff --git a/standard.scad b/standard.scad index 19289c8..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,32 +72,65 @@ 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; - // **************************************** // 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; -// Extracted from `profile_wall_sub_sub`. -stacking_lip_support_wall_height_mm = 1.2; -stacking_lip_support_height_mm = - stacking_lip_support_wall_height_mm + d_wall2; +/** + * @Summary Fillet so the stacking lip does not come to a sharp point. + */ +STACKING_LIP_FILLET_RADIUS = 0.6; + +/** + * @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; + +/** + * @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