Title image Spring 2017

Viewing

Due midnight, 27 February 2017

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


Tasks

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. You will then use the View matrix to create a set of axes that respond to user input to translate, scale, and rotate.

Note: while you should start by copying your project 1 Display code over to your project 3 directory, you will be replacing/deleting some of the code from project 1. Do not try to retain the old functionality or build on top of it. Please don't leave the old code commented-out in your file when you hand it in. If the old code is not being used, delete it.

  1. The first step is to create a set of axes in your visualization program. Import your View class into your application and make a new View object in your application's __init__ method.

    A simple method of implementing axes is to create and store a numpy matrix with the axis endpoints. You should have six endpoints, two for each axis, and the length of each axis should be 1. Each axis should have one endpoint at the origin and the other a distance 1 along its corrsponding axis. So the X-axis endpoints should be [0, 0, 0, 1], and [1, 0, 0, 1] (with the homogenous coordinate).

    You'll also want a field to hold a list that contains the actual graphics line objects that instantiate them on the screen. Initialize these variables in your __init__ to appropriate values (but don't actually create the lines on the canvas there).

    Make a function (e.g. buildAxes()) that builds the view transformation matrix [VTM], multiplies the axis endpoints by the VTM, then creates three new line objects, one for each axis. Use the x and y coordinates of the transformed endpoints as the pixel locations for the lines.

    Note that the VTM is built assuming that the points are in columns. If you are representing the axis points as a set of rows in a matrix, you will need to do the following.

    pts = (vtm * self.axes.T).T

    The above transposes the axis points so each point is a column, multiplies it by the VTM, and then takes the transpose so that the pts matrix has the axis endpoints as rows again. Whatever you do, be consistent in your representations.

    Make another function (e.g. updateAxes()) that executes the following algorithm.

      # build the VTM
      # multiply the axis endpoints by the VTM
      # for each line object
          # update the coordinates of the object
    

    Note that the original axis endpoints do not, in general, ever change for a given visualization. Only the VTM changes, which causes the screen appearance of the axes to change. If you normalize your data sets prior to running them through the view pipeline, which you should do, then axes of unit length should be appropriate.

    See if your axes make sense when you view them.

  2. User Interaction: The final part of the lab is to add user interaction to control the axes.
    1. Bind a function (eg. handleButton1) to the button 1 motion event. The standard button 1 event should store the user's click into a variable (e.g. baseClick1). The button 1 motion function should implement the following algorithm.

      # Calculate the differential motion since the last time the function was called
      # Divide the differential motion (dx, dy) by the screen size (view X, view Y)
      # Multiply the horizontal and vertical motion by the horizontal and vertical extents.
      # Put the result in delta0 and delta1
      # The VRP should be updated by delta0 * U + delta1 * VUP (this is a vector equation)
      # call updateAxes()
      

      Test your translation. See what happens if you put some multipliers on delta0 and delta1 to slow down or speed up the motion.

    2. Button 3 motion should implement scaling. The scaling behavior should act like a vertical lever. The button 3 click should store a base click point that does not change while the user holds down the mouse button. It should also store the value of the extent in the view space when the user clicked. This is the original extent. Make sure you create a copy and not a reference to the extent value (you could use the View clone function).

      The button 3 motion should convert the distance between the base click and the current mouse position into a scale factor. Keep the scale factor between 0.1 and 3.0. You can then multiply the original extent by the factor and put it into the View object. Then call updateAxes(). Do not modify the original extent while the mouse is in motion.

      Test out this capability. If you click in the window, as you move above your original click point, the scene should zoom in. As you move below your original click point, the scene should zoom out. As you come back to your original click point, the scene should go back to its original scale.

    3. Pick a rotation method. You could choose to rotate about the VRP. The method described below rotates about the center of the view volume.

      Make a method in your View class called rotateVRC that takes two angles as arguments, in addition to self. The two angles are how much to rotate about the VUP axis and how much to rotate about the U axis. The process you want to follow is to translate the center of rotation (the middle of the extent volume) to the origin, rotate around the Y axis, rotate around the X axis, then translate back by the opposite of the first translation.

      The process is as follows.

      1. Make a translation matrix to move the point ( VRP + VPN * extent[Z] * 0.5 ) to the origin. Put it in t1.
      2. Make an axis alignment matrix Rxyz using u, vup and vpn.
      3. Make a rotation matrix about the Y axis by the VUP angle, put it in r1.
      4. Make a rotation matrix about the X axis by the U angle. Put it in r2.
      5. Make a translation matrix that has the opposite translation from step 1.
      6. Make a numpy matrix where the VRP is on the first row, with a 1 in the homogeneous coordinate, and u, vup, and vpn are the next three rows, with a 0 in the homogeneous coordinate.
      7. Execute the following: tvrc = (t2*Rxyz.T*r2*r1*Rxyz*t1*tvrc.T).T

        Then copy the values from tvrc back into the VPR, U, VUP, and VPN fields and normalize U, VUP, and VPN.

    4. Add a button 2 motion function and binding. The button 2 click should store the button click location into a variable like baseClick2. The same function should store a clone of your View object. This is the original View object. In addition to Button-2 and B2-Motion, bind Control-Button-1 and Control-B1-motion to the button 2 functionality. You may not be able to access the Button-2 event on a laptop without a 3-button mouse.

      Within the button2motion function, first calculate delta0 and delta1 as the pixel motion differences in x and y divided by a constant (e.g. 200) and multiplied by pi (math.pi). Think of it as how many pixels the user must move the mouse to execute a 180 degree rotation.

      Clone the original View object and assign it to your standard view field, then rotate the view using the rotateVRC method and the two angles delta0 and delta1. Then call updateAxes. /p>

      See if your function does the right thing. You may need to negate the delta1 (vertical motion) to get the proper behavior.


Extensions


Writeup

Make a wiki page for the project writeup.

  • Write a brief summary of your project that describes the purpose, the task, and your solution to it. The summary should be 200 words or less.
  • Describe your View class API, with brief descriptions of all the functions, their inputs, outputs, and purpose. This should look like documentation.
  • Describe in your writeup how you store the parameters internally in your View class.
  • Include in your writeup a screen capture of the axes in two different viewing situations. Alternatively, take a screen capture movie of your axes moving around in response to the different user intputs.
  • Handin

    Once you have written up your assignment, give the page the label:

    cs251s17project3

    Put your code in a project 3 folder in your private subdirectory in your CS251 folder on Courses.