Files
modeling-app/docs/kcl/patternTransform.md
Nick Cameron e8feb0309b Automatic fixing of deprecations and use non-quoted default planes by default (#5902)
* Automatic fixing of deprecations and use non-quoted default planes by default

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-21 09:39:12 +00:00

748 KiB

title, excerpt, layout
title excerpt layout
patternTransform Repeat a 3-dimensional solid, changing it each time. manual

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")

patternTransform(
  solids: [Solid],
  instances: integer,
  transform: FunctionSource,
  useOriginal?: bool,
): [Solid]

Arguments

Name Type Description Required
solids [Solid] The solid(s) to duplicate Yes
instances integer 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. Yes
transform FunctionSource 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. Yes
useOriginal bool If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. No

Returns

[Solid]

Examples

// 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)

Rendered example of patternTransform 0

// 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)

Rendered example of patternTransform 1

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)
    |> startProfileAt(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, i), pow(1.1, i), pow(0.9, i)],
    // Turn by 15 degrees each time.
    rotation = { angle = 15 * i, origin = "local" }
  }
}

myCubes = cube(width, [100, 0])
  |> patternTransform(instances = 25, transform = transform)

Rendered example of patternTransform 2

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)
    |> startProfileAt(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(width, [100, 100])
  |> patternTransform(instances = 4, transform = transform)

Rendered example of patternTransform 3

// 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))
  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)

Rendered example of patternTransform 4

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)
  |> startProfileAt([0, 0], %)
  |> polygon({
       radius = 10,
       numSides = 4,
       center = [0, 0],
       inscribed = false
     }, %)
  |> extrude(length = 4)
  |> patternTransform(instances = 3, transform = transform)

Rendered example of patternTransform 5