9.2 3D Primitives and Transforms
SLO 3
Describe, design, implement, and test structured programs using currently accepted methodology.
SLO 4
Explain what an algorithm is and its importance in computer programming.
Learning Objectives
By the end of this section, you will be able to:
- name the main 3D primitives in JSCAD and describe what each one builds;
- read each primitive's options table and predict the shape it produces;
- choose the right primitive for a part you want to model;
- move, rotate, and resize a solid with the translate, rotate, and scale transforms;
- flip, center, and align a solid with the mirror, center, and align transforms;
- combine transforms and predict how their order changes the result.
cube
The cube(options?) function returns a geom3 — an axis-aligned solid cube with six equal square faces.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
cube center |
size |
Number | 2 |
edge length |
A cube is the simplest 3D solid: every edge is the same length. It is a good starting point when you want a plain, blocky part.
size is one number
Unlike a box, a cube has only one size value because all three dimensions are equal. If you need different width, depth, and height, you want cuboid instead.
Write a call to cube that creates a cube with edge length 4 centered at the origin.
Solution
Step 1 — pick the function: We want a cube, so we use cube.
Step 2 — set the size: The edge length is 4, so size: 4.
Step 3 — write the call:
Answer: cube({ size: 4 }) creates a cube with edge length 4 centered at [0,0,0].
cuboid
The cuboid(options?) function is like cube, but it lets you set independent width, depth, and height.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
cuboid center |
size |
Array | [2,2,2] |
[width, depth, height] |
Most real parts are not perfect cubes. A phone case, a book, or a board is a box with different dimensions, so cuboid is the shape you reach for most often.
The size array is always [width, depth, height]. Mixing up the order gives you a box with the wrong proportions, so keep the order in mind.
Write a call to cuboid that creates a box 6 wide, 2 deep, and 8 tall.
Solution
Step 1 — pick the function: We need different dimensions, so we use cuboid.
Step 2 — set the size array: Width 6, depth 2, height 8 gives size: [6, 2, 8].
Step 3 — write the call:
Answer: cuboid({ size: [6, 2, 8] }) creates a box 6 wide, 2 deep, and 8 tall.
sphere
The sphere(options?) function returns a geom3 where every surface point is an equal distance from the center.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
sphere center |
radius |
Number | 1 |
distance from center |
segments |
Number | 32 |
divisions per full rotation |
axes |
Array | optional | three basis vectors for x/y/z |
A sphere is the 3D version of a circle. Every point on its surface is the same distance from the center, which is exactly what the radius option sets.
segments smooths the ball
Like the rotation in the last section, a sphere is drawn with segments. More segments means a smoother ball; fewer means a faceted, gem-like shape.
Write a call to sphere that creates a sphere with radius 3.
Solution
Step 1 — pick the function: We want a sphere, so we use sphere.
Step 2 — set the radius: The radius is 3, so radius: 3.
Step 3 — write the call:
Answer: sphere({ radius: 3 }) creates a sphere with radius 3 centered at the origin.
cylinder
The cylinder(options?) function returns a geom3 — a Z-axis aligned solid cylinder.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
cylinder center |
height |
Number | 2 |
vertical extent |
radius |
Number | 1 |
radius |
segments |
Number | 32 |
divisions per full rotation |
A cylinder is like a soup can: a circular top and bottom with straight sides. It is the shape you want for a wheel, a pipe, or a peg.
The height controls how tall the can is, and the radius controls how wide its circular face is. Change one without the other and you get a taller or wider can.
Write a call to cylinder that creates a cylinder with height 5 and radius 2.
Solution
Step 1 — pick the function: We want a cylinder, so we use cylinder.
Step 2 — set the options: Height 5 and radius 2 give { height: 5, radius: 2 }.
Step 3 — write the call:
Answer: cylinder({ height: 5, radius: 2 }) creates a cylinder 5 tall with radius 2.
torus
The torus(options?) function returns a geom3 — a small circle swept around a larger circular path.
| Option | Type | Default | Meaning |
|---|---|---|---|
innerRadius |
Number | 1 |
radius of the small (tube) circle |
outerRadius |
Number | 4 |
radius of the large (path) circle |
innerSegments |
Integer | 32 |
divisions of the small circle |
outerSegments |
Integer | 32 |
divisions of the rotation |
innerRotation |
Integer | 0 |
small-circle angle, radians |
outerRotation |
Number | TAU |
rotation of the torus, radians |
startAngle |
Number | 0 |
beginning angle, radians |
A torus is the shape of a donut or a ring. The innerRadius is the thickness of the tube, and the outerRadius is the size of the ring the tube travels around.
A torus is really two circles working together. The small circle is the tube's cross-section, and the large circle is the path that tube follows. Each has its own radius.
Write a call to torus that creates a donut with a tube radius of 2 and a ring radius of 10.
Solution
Step 1 — pick the function: We want a donut, so we use torus.
Step 2 — set the radii: The tube radius is innerRadius: 2, and the ring radius is outerRadius: 10.
Step 3 — write the call:
Answer: torus({ innerRadius: 2, outerRadius: 10 }) creates a donut with a tube radius of 2 and a ring radius of 10.
roundedCuboid
The roundedCuboid(options?) function returns a geom3 — a cuboid with rounded edges. It is often preferable for parts that will be handled or 3D-printed.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
center location |
size |
Array | [2,2,2] |
[width, depth, height] |
roundRadius |
Number | 0.2 |
radius of the rounded edges |
segments |
Number | 32 |
segments per full rotation |
Sharp corners can be uncomfortable to hold and can print poorly. Rounded edges make a part feel nicer in the hand and often print more cleanly, which is why roundedCuboid exists.
roundRadius is the corner size
The roundRadius controls how much the sharp corners are rounded off. A small value gives a barely-rounded box; a large value gives a pill-like shape.
Write a call to roundedCuboid that creates a box 8 wide, 4 deep, and 6 tall with rounded edges of radius 1.
Solution
Step 1 — pick the function: We want a box with rounded edges, so we use roundedCuboid.
Step 2 — set the options: The size array is [8, 4, 6] and the round radius is 1, giving { size: [8, 4, 6], roundRadius: 1 }.
Step 3 — write the call:
Answer: roundedCuboid({ size: [8, 4, 6], roundRadius: 1 }) creates a box 8 wide, 4 deep, and 6 tall with rounded edges of radius 1.
roundedCylinder
The roundedCylinder(options?) function returns a geom3 — a cylinder with rounded ends.
| Option | Type | Default | Meaning |
|---|---|---|---|
center |
Array | [0,0,0] |
center location |
height |
Number | 2 |
cylinder height |
radius |
Number | 1 |
cylinder radius |
roundRadius |
Number | 0.2 |
radius of the rounded edges |
segments |
Number | 32 |
segments per full rotation |
A roundedCylinder is like a cylinder whose flat top and bottom have been rounded off, producing a pill or capsule shape. It is useful for parts that will be handled.
A roundedCylinder takes all the same options as a cylinder and adds one more: roundRadius, which rounds the ends.
Write a call to roundedCylinder that creates a cylinder with height 6, radius 3, and rounded ends of radius 0.5.
Solution
Step 1 — pick the function: We want a cylinder with rounded ends, so we use roundedCylinder.
Step 2 — set the options: Height 6, radius 3, and round radius 0.5 give { height: 6, radius: 3, roundRadius: 0.5 }.
Step 3 — write the call:
Answer: roundedCylinder({ height: 6, radius: 3, roundRadius: 0.5 }) creates a cylinder 6 tall with radius 3 and rounded ends.
translate(offset, ...objects) → geom3
Moves a solid by a given offset without changing its size or orientation.
| Option | Type | Default | Meaning |
|---|---|---|---|
offset |
Array | — (required) | [x, y, z] distance to move the solid |
objects |
geom3 | — (required) | the solid or solids to move |
The primitives take one options object, but transforms take their settings as plain positional arguments — translate([5, 0, 3], myshape), not translate({ offset: [5, 0, 3] }, myshape). The offset comes first, then the solid.
Move a 10-by-20-by-5 box 5 units to the right and 3 units up. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5].
Step 2 — pick the transform. We are moving the solid, so we use translate.
Step 3 — pass the offset and the solid. The offset is [5, 0, 3] (5 right, 0 deep, 3 up), and the solid comes second.
Answer: translate([5, 0, 3], cuboid({ size: [10, 20, 5] })) — the box shifted 5 units right and 3 units up.
rotate(angles, ...objects) → geom3
Turns a solid around the origin by the given angles.
| Option | Type | Default | Meaning |
|---|---|---|---|
angles |
Array | — (required) | [x, y, z] rotation in radians |
objects |
geom3 | — (required) | the solid or solids to rotate |
In 2D, rotating around x or y did nothing visible because the shape had no thickness there. A solid has real depth, so rotateX or rotateY now tips the whole part over — a rotation that was a no-op in the last section is a real move here. Angles are still in radians, and rotateX(angle, myshape) is a shortcut for rotate([angle, 0, 0], myshape).
A rotation that was a no-op in 2D becomes a real move on a solid, because a box has thickness. Build a box and tip it a quarter turn around the x axis — each comment below is one line for you to write. Press Run when you are done.
Solution
You should see the box tipped a quarter turn around the x axis, so the axis that used to point up now points sideways. In 2D this rotation did nothing because the shape had no thickness there; the box's depth is what lets it tip over. Change the angle to Math.PI and run again to flip it all the way upside down.
Rotate a 10-by-20-by-5 box a quarter turn around the x axis. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5].
Step 2 — pick the transform. We are turning the solid, so we use rotate.
Step 3 — pass the angles and the solid. A quarter turn is Math.PI / 2 radians around the x axis, so the angles are [Math.PI / 2, 0, 0].
Answer: rotate([Math.PI / 2, 0, 0], cuboid({ size: [10, 20, 5] })) — the box tipped a quarter turn around the x axis.
scale(factors, ...objects) → geom3
Stretches or shrinks a solid by the given factors along each axis.
| Option | Type | Default | Meaning |
|---|---|---|---|
factors |
Array | — (required) | [x, y, z] multiplier along each axis |
objects |
geom3 | — (required) | the solid or solids to scale |
A factor of 2 doubles the solid along that axis, and a factor of 0.5 halves it. A factor of 1 leaves that axis unchanged. In 3D the factors array has three entries, one per axis, and unequal factors stretch the part out of proportion.
Double the width and triple the height of a 10-by-20-by-5 box. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5].
Step 2 — pick the transform. We are resizing the solid, so we use scale.
Step 3 — pass the factors and the solid. Doubling the width means an x factor of 2; tripling the height means a z factor of 3; the depth stays 1.
Answer: scale([2, 1, 3], cuboid({ size: [10, 20, 5] })) — the box now 20 wide, 20 deep, and 15 tall.
mirror(options, ...objects) → geom3
Flips a solid across a mirror plane.
| Option | Type | Default | Meaning |
|---|---|---|---|
normal |
Array | [0,0,1] |
direction perpendicular to the mirror plane |
origin |
Array | [0,0,0] |
a point the mirror plane passes through |
objects |
geom3 | — (required) | the solid or solids to mirror |
A mirror plane is the flat surface the solid is flipped across, like a pane of glass. It is defined by a point on it (origin) and a direction pointing straight out of it (normal). A normal of [1, 0, 0] mirrors across the y-z plane. Mirroring a solid that is symmetric about that plane is a no-op, so start from an off-centre part to see the flip.
Flip a 10-by-20-by-5 box (whose center is [5, 0, 0]) across the y-z plane. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5]. We also pass center: [5, 0, 0] so the box starts off-centre and the mirror visibly moves it.
Step 2 — pick the transform. We are flipping the solid, so we use mirror.
Step 3 — set the mirror plane. Flipping across the y-z plane means the plane's normal points along x, so normal: [1, 0, 0].
Answer: mirror({ normal: [1, 0, 0] }, cuboid({ size: [10, 20, 5], center: [5, 0, 0] })) — the box flipped from [5, 0, 0] to [-5, 0, 0].
center(options, ...objects) → geom3
Centers a solid on the origin along the chosen axes.
| Option | Type | Default | Meaning |
|---|---|---|---|
axes |
Array | [true,true,true] |
which axes to center on |
relativeTo |
Array | [0,0,0] |
point to center relative to |
objects |
geom3 | — (required) | the solid or solids to center |
Centering slides a solid so its middle sits on the origin — it does not change the solid's size. The axes array tells JSCAD which directions to center in; a false leaves that axis alone. In 3D there are three axes to choose from, and the default centers on all of them.
Center a 10-by-20-by-5 box on the origin. The box's center is [12, 8, 3], so it starts off-centre. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5]. We also pass center: [12, 8, 3] so the box starts off-centre and the centering visibly moves it.
Step 2 — pick the transform. We are centering the solid, so we use center.
Step 3 — set the axes. We want to center on all three axes, so axes: [true, true, true].
Answer: center({ axes: [true, true, true] }, cuboid({ size: [10, 20, 5], center: [12, 8, 3] })) — the box slides from [12, 8, 3] to sit centered on the origin.
align(options, ...objects) → geom3
Aligns a solid to a reference point along each axis.
| Option | Type | Default | Meaning |
|---|---|---|---|
modes |
Array | ['center','center','min'] |
how to align on each axis |
relativeTo |
Array | [0,0,0] |
point to align to |
grouped |
Boolean | false |
align solids as one group |
objects |
geom3 | — (required) | the solid or solids to align |
Each axis has a mode: min puts the solid's low edge on the reference, max puts the high edge there, and center puts the middle there. The default centers on x and y and sets the low edge on z — and in 3D that third mode finally matters, because a solid has a real bottom to sit on.
Align a 10-by-20-by-5 box so its low edge sits on the x-y plane. The box's center is [12, 8, 3], so it starts off-centre and aligning visibly moves it. Write the JSCAD call.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5]. We also pass center: [12, 8, 3] so the box starts off-centre and the aligning visibly moves it.
Step 2 — pick the transform. We are aligning the solid, so we use align.
Step 3 — set the modes. We want the low edge on z, so the z mode is min; the x and y modes can stay center.
Answer: align({ modes: ['center', 'center', 'min'] }, cuboid({ size: [10, 20, 5], center: [12, 8, 3] })) — the box's low edge slides down to the x-y plane.
Combining transforms
You can chain transforms on one solid, but the order matters because rotation is about the origin.
| Transform | Effect |
|---|---|
translate then rotate |
the solid moves first, then the whole moved result spins around the origin |
rotate then translate |
the solid spins in place first, then the tilted result moves |
translate then rotate moves the box to [5, 0, 0] and then spins that whole moved box around the origin, so it ends up far from where it started. rotate then translate tips the box in place and then slides the tilted box over — a different position. This is the single most common 3D student error, and the fix is to read the call from the inside out: the innermost transform runs first.
The order you nest transforms changes the final position, because rotation happens about the origin. Build a box, rotate it, then move it — reading the call from the inside out. Each comment below is one line for you to write. Press Run when you are done.
Solution
You should see the box tipped a quarter turn around x, then slid 5 units to the right. Read it from the inside out: the innermost call (rotate) runs first, then translate wraps the result. Reverse the order — rotate([Math.PI / 2, 0, 0], translate([5, 0, 0], myshape)) — and run again: the box moves first, then the moved box spins around the origin, landing somewhere completely different.
Write the JSCAD call that rotates a 10-by-20-by-5 box a quarter turn around the x axis and then moves it 5 units to the right.
Solution
Step 1 — build the shape. We need a box, so we use cuboid with size: [10, 20, 5].
Step 2 — read the order from the inside out. We want to rotate first, then move, so the rotate call is the inner argument and translate wraps it.
Step 3 — write the call. The rotation is Math.PI / 2 around x, and the move is [5, 0, 0].
Answer: translate([5, 0, 0], rotate([Math.PI / 2, 0, 0], cuboid({ size: [10, 20, 5] }))) — the box tips a quarter turn around x, then slides 5 units right.
Problem Set
Problem 1. What is the difference between cube and cuboid?
Problem 2. What does the radius option control in sphere?
Problem 3. What does the height option control in cylinder?
Problem 4. In torus, what do innerRadius and outerRadius each control?
Problem 5. What does the roundRadius option do in roundedCuboid and roundedCylinder?
Problem 6. Write a call to cuboid that creates a box 10 wide, 5 deep, and 3 tall.
Problem 7. Write a call to cylinder that creates a cylinder with height 8 and radius 4.
Problem 8. Write a call to torus that creates a donut with a tube radius of 3 and a ring radius of 15.
Problem 9. Write a JSCAD call that moves a 10-by-20-by-5 box 5 units to the right and 3 units up.
Problem 10. Write a JSCAD call that rotates a 10-by-20-by-5 box a quarter turn around the x axis.
Problem 11. Write a JSCAD call that doubles the width and triples the height of a 10-by-20-by-5 box.
Problem 12. Write a JSCAD call that flips a 10-by-20-by-5 box (whose center is [5, 0, 0]) across the y-z plane.
Problem 13. Write a JSCAD call that centers a 10-by-20-by-5 box (whose center is [12, 8, 3]) on the origin.
Problem 14. Write a JSCAD call that aligns a 10-by-20-by-5 box (whose center is [12, 8, 3]) so its low edge sits on the x-y plane.
Problem 15. Write a JSCAD call that rotates a 10-by-20-by-5 box a quarter turn around the x axis and then moves it 5 units to the right.
Key Terms
primitive — a basic, ready-made 3D shape such as a cube, sphere, or cylinder.
axis-aligned — aligned with the x, y, and z axes of the coordinate system.
radius — the distance from the center of a shape to its surface.
geom3 — the object type JSCAD uses to represent a 3D geometry.
transform — an operation that moves, rotates, resizes, flips, or aligns an existing solid.
radians — the unit JSCAD uses for angles; a full turn is TAU radians.
mirror plane — the flat surface a solid is flipped across when mirrored.