For this assignment we'll modify last week's GUI so we can read and visualize data from a file. We'll stick with 2D data for this week.
You should start this week by making a copy of your python program from week 1. Alternatively, you can download the skeleton file for project 2.
The goal of this week is to get the viewing pipeline up and running properly. Except for translating, we'll leave the mouse manipulation for next week.
This week we're also going to start using the NumPy package for data handling and manipulation. Matrix multiplication and many other operations are handled natively by NumPy, which makes our lives much easier.
Edit your handleOpen function so that it reads in a 2D data file and
stores it into a NumPy matrix object. There is an example of how to
do this in the skeleton file.
Add the additional capability to the open function such that the program skips any lines that begin with a #. To test that capability, try out the data file datafile2.txt.
If the user opens a new file, modify your handleOpen to delete any existing data and any existing points from the Canvas.
Finally, if the data you read is 2D, add a 0 and a 1 to each row of the matrix. If the data is 3D, just add a 1 to the end of the row. Each row of the data matrix should end up with 4 columns, with a 1 in the last column.
In a new file, create a new class called ViewRef. The ViewRef class
should have the following felds and default values, which should be
set in the __init__ method.
- vrp: a NumPy matrix with the default value [0, 0, 0]
- 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 with the default values [1, 1, 1]
- view: a list with the default value [100, 100]
The ViewRef class also needs a function build that computes the view matrix and returns it. The process to execute in the build function is as follows.
Generate a 4x4 identity matrix, which will be the basis for the view matrix. For example:
m = numpy.identity( 4, float )
Generate a translation matrix to move the VRP to the origin and then
premultiply m 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] ] ) m = t1 * m
Calculate the view reference axes tu, tvup, tvpn.
- tu is the cross product 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. You probably want to write a normalize function to handle this. You'll need the math library for the square root function.
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 ] ] ); m = r1 * m
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. Conceptually, this is
m = T( 0.5*extent, 0.5*extent, 0 ) * m
Use the extent and screen size values to scale to the screen.
Conceptually, this is represented as:
m = S( -view / extent, -view / extent, 1.0 / extent ) * m
Finally, translate the lower left corner to the origin.
m = T( view, view, 0 ) * m
If your code is working properly, then using the default parameters you should get the matrix below.
[[-100. 0. 0. 50.] [ 0. -100. 0. 50.] [ 0. 0. 1. 0.] [ 0. 0. 0. 1.]]
- Calculate the min, max, and mean of the x and y values of the data set.
- Set the view reference point to the mean of the data (z value 0).
- Set the view plane normal to be the negative z axis [0, 0, -1].
- Set the x and y extents so that all points will be visible.
- Set the viewing size to be the window size.
Once the parameters are set, use the build() function to calculate the view matrix. Store the matrix in a variable (e.g. viewMatrix).
viewdata = viewMatrix * self.data.transpose()
viewdata = viewdata.transpose()
Now viewdata holds a matrix of drawing locations. The first two values in each row are the x and y coordinates. Note that when drawing a shape, the x coordinate is the column and the y coordinate is the row. When you create a shape using tk, the coordinates are row column, not x, y.
Draw the data into the window.
If you keep track of everything you've drawn, then all of your manipulation tools you made last week will still work. Next week we'll look at improving on them.
- Enable the user to pick a color scheme for the points.
- Add functionality to your ViewRef class to make it easier to specify the view parameters. For example, have it update the view parameters based on the min, max, and mean of the data set.
- Modularize your code by making a method for your Application class that transforms and optionally updates the data locations in the window.
- If you didn't do it in project 1, enable keyboard equivalents for Open... and Quit in the File menu (cmd-o and cmd-q).
The writeup for each weekly project should be a brief summary of what you did along with some screen shots, graphs, or tables of results, depending upon the assignment. Please organize the writeup as follows.
- Title of the project and your name
- An abstract describing what you did in 200 words or less.
- A brief description of code you wrote or analysis you undertook for the project.
- Figures, screen shots, graphs, tables, or other results.
- A brief description of what you learned.
Make your writeup for the project a wiki page in your personal space. If you have questions about making a page, stop by during office hours 1-3pm on Mondays or Tuesdays.
Once you have written up your assignment, give the page the label:
You can give any page a label when you're editing it using the label field at the bottom of the page.
Do not put code on your writeup page or anywhere it can be publicly accessed. To hand in code, attach it to an email and send it to the prof.