Declare pattern transform functions in KCL (#7057)
* Declare pattern transform using KCL Signed-off-by: Nick Cameron <nrc@ncameron.org> * Boolean function param defaults Signed-off-by: Nick Cameron <nrc@ncameron.org> * Parse empty record types in fn types Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
		@ -278,3 +278,28 @@ export fn revolve(
 | 
			
		||||
  /// A named tag for the face at the end of the revolve.
 | 
			
		||||
  tagEnd?: tag,
 | 
			
		||||
): [Solid; 1+] {}
 | 
			
		||||
 | 
			
		||||
/// Just like `patternTransform`, but works on 2D sketches not 3D solids.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Each instance will be shifted along the X axis.
 | 
			
		||||
/// fn transform(@id) {
 | 
			
		||||
///   return { translate = [4 * id, 0] }
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// // Sketch 4 circles.
 | 
			
		||||
/// sketch001 = startSketchOn(XZ)
 | 
			
		||||
///   |> circle(center = [0, 0], radius = 2)
 | 
			
		||||
///   |> patternTransform2d(instances = 4, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn patternTransform2d(
 | 
			
		||||
  /// The sketch(es) to duplicate.
 | 
			
		||||
  @sketches: [Sketch; 1+],
 | 
			
		||||
  /// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
 | 
			
		||||
  instances: number(Count),
 | 
			
		||||
  /// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
 | 
			
		||||
  transform: fn(number(Count)): {},
 | 
			
		||||
  /// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
 | 
			
		||||
  useOriginal?: boolean = false,
 | 
			
		||||
): [Sketch; 1+] {}
 | 
			
		||||
 | 
			
		||||
@ -365,3 +365,202 @@ export fn hollow(
 | 
			
		||||
  /// The thickness of the remaining shell
 | 
			
		||||
  thickness: number(Length),
 | 
			
		||||
): Solid {}
 | 
			
		||||
 | 
			
		||||
/// Repeat a 3-dimensional solid, changing it each time.
 | 
			
		||||
///
 | 
			
		||||
/// Replicates the 3D solid, applying a transformation function to each replica.
 | 
			
		||||
/// Transformation function could alter rotation, scale, visibility, position, etc.
 | 
			
		||||
///
 | 
			
		||||
/// The `patternTransform` call itself takes a number for how many total instances of
 | 
			
		||||
/// the shape should be. For example, if you use a circle with `patternTransform(instances = 4, transform = f)`
 | 
			
		||||
/// then there will be 4 circles: the original, and 3 created by replicating the original and
 | 
			
		||||
/// calling the transform function on each.
 | 
			
		||||
///
 | 
			
		||||
/// The transform function takes a single parameter: an integer representing which
 | 
			
		||||
/// number replication the transform is for. E.g. the first replica to be transformed
 | 
			
		||||
/// will be passed the argument `1`. This simplifies your math: the transform function can
 | 
			
		||||
/// rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
 | 
			
		||||
///
 | 
			
		||||
/// The transform function returns a transform object. All properties of the object are optional,
 | 
			
		||||
/// they each default to "no change". So the overall transform object defaults to "no change" too.
 | 
			
		||||
/// Its properties are:
 | 
			
		||||
///
 | 
			
		||||
///  - `translate` (3D point)
 | 
			
		||||
///
 | 
			
		||||
///    Translates the replica, moving its position in space.      
 | 
			
		||||
///
 | 
			
		||||
///  - `replicate` (bool)
 | 
			
		||||
///
 | 
			
		||||
///    If false, this ID will not actually copy the object. It'll be skipped.
 | 
			
		||||
///
 | 
			
		||||
///  - `scale` (3D point)
 | 
			
		||||
///
 | 
			
		||||
///    Stretches the object, multiplying its width in the given dimension by the point's component in
 | 
			
		||||
///    that direction.      
 | 
			
		||||
///
 | 
			
		||||
///  - `rotation` (object, with the following properties)
 | 
			
		||||
///
 | 
			
		||||
///    - `rotation.axis` (a 3D point, defaults to the Z axis)
 | 
			
		||||
///
 | 
			
		||||
///    - `rotation.angle` (number of degrees)
 | 
			
		||||
///
 | 
			
		||||
///    - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Each instance will be shifted along the X axis.
 | 
			
		||||
/// fn transform(@id) {
 | 
			
		||||
///   return { translate = [4 * id, 0, 0] }
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// // Sketch 4 cylinders.
 | 
			
		||||
/// sketch001 = startSketchOn(XZ)
 | 
			
		||||
///   |> circle(center = [0, 0], radius = 2)
 | 
			
		||||
///   |> extrude(length = 5)
 | 
			
		||||
///   |> patternTransform(instances = 4, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Each instance will be shifted along the X axis,
 | 
			
		||||
/// // with a gap between the original (at x = 0) and the first replica
 | 
			
		||||
/// // (at x = 8). This is because `id` starts at 1.
 | 
			
		||||
/// fn transform(@id) {
 | 
			
		||||
///   return { translate = [4 * (1+id), 0, 0] }
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XZ)
 | 
			
		||||
///   |> circle(center = [0, 0], radius = 2)
 | 
			
		||||
///   |> extrude(length = 5)
 | 
			
		||||
///   |> patternTransform(instances = 4, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// fn cube(length, center) {
 | 
			
		||||
///   l = length/2
 | 
			
		||||
///   x = center[0]
 | 
			
		||||
///   y = center[1]
 | 
			
		||||
///   p0 = [-l + x, -l + y]
 | 
			
		||||
///   p1 = [-l + x,  l + y]
 | 
			
		||||
