CS 251: Project #3

Project 3: Interactive 3D Viewing

Project due Monday night Mar 3, 2014

The purpose of this assignment is to integrate your viewing pipeline with your display application. You will also write code to support user interaction (i.e. translation, scaling, and rotation).


  1. The next step is to create a set of axes in your visualization program (display.py).

    Remove the code that creates, manipulates, and destroys the 2D viewing objects. One way to think of this is "remove most of the code that uses self.objects". For example, you will need to remove createRandomDataPoints and clearData and the buttons that call them. You will definitely want to remove any code that determines the positions of the points. However, it is perfectly reasonable to leave the code that changes the color or shapes of the points. In the second half of the project, you will be replacing all the code in the mouse-button and mouse-movement callbacks, so you will probably want to gut them, but not delete them.

    Import view.py into your application and make a new View object in your application's __init__.

    A simple method of implementing axes is to create and store a numpy matrix with the axis endpoints. You should have six endpoints, and the length of each axis should be 1 (don't assume that one of the endpoints will always be zero). You'll also want a list to hold the actual graphics objects (the lines) that instantiate them on the screen. Initialize these variables to None in your __init__.

    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. Store the axis endpoints and the three line objects.

    Note that the VTM is 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.

    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, 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. Translation: 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 and 
          #   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. Scaling: 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.

      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().

      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. Rotation. Implement a method that allows the user to rotate the data 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 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.

      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.

      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, then rotate it using the rotateVRC method and the two angles delta0 and delta1. Then call updateAxes.

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


Writeup and Hand-in

Make a wiki page for the project writeup. On it, describe your View class API, with brief descriptions of all the functions, their inputs, outputs, and purpose.

Describe in your writeup how you store the parameters internally in your View class.

Include in your writeup a screen capture of your 3D axes in two different viewing situations.


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


Put your code in a folder named "Proj3" in your private subdirectory in Courses/CS251.