/ assets / parametric-flake.scad
parametric-flake.scad
  1  unit = [1, 0];
  2  origin = [0, 0];
  3  
  4  
  5  /*
  6      Returns a 2D rotation matrix given an angle.
  7  */
  8  function rot2d(angle) = [
  9      [+cos(angle), -sin(angle)],
 10      [+sin(angle), +cos(angle)]
 11  ];
 12  
 13  
 14  /*
 15      Calculates the points for a hexagon.
 16      Used to reference off of for creating the lambda.
 17      Points are created starting from the right and moving counter-clockwise.
 18  
 19           2   1
 20  
 21        3         0
 22  
 23           4   5
 24  */
 25  function inner_hex_points(data) = [
 26      for (angle = [0 : 60 : 300]) 
 27      dict_get(data, "scale") * rot2d(angle) * unit
 28  ];
 29  
 30  
 31  
 32  /*
 33      Calculates the points for making a lambda.
 34  
 35      Returns two a list of two lists.
 36      The first list contains the offset for making a flake.
 37      The second list is used to form the lambda polygon.
 38  */
 39  function lambda_points(data) =
 40  let (
 41      hex_points = inner_hex_points(data),
 42      thickness = dict_get(data, "thickness") / 2,
 43      top_left = hex_points[2],
 44      bottom_left = hex_points[4],
 45      bottom_right = hex_points[5],
 46      v_0 = thickness * unit,
 47      v_p60 = thickness * rot2d(60) * unit,
 48      v_n60 = thickness * rot2d(-60) * unit,
 49      v_n90 = thickness * rot2d(-90) * unit,
 50      gap = dict_get(data, "gap") * rot2d(-60) * unit
 51  )
 52  [
 53      [
 54          top_left + v_p60,
 55      ],
 56      [
 57          top_left - v_p60 + gap,
 58          top_left + v_p60 + gap,
 59          bottom_right + v_0,
 60          bottom_right - v_0,
 61          sqrt(3) * v_n90,
 62          bottom_left + v_0,
 63          bottom_left,
 64          bottom_left - v_n60,
 65          -v_0
 66      ]
 67  ];
 68  
 69  
 70  /*
 71      Generates a single lambda at the origin.
 72  */
 73  module lambda(data) {
 74      new_data = update_params(data);
 75      linear_extrude(dict_get(new_data, "height"))
 76      polygon(lambda_points(new_data)[1]);
 77  }
 78  
 79  
 80  /*
 81      Generates a NixOS flake.
 82  */
 83  module flake(data) {
 84      new_data = update_params(data);
 85      colors = dict_get(data, "colors");
 86      echo(colors);
 87      for (idx = [0 : 5]) {
 88          color(colors[idx % len(colors)])
 89          rotate([0, 0, idx * 60]) 
 90          translate(-lambda_points(new_data)[0][0])
 91          translate([-dict_get(new_data, "scale"), 0, 0])
 92          linear_extrude(dict_get(new_data, "height"))
 93          polygon(lambda_points(new_data)[1]);
 94      }
 95  }
 96  
 97  
 98  /*
 99      Returns the value from a associative array/dictionary type structure given some key.
100  
101      Values can be of any type.
102      The dictionary must of be of the form:
103      [
104          [ "key0", <value0> ],
105          [ "key1", <value1> ],
106          ...
107      ]
108  */
109  function dict_get(dict, key) =
110    dict[search([key], dict)[0]][1];
111  
112  
113  /*
114      Updates the user parameters so the flake is unit size when `scale = 1`.
115  */
116  function update_params(data) = let
117      // factor was empirically found. It gives a flake that is circumscribed by the unit circle.
118      (factor = 2.25) [
119      [ "gap", 0.75 * dict_get(data, "scale") * dict_get(data, "gap") / factor ],
120      [ "height", dict_get(data, "height") ],
121      [ "scale", dict_get(data, "scale") / factor ],
122      [ "thickness", dict_get(data, "scale") * dict_get(data, "thickness") / factor ],
123  ];
124  
125  
126  /*
127      The parameters for generating a NixOS flake.
128  
129      `gap` - The gap between lambdas.
130      `height` - The z-height of the flake.
131      `scale` - The radial (x,y) size of the flake.
132      `thickness` - The thickness of the lambda legs.
133  
134      `scale` updates `gap` and `thickness` so there is no need to compesate these values.
135      A `gap` of 0 will leave no gap between the lambdas.
136      A `gap` of 1 will remove the top portion of the long lambda leg until the point where the two lambda legs intersect.
137  
138      A `gap` of 0.05 to 0.15 is a good value for replicating the official NixOS flake.
139      A `thickness` of 0.5 is a good value for replicating the official NixOS flake.
140      OpenSCAD doesn't have a concept of units so use `scale` and `height` values in the desired ratio.
141  */
142  params = [
143      [ "gap", 0.1 ],
144      [ "height", 1 ],
145      [ "scale", 10 ],
146      [ "thickness", 0.5 ],
147      // [ "colors", ["#5277C3", "#7EBAE4"]],
148      [ "colors", ["#b40bdf", "#ffd200"]],
149  ];
150  
151  
152  flake(params);