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.
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.]
Write a build method that that uses the current viewing
parameters to return a view matrix.
Generate a 4x4 identity matrix, which will be the basis for the view
matrix. For example:
vtm = numpy.identity( 4, float )
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
Calculate the view reference axes tu, tvup, tvpn.
- tu is the cross product (np.cross) of the vup and vpn vectors.
- tvup is the cross product of the vpn and tu vectors.
- tvpn is a copy of the vpn vector.
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 = Vx / length
Vnorm = Vy / length
Vnorm = Vz / length
- Copy the orthonormal axes tu, tvup, and tvpn back to self.u, self.vup and self.vpn.
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
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.5*extent, 0 ) * vtm
Use the extent and screen size values to scale to the screen.
vtm = S( -screen / extent, -screen / extent, 1.0 / extent ) * vtm
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 + offset, screen + offset, 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.]]
- Generate a 4x4 identity matrix, which will be the basis for the view matrix. For example:
- 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.