///   p2 = [ l + x,  l + y]
 | 
			
		||||
///   p3 = [ l + x, -l + y]
 | 
			
		||||
///
 | 
			
		||||
///   return startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = p0)
 | 
			
		||||
///   |> line(endAbsolute = p1)
 | 
			
		||||
///   |> line(endAbsolute = p2)
 | 
			
		||||
///   |> line(endAbsolute = p3)
 | 
			
		||||
///   |> line(endAbsolute = p0)
 | 
			
		||||
///   |> close()
 | 
			
		||||
///   |> extrude(length = length)
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// width = 20
 | 
			
		||||
/// fn transform(@i) {
 | 
			
		||||
///   return {
 | 
			
		||||
///     // Move down each time.
 | 
			
		||||
///     translate = [0, 0, -i * width],
 | 
			
		||||
///     // Make the cube longer, wider and flatter each time.
 | 
			
		||||
///     scale = [pow(1.1, exp = i), pow(1.1, exp = i), pow(0.9, exp = i)],
 | 
			
		||||
///     // Turn by 15 degrees each time.
 | 
			
		||||
///     rotation = {
 | 
			
		||||
///       angle = 15 * i,
 | 
			
		||||
///       origin = "local",
 | 
			
		||||
///     }
 | 
			
		||||
///   }
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// myCubes =
 | 
			
		||||
///   cube(length = width, center = [100,0])
 | 
			
		||||
///   |> patternTransform(instances = 25, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// fn cube(length, center) {
 | 
			
		||||
///   l = length/2
 | 
			
		||||
///   x = center[0]
 | 
			
		||||
///   y = center[1]
 | 
			
		||||
///   p0 = [-l + x, -l + y]
 | 
			
		||||
///   p1 = [-l + x,  l + y]
 | 
			
		||||
///   p2 = [ l + x,  l + y]
 | 
			
		||||
///   p3 = [ l + x, -l + y]
 | 
			
		||||
///   
 | 
			
		||||
///   return startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = p0)
 | 
			
		||||
///   |> line(endAbsolute = p1)
 | 
			
		||||
///   |> line(endAbsolute = p2)
 | 
			
		||||
///   |> line(endAbsolute = p3)
 | 
			
		||||
///   |> line(endAbsolute = p0)
 | 
			
		||||
///   |> close()
 | 
			
		||||
///   |> extrude(length = length)
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// width = 20
 | 
			
		||||
/// fn transform(@i) {
 | 
			
		||||
///   return {
 | 
			
		||||
///     translate = [0, 0, -i * width],
 | 
			
		||||
///     rotation = {
 | 
			
		||||
///       angle = 90 * i,
 | 
			
		||||
///       // Rotate around the overall scene's origin.
 | 
			
		||||
///       origin = "global",
 | 
			
		||||
///     }
 | 
			
		||||
///   }
 | 
			
		||||
/// }
 | 
			
		||||
/// myCubes =
 | 
			
		||||
///   cube(length = width, center = [100,100])
 | 
			
		||||
///   |> patternTransform(instances = 4, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Parameters
 | 
			
		||||
/// r = 50    // base radius
 | 
			
		||||
/// h = 10    // layer height
 | 
			
		||||
/// t = 0.005 // taper factor [0-1)
 | 
			
		||||
/// // Defines how to modify each layer of the vase.
 | 
			
		||||
/// // Each replica is shifted up the Z axis, and has a smoothly-varying radius
 | 
			
		||||
/// fn transform(@replicaId) {
 | 
			
		||||
///   scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
 | 
			
		||||
///   return {
 | 
			
		||||
///     translate = [0, 0, replicaId * 10],
 | 
			
		||||
///     scale = [scale, scale, 0],
 | 
			
		||||
///   }
 | 
			
		||||
/// }
 | 
			
		||||
/// // Each layer is just a pretty thin cylinder.
 | 
			
		||||
/// fn layer() {
 | 
			
		||||
///   return startSketchOn(XY) // or some other plane idk
 | 
			
		||||
///     |> circle(center = [0, 0], radius = 1, tag = $tag1)
 | 
			
		||||
///     |> extrude(length = h)
 | 
			
		||||
/// }
 | 
			
		||||
/// // The vase is 100 layers tall.
 | 
			
		||||
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
 | 
			
		||||
/// vase = layer() |> patternTransform(instances = 100, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// fn transform(@i) {
 | 
			
		||||
///   // Transform functions can return multiple transforms. They'll be applied in order.
 | 
			
		||||
///   return [
 | 
			
		||||
///     { translate = [30 * i, 0, 0] },
 | 
			
		||||
///     { rotation = { angle = 45 * i } },
 | 
			
		||||
///   ]
 | 
			
		||||
/// }
 | 
			
		||||
/// startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> polygon(
 | 
			
		||||
///        radius = 10,
 | 
			
		||||
///        numSides = 4,
 | 
			
		||||
///        center = [0, 0],
 | 
			
		||||
///        inscribed = false,
 | 
			
		||||
///      )
 | 
			
		||||
///   |> extrude(length = 4)
 | 
			
		||||
///   |> patternTransform(instances = 3, transform = transform)
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn patternTransform(
 | 
			
		||||
  /// The solid(s) to duplicate.
 | 
			
		||||
  @solids: [Solid; 1+],
 | 
			
		||||
  /// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
 | 
			
		||||
  instances: number(Count),
 | 
			
		||||
  /// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
 | 
			
		||||
  transform: fn(number(Count)): {},
 | 
			
		||||
  /// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
 | 
			
		||||
  useOriginal?: boolean = false,
 | 
			
		||||
): [Solid; 1+] {}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user