Title image Spring 2018


Due 5 March 2018

The goal of this week's 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 will be a View class that holds the current viewing parameters and can build a view transformation matrix [VTM] based on the parameters. You will also use the View matrix in a simple GUI to create a set of axes that respond to user input to translate, scale, and rotate.

You can use this skeleton file as the basis for your new GUI. You could copy over your display code from project 1, but you need to make sure and remove any code you don't need for project 3. It's probably better to use the new skeleton file and then copy over any specific functions you need from the old file.

  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.

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



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. It should describe the task, the key parts of your solution, and the result of your work (did it work, what can you do with your GUI?). 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 report how you store the parameters internally in your View class.
  • Include in your report 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 project, give the page the label:


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