HW4: Transforms

Due Tues 1/19 @ 11:00am.

Where & What to Hand In

Note that it's important to always use exactly the specified filename when handing in work for a CS class. (Canvas may automatically append a number to your filename; that's okay.) For instructions on creating a screenshot file, see HW0.

Overview & Getting Started

In this assignment, you'll make two 3D scenes (one that I design, and one that you design) by constructing scenegraph that pose all the objects appropriately in the world frame.

Download all the starter code:

First: Transformation Matrices

Fill in the body of each method in transforms.py, so that your scene code can use it to pose objects in the scene. Make sure you test this thoroughly; if there are mistakes here, they'll be difficult to diagnose just from looking at rendered scenes!

If you need to perform any matrix multiplications in your code, remember that np.dot() (or the .dot() method of a numpy array) is the tool that does this — * instead means a plain, elementwise multiplication!

About Rotations

When you rotate around a given vector, all the rotation happens within parallel planes that are orthogonal (normal) to that vector. For example, rotation about the Z axis is rotation strictly in the X–Y plane; the Z coordinate of each point is left unchanged. In turn, this means that rotations around the axes of our space have simpler matrices: they're really just 2-D rotations, and they look like the identity matrix in the row and column corresponding to the rotation axis. Here's the matrix for a rotation about Z by $\theta$ radians, for example:

$$ \begin{bmatrix} \cos(\theta) & -\sin(\theta) & 0 & 0 \\ \sin(\theta) & \cos(\theta) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} $$

So it would be really nice to define our rotations as a composition of fundamental rotations around the X, Y, and Z axes of the space.

Euler angles are a set of parameterizations of rotations, using three angles. However, there's no one fixed, universal sense of what “Euler angles” are — there are many different ways (24 of them, in fact) to use three angles to express a single rotation. Somehow, the meaning of the three angles must be decided, either by the person telling them to you or by a predetermined convention. In this class we've fixed a convention, and specified it using the terms “yaw”, “pitch”, and “roll”: yaw goes around the object's Z axis, pitch around the “yawed” X axis, and roll around the “yawed-and-pitched” Y axis.

That sounds easy enough: we'd apply the rotations as $M_{roll} \cdot M_{pitch} \cdot M_{yaw}$, right? The trouble is that the matrices for pitch and roll, defined this way, would be about the transformed axes, which are not necessarily aligned to the space. These matrices would be really difficult to write out.

Instead, and maybe counterintuitively, we should apply the transformations in reverse order: $M_{yaw} \cdot M_{pitch} \cdot M_{roll}$. Think carefully about this: first, we roll around the Y axis, and then we pitch around the X axis (which transforms the Y axis that we already rolled around). Finally, we yaw around the Z axis, which transforms both the Y and X axes that we already transformed around. Done this way, each of the three matrices may be defined around a principal axis of the space on which it acts, and so twelve of its entries are just 0 or 1.

(Compare this discussion of the order of application to the similar choice of whether to rotate or translate first, when making one object “orbit” around the origin. The correct approach is $M_r \cdot M_t$: first translate [out along the X axis, for example], then rotate [around the origin of the whole space]. Another way of looking at it: we're rotating the coordinate frame, and then translating within that rotated coordinate frame along the rotated X axis.)

As a final note: the rotation angles are taken to be “right-handed” rotations around each axis: a small positive angle corresponds to rotation counterclockwise when looking straight down the given axis. (So a rotation with no yaw and no pitch, but a small positive roll, would cause the X axis to move downward along -Z, and the Z axis to move in the +X direction.) When constructing your rotation matrices, think carefully about what the labels and orientations are for those parallel planes. For example, when you look down the X axis, the thing pointing to the right is the positive-Y axis, and the thing pointing up is the positive-Z axis. What do you get when you look down the Y axis? What about the Z axis?

Scene 1: A Robotic Arm

The first scene you'll build is a near-duplicate of the one we made in class: a robotic arm. Here are the specifications:

Follow these steps, most of which are documented in TODO comments in robot_scene.py:

  1. Before anything else, sketch out the scene and construct the scenegraph, by hand, on paper or a chalkboard. Your scenegraph should have only one shape node. Take a picture or scan of it, and put it in your PDF. (My solution scenegraph has 18 nodes in it.)
  2. Fill in the body of makeScenegraph(). It may be helpful to look at the test function in scenegraph.py.
  3. In your main function, make a call to makeScenegraph() with argument values of your choice.
  4. Use the resulting scenegraph to construct a list of shapes in the world frame. Here's how:
    1. Ask the scenegraph to generate the compositions of all your transformations (using getCompositeTransforms()).
    2. Build a new list of shapes ((verts, tris) 2-tuples) that are the result of applying each transform to each corresponding shape. Be careful not to modify the original ShapeNode objects. Again, look in scenegraph.py for guidance.
  5. Use the Mayavi helper module to render all of the objects in the scene.
  6. Take at least three screenshots of your scene, making sure that the axis indicator is visible. Add them to your PDF.

Scene 2: Your Design

  1. Design your own scene. Here are the requirements:

    • The scene must contain at least six objects, which must be instances of at least three distinct shapes.
    • At least two of those shapes must be custom ones written by you or someone else in the class (taken from our_meshes.py).
    • The scene must involve at least twelve elementary transforms in total.
    • Your scene must include a floor that sits below all the other objects — this counts as one of your six, and transforms for it count toward that total too (I'd expect one scaling and one translation).
    • The "up" direction for your scene must be the positive Z direction. The floor is parallel to the X-Y plane, perhaps at a negative Z value.

    If your scene involves rotating an object about its center point, make sure you use a shape other than a sphere (and that you rotate tori and cylinders around axes other than their axis of symmetry). Otherwise, the action of your rotation will not be apparent in the scene.

    Write out a paragraph description of your scene, and add it to your PDF. Sketching out your scene will probably help you think through the spatial relationships; if you'd like, you may augment your paragraph description with a copy of a sketch or two.

    Design the scenegraph, draw it out on paper or a chalkboard, take a picture, and add that to your PDF.

  2. In my_scene.py, flesh out makeScenegraph(). This includes the arguments — at least three of the transforms must be controlled by arguments to this function. Write a good docstring, and fill in the body. This function should return the root node of the scenegraph.
  3. In main(), make an appropriate call to makeScenegraph().
  4. Get the composite transformations from the scenegraph and make a corresponding list of shapes in the world frame. You should be able to just copy and paste your work from the previous scene for this step.
  5. Render the scene. Again, you should be able to copy and paste.
  6. Take at least three screenshots of your scene, making sure that the axis indicator is visible. Add them to your PDF.

Rubric

This assignment is worth 15 points.

CriterionPoints
scenes.pdf shows all drawings and screenshots.1
Each program runs from command line, displaying the scene in a window that stays open.1
scale(), shear(), and translate() return correct values.3
rotate() returns correct values.2
Both scenes: All transforms are encoded by the scenegraph.1
Both scenes: All functions are named correctly and have complete docstrings.1
Robot: makeScenegraph() produces correct output for several combinations of arguments.2
Robot: Instance transforms are applied correctly for all shapes in the scene.1
Your scene: makeScenegraph() produces correct output for several combinations of arguments.2
Your scene: Instance transforms are applied correctly for all shapes in the scene.1
Total15