Transforms#
Four operations that move geometry without changing its shape. Each works in 2D and 3D, and passes its children's dimensionality through.
All four union their children first, so a transform applied to several blocks moves them together:
translate {
offset = [0, 0, 10]
box { size = [4, 4, 4] }
sphere { radius = 3 } // both move
}
Nested transforms collapse into a single matrix before they reach the kernel, so a deep stack
of translate and rotate blocks costs one operation rather than one per level.
translate#
Moves geometry.
translate { offset = [10, 0, 5] box { size = [4, 4, 4] } }
translate { offset = [10, 0] rect { size = [4, 4] } } // 2D
Attributes#
| Attribute | Type | Default | Description |
|---|---|---|---|
offset |
vector of the child's dimensionality | required | How far to move along each axis |
Caveats#
offsetmust have as many components as the child has dimensions - 2 for a cross-section, 3 for a solid. A mismatch istranslate: "offset" must be a number or an array of 3 numbers.- A single number broadcasts to every axis, which is rarely what you want here but is consistent with every other vector attribute.
rotate#
Turns geometry about the origin. All angles are in degrees.
rotate { z = 45 box { size = [20, 4, 4] } } // 3D, one axis
rotate { angles = [0, 90, 45] box { size = [20, 4, 4] } } // 3D, all three
rotate { angle = 30 rect { size = [20, 4] } } // 2D
Attributes - 3D children#
| Attribute | Type | Default | Description |
|---|---|---|---|
angles |
3-vector | - | Degrees about X, Y and Z at once |
x, y, z |
number | 0 |
Degrees about that axis, named one at a time |
Attributes - 2D children#
| Attribute | Type | Default | Description |
|---|---|---|---|
angle |
angle | required | Degrees, counter-clockwise |
Caveats#
- Rotation is about the origin, not about the geometry. To spin something in place, put it at the origin, rotate, then translate - or build it centred in the first place.
- In 3D the axes are applied X, then Y, then Z, matching OpenSCAD, so a model ported from
there lands in the same orientation. Composing rotations in a different order requires
separate
rotateblocks. anglesand the named axes are alternatives. Usingangleon a 3D child, orx/y/zon a 2D one, is an unknown attribute.
scale#
Scales geometry about the origin.
scale { factor = 2 sphere { radius = 5 } }
scale { factor = [1, 1, 0.5] sphere { radius = 5 } } // flattened
Attributes#
| Attribute | Type | Default | Description |
|---|---|---|---|
factor |
vector of the child's dimensionality | required | Multiplier per axis. A single number scales every axis alike |
Caveats#
- Scaling is about the origin, so anything not centred there also moves.
- A negative factor mirrors as well as scales, and flips the winding. Prefer
mirrorwhen reflection is what you mean. - A factor of
0collapses the geometry to nothing. - Non-uniform scaling of a curved surface changes its radii - a scaled sphere is an ellipsoid, which is the cheapest way to get one.
mirror#
Reflects geometry in the plane through the origin with the given normal.
mirror { normal = [1, 0, 0] bracket { } } // reflect across the YZ plane
Attributes#
| Attribute | Type | Default | Description |
|---|---|---|---|
normal |
vector of the child's dimensionality | required | The normal of the mirror plane. Normalised first, so its length does not matter |
Caveats#
The plane always passes through the origin. To mirror about some other plane, translate to the origin, mirror, and translate back.
In 2D,
normaldescribes a line rather than a plane.mirrorreplaces the geometry with its reflection; it does not keep the original. For a symmetric pair, union both:union { bracket { } mirror { normal = [1, 0, 0] bracket { } } }
Usage#
Rotating in place#
The origin is the pivot, so centre first:
// ✗ the box sweeps around the origin
rotate { z = 30 box { size = [40, 10, 4] } }
// ✓ centred, so it turns on the spot
rotate { z = 30 box { size = [40, 10, 4] center = true } }
// ✓ or move it back afterwards
translate { offset = [20, 5, 0] rotate { z = 30 box { size = [40, 10, 4] center = true } } }
Order of operations#
Blocks nest inside-out: the transform closest to the shape is applied first.
translate { // …then moved 50 along X
offset = [50, 0, 0]
rotate { // the box is rotated first…
z = 45
box { size = [20, 4, 4] center = true }
}
}
Swapping the two gives a different result - rotating a translated box sweeps it around the origin on an arc of radius 50.
Radial patterns#
Rotate a translated copy, once per step:
for i in range(0, 8) {
rotate {
z = i * 45
translate { offset = [30, 0, 0] cylinder { radius = 3 height = 10 } }
}
}
Because identity is content identity, this costs one cylinder and eight transforms - the cylinder node is the same node every time round the loop.
Mirrored pairs#
component "hinge_leaf" { ... }
model "hinge" {
part "left" { hinge_leaf { } }
part "right" { mirror { normal = [1, 0, 0] hinge_leaf { } } }
}
Ellipsoids and tapers#
Non-uniform scale is the simplest route to shapes the primitives do not offer:
scale { factor = [1, 0.6, 1] sphere { radius = 20 segments = 64 } }
Troubleshooting#
translate: "offset" must be a number or an array of 3 numbers#
The offset has the wrong number of components for its child. 2D children want two, 3D children want three - and a 2D shape stays 2D until it is extruded:
translate { offset = [1, 2, 3] rect { size = [4, 4] } } // ✗ rect is 2D
translate { offset = [1, 2] rect { size = [4, 4] } } // ✓
rotate: unknown attribute "angle"#
angle is the 2D spelling. A 3D child takes x, y, z or angles:
rotate { angle = 45 box { size = [1, 1, 1] } } // ✗
rotate { z = 45 box { size = [1, 1, 1] } } // ✓
The reverse - z on a 2D child - fails the same way.
"translate" needs at least one shape inside it#
Transforms need something to act on. Often the body contains only a
part, which contributes no geometry to its parent, or an if that took no
branch.
My rotated part flew off across the scene#
Rotation is about the origin. A shape 200 mm out rotates around a 200 mm arc. Centre it, rotate, then move it where it belongs.
My mirrored copy vanished#
mirror replaces rather than adds. Union the original with the reflection if you want both.