From 1cf350121dcf1f69ff304aae7eeb161624ddff21 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sat, 24 Feb 2024 18:04:18 -0500 Subject: [PATCH 1/5] Use Affine transformation matrices for creating walls. This involves more math, but results in a cleaner abstract syntax tree. Which also means it is cleaner when importing to something like FreeCAD. --- gridfinity-rebuilt-utility.scad | 141 ++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 15 deletions(-) diff --git a/gridfinity-rebuilt-utility.scad b/gridfinity-rebuilt-utility.scad index a39dd1f..333dbda 100644 --- a/gridfinity-rebuilt-utility.scad +++ b/gridfinity-rebuilt-utility.scad @@ -659,22 +659,133 @@ module pattern_circular(n=2) { 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(); +/** + * @brief Unity (no change) affine transformation matrix. + * @details For use with multmatrix transforms. + */ +unity_matrix = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] +]; - translate([w/2,0,0]) - rotate([90,0,0]) - linear_extrude(height = h, center = true) - children(); +/** + * @brief Get the magnitude of a 2d or 3d vector + * @param vector A 2d or 3d vectorm + * @returns Magnitude of the vector. + */ + function vector_magnitude(vector) = + sqrt(vector.x^2 + vector.y^2 + (len(vector) == 3 ? vector.z^2 : 0)); - rotate([0,0,90]) - translate([h/2,0,0]) - rotate([90,0,0]) - linear_extrude(height = w, center = true) - children(); +/** + * @brief Convert a 2d or 3d vector into a unit vector + * @returns The unit vector. Where total magnitude is 1. + */ +function vector_as_unit(vector) = vector / vector_magnitude(vector); + +/** + * @brief Convert a 2d vector into an angle. + * @details Just a wrapper around atan2. + * @param A 2d vectorm + * @returns Angle of the vector. + */ +function atanv(vector) = atan2(vector.y, vector.x); + +/** + * @brief Affine transformation matrix for 2d rotation on the X,Y plane. + * @param angle The angle to rotate things by + * @returns an Affine transformation matrix for use with `multmatrix()` + */ +function affine_rotation(angle) = [ + [cos(angle), 0, sin(angle), 0], + [0, 1, 0, 0], + [-sin(angle), 0, cos(angle), 0], + [0, 0, 0, 1] +]; + + +/** + * @brief Affine transformation matrix for 2d translation on the X,Y plane. + * @param vector 2d Vector to translate by. + * @returns an Affine transformation matrix for use with `multmatrix()` + */ +function affine_translation(vector) = [ + [1, 0, 0, vector.y], + [0, 1, 0, 0], + [0, 0, 1, vector.x], + [0, 0, 0, 1] +]; + +/** + * @brief Create a rectangle with rounded corners by sweeping a 2d object along a path. + * Centered on origin. + */ +module sweep_rounded(width=10, length=10) { + half_width = width/2; + half_length = length/2; + path_points = [ + [-half_width, half_length], //Start + [half_width, half_length], // Over + [half_width, -half_length], //Down + [-half_width, -half_length], // Back over + [-half_width, half_length] // Up to start + ]; + path_vectors = [ + path_points[1] - path_points[0], + path_points[2] - path_points[1], + path_points[3] - path_points[2], + path_points[4] - path_points[3], + ]; + // These contain the translations, but not the rotations + // OpenSCAD requires this hacky for loop to get accumulate to work! + first_translation = affine_translation(path_points[0]); + affine_translations = concat([first_translation], [ + for (i = 0, a = first_translation; + i < len(path_vectors); + a=a * affine_translation(path_vectors[i]), i=i+1) + a * affine_translation(path_vectors[i]) + ]); + + // Affine matrix to rotate around X axis + rot_x = 90; + x_matrix = [ + [1, 0, 0, 0], + [0, cos(rot_x), -sin(rot_x), 0], + [0, sin(rot_x), cos(rot_x), 0], + [0, 0, 0, 1] + ]; + + // Affine matrix to rotate around Z axis + z_rot = 90; + z_matrix = [ + [cos(z_rot), -sin(z_rot), 0, 0], + [sin(z_rot), cos(z_rot), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]; + + // Bring extrusion to the xy plane + affine_matrix = z_matrix * x_matrix; + + walls = [ + for (i = [0 : len(path_vectors) - 1]) + affine_matrix * affine_translations[i] + * affine_rotation(atanv(path_vectors[i])) + ]; + + union() + { + for (i = [0 : len(walls) - 1]){ + multmatrix(walls[i]) + linear_extrude(vector_magnitude(path_vectors[i])) + children(); + + // Rounded Corners + multmatrix(walls[i] + *x_matrix*x_matrix*x_matrix *z_matrix*z_matrix*z_matrix*z_matrix) + rotate_extrude(angle = 90, convexity = 4) + children(); + } } } From 574d9dc6b1edc7c46f76a0e141a72edf0faa2637 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Sat, 9 Mar 2024 23:35:04 -0500 Subject: [PATCH 2/5] Move helper functions / modules to their own file These are not explicitly tied to Gridfinity. --- generic-helpers.scad | 170 ++++++++++++++++++++++++++++++++ gridfinity-rebuilt-utility.scad | 169 +------------------------------ 2 files changed, 171 insertions(+), 168 deletions(-) create mode 100644 generic-helpers.scad diff --git a/generic-helpers.scad b/generic-helpers.scad new file mode 100644 index 0000000..b40c9d8 --- /dev/null +++ b/generic-helpers.scad @@ -0,0 +1,170 @@ +/** + * @file generic-helpers.scad + * @brief Generic Helper Functions. Not gridfinity specific. + */ + +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(); +} + +/** + * @brief Unity (no change) affine transformation matrix. + * @details For use with multmatrix transforms. + */ +unity_matrix = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] +]; + +/** + * @brief Get the magnitude of a 2d or 3d vector + * @param vector A 2d or 3d vectorm + * @returns Magnitude of the vector. + */ + function vector_magnitude(vector) = + sqrt(vector.x^2 + vector.y^2 + (len(vector) == 3 ? vector.z^2 : 0)); + +/** + * @brief Convert a 2d or 3d vector into a unit vector + * @returns The unit vector. Where total magnitude is 1. + */ +function vector_as_unit(vector) = vector / vector_magnitude(vector); + +/** + * @brief Convert a 2d vector into an angle. + * @details Just a wrapper around atan2. + * @param A 2d vectorm + * @returns Angle of the vector. + */ +function atanv(vector) = atan2(vector.y, vector.x); + +/** + * @brief Affine transformation matrix for 2d rotation on the X,Y plane. + * @param angle The angle to rotate things by + * @returns an Affine transformation matrix for use with `multmatrix()` + */ +function affine_rotation(angle) = [ + [cos(angle), 0, sin(angle), 0], + [0, 1, 0, 0], + [-sin(angle), 0, cos(angle), 0], + [0, 0, 0, 1] +]; + + +/** + * @brief Affine transformation matrix for 2d translation on the X,Y plane. + * @param vector 2d Vector to translate by. + * @returns an Affine transformation matrix for use with `multmatrix()` + */ +function affine_translation(vector) = [ + [1, 0, 0, vector.y], + [0, 1, 0, 0], + [0, 0, 1, vector.x], + [0, 0, 0, 1] +]; + +/** + * @brief Create a rectangle with rounded corners by sweeping a 2d object along a path. + * Centered on origin. + */ +module sweep_rounded(width=10, length=10) { + half_width = width/2; + half_length = length/2; + path_points = [ + [-half_width, half_length], //Start + [half_width, half_length], // Over + [half_width, -half_length], //Down + [-half_width, -half_length], // Back over + [-half_width, half_length] // Up to start + ]; + path_vectors = [ + path_points[1] - path_points[0], + path_points[2] - path_points[1], + path_points[3] - path_points[2], + path_points[4] - path_points[3], + ]; + // These contain the translations, but not the rotations + // OpenSCAD requires this hacky for loop to get accumulate to work! + first_translation = affine_translation(path_points[0]); + affine_translations = concat([first_translation], [ + for (i = 0, a = first_translation; + i < len(path_vectors); + a=a * affine_translation(path_vectors[i]), i=i+1) + a * affine_translation(path_vectors[i]) + ]); + + // Affine matrix to rotate around X axis + rot_x = 90; + x_matrix = [ + [1, 0, 0, 0], + [0, cos(rot_x), -sin(rot_x), 0], + [0, sin(rot_x), cos(rot_x), 0], + [0, 0, 0, 1] + ]; + + // Affine matrix to rotate around Z axis + z_rot = 90; + z_matrix = [ + [cos(z_rot), -sin(z_rot), 0, 0], + [sin(z_rot), cos(z_rot), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]; + + // Bring extrusion to the xy plane + affine_matrix = z_matrix * x_matrix; + + walls = [ + for (i = [0 : len(path_vectors) - 1]) + affine_matrix * affine_translations[i] + * affine_rotation(atanv(path_vectors[i])) + ]; + + union() + { + for (i = [0 : len(walls) - 1]){ + multmatrix(walls[i]) + linear_extrude(vector_magnitude(path_vectors[i])) + children(); + + // Rounded Corners + multmatrix(walls[i] + *x_matrix*x_matrix*x_matrix *z_matrix*z_matrix*z_matrix*z_matrix) + rotate_extrude(angle = 90, convexity = 4) + children(); + } + } +} diff --git a/gridfinity-rebuilt-utility.scad b/gridfinity-rebuilt-utility.scad index 333dbda..3fa930f 100644 --- a/gridfinity-rebuilt-utility.scad +++ b/gridfinity-rebuilt-utility.scad @@ -5,6 +5,7 @@ */ include +use // ===== User Modules ===== // @@ -621,171 +622,3 @@ module profile_cutter_tab(h, tab, ang) { 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(); -} - -/** - * @brief Unity (no change) affine transformation matrix. - * @details For use with multmatrix transforms. - */ -unity_matrix = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] -]; - -/** - * @brief Get the magnitude of a 2d or 3d vector - * @param vector A 2d or 3d vectorm - * @returns Magnitude of the vector. - */ - function vector_magnitude(vector) = - sqrt(vector.x^2 + vector.y^2 + (len(vector) == 3 ? vector.z^2 : 0)); - -/** - * @brief Convert a 2d or 3d vector into a unit vector - * @returns The unit vector. Where total magnitude is 1. - */ -function vector_as_unit(vector) = vector / vector_magnitude(vector); - -/** - * @brief Convert a 2d vector into an angle. - * @details Just a wrapper around atan2. - * @param A 2d vectorm - * @returns Angle of the vector. - */ -function atanv(vector) = atan2(vector.y, vector.x); - -/** - * @brief Affine transformation matrix for 2d rotation on the X,Y plane. - * @param angle The angle to rotate things by - * @returns an Affine transformation matrix for use with `multmatrix()` - */ -function affine_rotation(angle) = [ - [cos(angle), 0, sin(angle), 0], - [0, 1, 0, 0], - [-sin(angle), 0, cos(angle), 0], - [0, 0, 0, 1] -]; - - -/** - * @brief Affine transformation matrix for 2d translation on the X,Y plane. - * @param vector 2d Vector to translate by. - * @returns an Affine transformation matrix for use with `multmatrix()` - */ -function affine_translation(vector) = [ - [1, 0, 0, vector.y], - [0, 1, 0, 0], - [0, 0, 1, vector.x], - [0, 0, 0, 1] -]; - -/** - * @brief Create a rectangle with rounded corners by sweeping a 2d object along a path. - * Centered on origin. - */ -module sweep_rounded(width=10, length=10) { - half_width = width/2; - half_length = length/2; - path_points = [ - [-half_width, half_length], //Start - [half_width, half_length], // Over - [half_width, -half_length], //Down - [-half_width, -half_length], // Back over - [-half_width, half_length] // Up to start - ]; - path_vectors = [ - path_points[1] - path_points[0], - path_points[2] - path_points[1], - path_points[3] - path_points[2], - path_points[4] - path_points[3], - ]; - // These contain the translations, but not the rotations - // OpenSCAD requires this hacky for loop to get accumulate to work! - first_translation = affine_translation(path_points[0]); - affine_translations = concat([first_translation], [ - for (i = 0, a = first_translation; - i < len(path_vectors); - a=a * affine_translation(path_vectors[i]), i=i+1) - a * affine_translation(path_vectors[i]) - ]); - - // Affine matrix to rotate around X axis - rot_x = 90; - x_matrix = [ - [1, 0, 0, 0], - [0, cos(rot_x), -sin(rot_x), 0], - [0, sin(rot_x), cos(rot_x), 0], - [0, 0, 0, 1] - ]; - - // Affine matrix to rotate around Z axis - z_rot = 90; - z_matrix = [ - [cos(z_rot), -sin(z_rot), 0, 0], - [sin(z_rot), cos(z_rot), 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - // Bring extrusion to the xy plane - affine_matrix = z_matrix * x_matrix; - - walls = [ - for (i = [0 : len(path_vectors) - 1]) - affine_matrix * affine_translations[i] - * affine_rotation(atanv(path_vectors[i])) - ]; - - union() - { - for (i = [0 : len(walls) - 1]){ - multmatrix(walls[i]) - linear_extrude(vector_magnitude(path_vectors[i])) - children(); - - // Rounded Corners - multmatrix(walls[i] - *x_matrix*x_matrix*x_matrix *z_matrix*z_matrix*z_matrix*z_matrix) - rotate_extrude(angle = 90, convexity = 4) - children(); - } - } -} From 20492634d87e5de4e56ebd5a0d40a7ee3ba08a4b Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Wed, 17 Apr 2024 21:51:37 -0400 Subject: [PATCH 3/5] Update affine_rotate to support all axes. --- generic-helpers.scad | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index b40c9d8..672c8c6 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -71,18 +71,36 @@ function vector_as_unit(vector) = vector / vector_magnitude(vector); */ function atanv(vector) = atan2(vector.y, vector.x); -/** - * @brief Affine transformation matrix for 2d rotation on the X,Y plane. - * @param angle The angle to rotate things by - * @returns an Affine transformation matrix for use with `multmatrix()` - */ -function affine_rotation(angle) = [ - [cos(angle), 0, sin(angle), 0], - [0, 1, 0, 0], - [-sin(angle), 0, cos(angle), 0], +function _affine_rotate_x(angle_x) = [ + [1, 0, 0, 0], + [0, cos(angle_x), -sin(angle_x), 0], + [0, sin(angle_x), cos(angle_x), 0], [0, 0, 0, 1] ]; +function _affine_rotate_y(angle_y) = [ + [cos(angle_y), 0, sin(angle_y), 0], + [0, 1, 0, 0], + [-sin(angle_y), 0, cos(angle_y), 0], + [0, 0, 0, 1] +]; + +function _affine_rotate_z(angle_z) = [ + [cos(angle_z), -sin(angle_z), 0, 0], + [sin(angle_z), cos(angle_z), 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] +]; + + +/** + * @brief Affine transformation matrix equivalent of `rotate` + * @param angle_vector @see `rotate` + * @details Equivalent to `rotate([0, angle, 0])` + * @returns An affine transformation matrix for use with `multmatrix()` + */ +function affine_rotate(angle_vector) = + _affine_rotate_z(angle_vector.z) * _affine_rotate_y(angle_vector.y) * _affine_rotate_x(angle_vector.x); /** * @brief Affine transformation matrix for 2d translation on the X,Y plane. @@ -150,7 +168,7 @@ module sweep_rounded(width=10, length=10) { walls = [ for (i = [0 : len(path_vectors) - 1]) affine_matrix * affine_translations[i] - * affine_rotation(atanv(path_vectors[i])) + * affine_rotate([0, atanv(path_vectors[i]), 0]) ]; union() From ff3a325b379fc993beac8074819ca22914eeac69 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Wed, 17 Apr 2024 22:09:04 -0400 Subject: [PATCH 4/5] Update affine_translate to support all axes. --- generic-helpers.scad | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index 672c8c6..63fcd46 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -103,14 +103,14 @@ function affine_rotate(angle_vector) = _affine_rotate_z(angle_vector.z) * _affine_rotate_y(angle_vector.y) * _affine_rotate_x(angle_vector.x); /** - * @brief Affine transformation matrix for 2d translation on the X,Y plane. - * @param vector 2d Vector to translate by. - * @returns an Affine transformation matrix for use with `multmatrix()` + * @brief Affine transformation matrix equivalent of `translate` + * @param vector @see `translate` + * @returns An affine transformation matrix for use with `multmatrix()` */ -function affine_translation(vector) = [ - [1, 0, 0, vector.y], - [0, 1, 0, 0], - [0, 0, 1, vector.x], +function affine_translate(vector) = [ + [1, 0, 0, vector.x], + [0, 1, 0, vector.y], + [0, 0, 1, vector.z], [0, 0, 0, 1] ]; @@ -136,12 +136,12 @@ module sweep_rounded(width=10, length=10) { ]; // These contain the translations, but not the rotations // OpenSCAD requires this hacky for loop to get accumulate to work! - first_translation = affine_translation(path_points[0]); + first_translation = affine_translate([path_points[0].y, 0,path_points[0].x]); affine_translations = concat([first_translation], [ for (i = 0, a = first_translation; i < len(path_vectors); - a=a * affine_translation(path_vectors[i]), i=i+1) - a * affine_translation(path_vectors[i]) + a=a * affine_translate([path_vectors[i].y, 0, path_vectors[i].x]), i=i+1) + a * affine_translate([path_vectors[i].y, 0, path_vectors[i].x]) ]); // Affine matrix to rotate around X axis From e7ef96bbcfcce7107321436f8b526ab2f762d581 Mon Sep 17 00:00:00 2001 From: Arthur Moore Date: Wed, 17 Apr 2024 22:32:32 -0400 Subject: [PATCH 5/5] Stop using as many hacks in sweep_rounded --- generic-helpers.scad | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/generic-helpers.scad b/generic-helpers.scad index 63fcd46..99a96dc 100644 --- a/generic-helpers.scad +++ b/generic-helpers.scad @@ -144,26 +144,8 @@ module sweep_rounded(width=10, length=10) { a * affine_translate([path_vectors[i].y, 0, path_vectors[i].x]) ]); - // Affine matrix to rotate around X axis - rot_x = 90; - x_matrix = [ - [1, 0, 0, 0], - [0, cos(rot_x), -sin(rot_x), 0], - [0, sin(rot_x), cos(rot_x), 0], - [0, 0, 0, 1] - ]; - - // Affine matrix to rotate around Z axis - z_rot = 90; - z_matrix = [ - [cos(z_rot), -sin(z_rot), 0, 0], - [sin(z_rot), cos(z_rot), 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - // Bring extrusion to the xy plane - affine_matrix = z_matrix * x_matrix; + affine_matrix = affine_rotate([90, 0, 90]); walls = [ for (i = [0 : len(path_vectors) - 1]) @@ -179,8 +161,7 @@ module sweep_rounded(width=10, length=10) { children(); // Rounded Corners - multmatrix(walls[i] - *x_matrix*x_matrix*x_matrix *z_matrix*z_matrix*z_matrix*z_matrix) + multmatrix(walls[i] * affine_rotate([-90, 0, 0])) rotate_extrude(angle = 90, convexity = 4) children(); }