Transformations
Meet the Cylinder
Everything from the last lesson (position, size, align=, center=) works
exactly the same for a new shape: Cylinder(radius, height, x=0, y=0,
z=0). Same base-at-z, centered-on-x/y rule as Box. Try it:
We’re using a cylinder for this lesson on purpose, not just for variety. A box that’s rotated often looks almost the same as before, especially if it’s close to a cube. A cylinder standing up and a cylinder lying down look nothing alike, so it’s much easier to actually see what a transformation did.
Turning Things: rotate()
rotate(shape, angle, axis="z") takes a shape and gives back a new,
rotated one. angle is in degrees. axis is "x", "y", or "z" –
which direction you’re turning around.
Notice standing never shows up on its own here. That’s on purpose:
rotate() consumes the shape you give it, the same way you’d expect if
you handed someone a piece of paper and they folded it – you don’t also
still have an unfolded copy. Only the result of rotate(...) renders.
You run a = Cylinder(1, 3) then b = rotate(a, 90, axis="x"), and nothing else. What shows up in the scene?
If you rotate a standing cylinder 90° around axis="z", what happens to how it looks?
Try it yourself below before reading on.
A standing cylinder’s own axis is z. Spinning something around its own
axis doesn’t change its silhouette at all – it’s the same reason spinning
a can of soup in place doesn’t make it look any different, even though it
really is turning. This is worth remembering: rotating around the axis a
shape is already lined up with is often invisible.
Moving Things: translate()
You already know how to move a shape: give Box/Cylinder their own
x=/y=/z=. So why would you ever need translate(shape, x=0, y=0,
z=0) too?
Because once you’ve rotated something, its own sense of “up” has changed.
A cylinder lying on its side doesn’t have a clean z= to lift it off the
ground with any more – its height now runs sideways. translate() moves
the finished shape in the scene’s real x/y/z, no matter what it’s been
rotated into.
Why does the example above use translate(lying, z=1) instead of just building the cylinder with z=1 in the first place?
Order Matters
translate() and rotate() nest like any other function calls, and like
CMU’s own nested function calls, the inside runs first. That means
translate(rotate(shape, 90, axis="x"), z=3) and rotate(translate(shape,
z=3), 90, axis="x") are not the same thing, even though they use the
exact same two operations.
Run that, then look closely at a and b. Try swapping which one has
x=4 if you want to line them up side by side for an easier comparison.
translate(rotate(cyl, 90, axis="x"), z=3) -- which happens first?
Checking Your Work
Exercise: build a cylinder that's lying down instead of standing up (its long axis running sideways, not vertically). Any axis, any position -- just make it lie down.
Curve Resolution
A cylinder’s curved side isn’t actually curved – it’s a bunch of flat
faces standing close enough together to look round. segments= controls
how many:
6 segments makes a hexagon, not a circle. The default is 32, which is
usually enough to look smooth. This is the same idea as OpenSCAD’s $fn,
if you’ve ever heard of that – just under a name that’s actually legal in
Python ($ can’t start a variable name here).
If you’re about to build several cylinders and want them all a little
chunkier or a little smoother, you don’t have to repeat segments= on
every single one. Set the global SEGMENTS once, and every Cylinder
after that point picks it up automatically:
That last one still comes out smooth – giving a shape its own segments=
always wins over the global default.
If you set SEGMENTS = 4 before making a Cylinder, what shape do you actually get?
Practice
That’s rotate(), translate(), how they compose, and how to control curve
resolution. One more thing worth knowing, in the cheatsheet if you want
the details: rotate() can also take a list of three angles instead of
one angle and an axis: rotate(shape, [30, 0, 45]) rotates around x, then
y, then z, all in one call. Everything in this lesson used the angle,
axis="x" form on purpose, since it doesn’t require knowing what a list is
yet – but if you’ve used lists before, the list form is there when you
want more than one axis at once.