1/**
  2 * @file generic-helpers.scad
  3 * @brief Generic Helper Functions. Not gridfinity specific.
  4 */
  5
  6function clp(x,a,b) = min(max(x,a),b);
  7
  8module rounded_rectangle(length, width, height, rad) {
  9    linear_extrude(height)
 10    offset(rad)
 11    offset(-rad)
 12    square([length,width], center = true);
 13}
 14
 15module rounded_square(length, height, rad) {
 16    rounded_rectangle(length, length, height, rad);
 17}
 18
 19module copy_mirror(vec=[0,1,0]) {
 20    children();
 21    if (vec != [0,0,0])
 22    mirror(vec)
 23    children();
 24}
 25
 26module pattern_linear(x = 1, y = 1, sx = 0, sy = 0) {
 27    yy = sy <= 0 ? sx : sy;
 28    translate([-(x-1)*sx/2,-(y-1)*yy/2,0])
 29    for (i = [1:ceil(x)])
 30    for (j = [1:ceil(y)])
 31    translate([(i-1)*sx,(j-1)*yy,0])
 32    children();
 33}
 34
 35module pattern_circular(n=2) {
 36    for (i = [1:n])
 37    rotate(i*360/n)
 38    children();
 39}
 40
 41/**
 42 * @brief Unity (no change) affine transformation matrix.
 43 * @details For use with multmatrix transforms.
 44 */
 45unity_matrix = [
 46    [1, 0, 0, 0],
 47    [0, 1, 0, 0],
 48    [0, 0, 1, 0],
 49    [0, 0, 0, 1]
 50];
 51
 52/**
 53 * @brief Get the magnitude of a 2d or 3d vector
 54 * @param vector A 2d or 3d vectorm
 55 * @returns Magnitude of the vector.
 56 */
 57 function vector_magnitude(vector) =
 58    sqrt(vector.x^2 + vector.y^2 + (len(vector) == 3 ? vector.z^2 : 0));
 59
 60/**
 61 * @brief Convert a 2d or 3d vector into a unit vector
 62 * @returns The unit vector.  Where total magnitude is 1.
 63 */
 64function vector_as_unit(vector) = vector / vector_magnitude(vector);
 65
 66/**
 67 * @brief Convert a 2d vector into an angle.
 68 * @details Just a wrapper around atan2.
 69 * @param A 2d vectorm
 70 * @returns Angle of the vector.
 71 */
 72function atanv(vector) = atan2(vector.y, vector.x);
 73
 74function _affine_rotate_x(angle_x) = [
 75    [1,  0, 0, 0],
 76    [0, cos(angle_x), -sin(angle_x), 0],
 77    [0, sin(angle_x), cos(angle_x), 0],
 78    [0, 0, 0, 1]
 79];
 80
 81function _affine_rotate_y(angle_y) = [
 82    [cos(angle_y),  0, sin(angle_y), 0],
 83    [0, 1, 0, 0],
 84    [-sin(angle_y), 0, cos(angle_y), 0],
 85    [0, 0, 0, 1]
 86];
 87
 88function _affine_rotate_z(angle_z) = [
 89    [cos(angle_z), -sin(angle_z), 0, 0],
 90    [sin(angle_z), cos(angle_z), 0, 0],
 91    [0, 0, 1, 0],
 92    [0, 0, 0, 1]
 93];
 94
 95
 96/**
 97 * @brief Affine transformation matrix equivalent of `rotate`
 98 * @param angle_vector @see `rotate`
 99 * @details Equivalent to `rotate([0, angle, 0])`
100 * @returns An affine transformation matrix for use with `multmatrix()`
101 */
102function affine_rotate(angle_vector) =
103    _affine_rotate_z(angle_vector.z) * _affine_rotate_y(angle_vector.y) * _affine_rotate_x(angle_vector.x);
104
105/**
106 * @brief Affine transformation matrix equivalent of `translate`
107 * @param vector @see `translate`
108 * @returns An affine transformation matrix for use with `multmatrix()`
109 */
110function affine_translate(vector) = [
111    [1, 0, 0, vector.x],
112    [0, 1, 0, vector.y],
113    [0, 0, 1, vector.z],
114    [0, 0, 0, 1]
115];
116
117/**
118 * @brief Create a rectangle with rounded corners by sweeping a 2d object along a path.
119 *        Centered on origin.
120 */
121module sweep_rounded(width=10, length=10) {
122    half_width = width/2;
123    half_length = length/2;
124    path_points = [
125        [-half_width, half_length],  //Start
126        [half_width, half_length], // Over
127        [half_width, -half_length], //Down
128        [-half_width, -half_length], // Back over
129        [-half_width, half_length]  // Up to start
130    ];
131    path_vectors = [
132        path_points[1] - path_points[0],
133        path_points[2] - path_points[1],
134        path_points[3] - path_points[2],
135        path_points[4] - path_points[3],
136    ];
137    // These contain the translations, but not the rotations
138    // OpenSCAD requires this hacky for loop to get accumulate to work!
139    first_translation = affine_translate([path_points[0].y, 0,path_points[0].x]);
140    affine_translations = concat([first_translation], [
141        for (i = 0, a = first_translation;
142            i < len(path_vectors);
143            a=a * affine_translate([path_vectors[i].y, 0, path_vectors[i].x]), i=i+1)
144        a * affine_translate([path_vectors[i].y, 0, path_vectors[i].x])
145    ]);
146
147    // Bring extrusion to the xy plane
148    affine_matrix = affine_rotate([90, 0, 90]);
149
150    walls = [
151        for (i = [0 : len(path_vectors) - 1])
152        affine_matrix * affine_translations[i]
153        * affine_rotate([0, atanv(path_vectors[i]), 0])
154    ];
155
156    union()
157    {
158        for (i = [0 : len(walls) - 1]){
159            multmatrix(walls[i])
160            linear_extrude(vector_magnitude(path_vectors[i]))
161            children();
162
163            // Rounded Corners
164            multmatrix(walls[i] * affine_rotate([-90, 0, 0]))
165            rotate_extrude(angle = 90, convexity = 4)
166            children();
167        }
168    }
169}