Title image Spring 2018


The goal of this week's lab and project is to build a class for creating and managing the viewing parameters and view transformation matrix.


The main result of your work this week should be a View class that holds the current viewing parameters and can build a view transformation matrix [VTM] based on the parameters.

  1. Create a file view.py and begin a class View (you can use any name you like, but I'll provide some short test programs that assume the class is named View).

    Within the __init__ method, create the following fields and give them the specified default values. You may want to have a reset method, called from __init__ that assigns the default values, as you may want to enable the user to reset the view at any time. Note that the view parameters provide a complete view of the data, looking normal to the X-Y plane, assuming the data is all located inside the unit cube with one corner on the origin.

    • vrp: a NumPy matrix with the default value [0.5, 0.5, 1].
    • vpn: a NumPy matrix with the default value [0, 0, -1].
    • vup: a NumPy matrix with the default value [0, 1, 0].
    • u: a NumPy matrix with the default value [-1, 0, 0].
    • extent: a list or NumPy matrix with the default value [1., 1., 1.]
    • screen: a list or NumPy matrix with the default value [400., 400.]
    • offset: a list or NumPy matrix with the default value [20., 20.]

  2. Write a build method that that uses the current viewing parameters to return a view matrix.
    1. Generate a 4x4 identity matrix, which will be the basis for the view matrix. For example:

      vtm = numpy.identity( 4, float )

    2. Generate a translation matrix to move the VRP to the origin and then premultiply the vtm by the translation matrix. For example:
      t1 = numpy.matrix( [[1, 0, 0, -self.vrp[0, 0]],
                          [0, 1, 0, -self.vrp[0, 1]],
                          [0, 0, 1, -self.vrp[0, 2]],
                          [0, 0, 0, 1] ] )
      vtm = t1 * vtm
    3. Calculate the view reference axes tu, tvup, tvpn.
      1. tu is the cross product (np.cross) of the vup and vpn vectors.
      2. tvup is the cross product of the vpn and tu vectors.
      3. tvpn is a copy of the vpn vector.
    4. Normalize the view axes tu, tvup, and tvpn to unit length. You probably want to write a simple normalize function to handle this. To normalize a vector, calculate the length of the vector and divide each element of the vector by the length. Make sure you do not include the homogeneous coordinate in the normalization process. A normalized vector has unit length, and can be computed as below. (There are more compact ways to do this using numpy, such as by using np.linalg.norm and using matrix math.)

      Length = math.sqrt( Vx*Vx + Vy*Vy + Vz*Vz )
      Vnorm[0] = Vx / length
      Vnorm[1] = Vy / length
      Vnorm[2] = Vz / length

    5. Copy the orthonormal axes tu, tvup, and tvpn back to self.u, self.vup and self.vpn.
    6. Use the normalized view reference axes to generate the rotation matrix to align the view reference axes and then premultiply M by the rotation. For example:
      # align the axes
      r1 = numpy.matrix( [[ tu[0, 0], tu[0, 1], tu[0, 2], 0.0 ],
                          [ tvup[0, 0], tvup[0, 1], tvup[0, 2], 0.0 ],
                          [ tvpn[0, 0], tvpn[0, 1], tvpn[0, 2], 0.0 ],
                          [ 0.0, 0.0, 0.0, 1.0 ] ] )
      vtm = r1 * vtm
    7. Translate the lower left corner of the view space to the origin. Since the axes are aligned, this is just a translation by half the extent of the view volume in the X and Y view axes.

      Note that the rest of the project description will use shorthand, rather than writing out the matrices explicitly. Conceptually, the translation is represented as:

      vtm = T( 0.5*extent[0], 0.5*extent[1], 0 ) * vtm

    8. Use the extent and screen size values to scale to the screen.

      vtm = S( -screen[0] / extent[0], -screen[1] / extent[1], 1.0 / extent[2] ) * vtm

    9. Finally, translate the lower left corner to the origin and add the view offset, which gives a little buffer around the top and left edges of the window.

      vtm = T( screen[0] + offset[0], screen[1] + offset[1], 0 ) * vtm

    If your code is working properly, then using the default parameters you should get the matrix below.

    [[ 400.    0.    0.    20.]
     [   0. -400.    0.   420.]
     [   0.    0.   -1.    1]
     [   0.    0.    0.    1.]]
  3. Create a clone method for your View object that makes a duplicate View object and returns it. That means you need to create a new View object and the manually copy each field from the current View to the new View object.

When you are done with these tasks, go ahead and start the rest of the project.