In this project, we combined elements of the previous three projects -- interactivity, reading in data, and 3D viewing- to create a useful program that reads in data and selectively plots it in an interactive 3D plane. We can specify a csv file, and choose which dimensions to plot where; for example, col 1 on axis x, col 2 on axis y, and col 3 on axis z, as well as color and size, also derived from columns. This means that each point can have 2-5 numbers specifying its location/features. In my project, I also added labels to the axes to show which column headers they represent, as well as the ability to mouse over a point and see its x,y,and (possibly) z column values from its csv file.
Step 1 was to write an update points method, called whenever the user wants to move/rotate/zoom in, in order to move the points around like we did with the axes. For this, I multiplied the vtm by the matrix with the point locations and looped through all of the objects on the canvas to put their new values in.
Step 2 was to make a choose axes method, in which I make a new instance of a class that opens a dialog box with 5 columns and a list of all of the headers below each. Unlike previous dialog boxes, I had to use a grid arrangement to make the headers go above the boxes because we cannot vertically stack this many listboxes like we did in previous projects. Under the x and y columns, I insert all of the headers, and all of the other columns have "None" added in first because it defaults to only having x and y axes, the bare minimum. Then I highlight the first choice of each. In the end, after the user presses ok, I store the choices they made in fields in the display class. Here is a picture of this window:
For step 3, we had to add code to support making the color and size aspects. This means that I needed to make functions to convert those columns' values to colors and sizes. For my color function, I take in the normalized value and, provided it is between .1 and .9, I multiply it times 255 for the R and G values, but subtract it from 1 and multiply that by 255 for the B value. This makes the blue decrease as the value gets higher and increase as it gets lower. For things out of that range, blue is the lower end's color and yellow is the higher end's. For the size choosing function, I just multiply the normalized value by 10 and turn that to an int to keep it whole, then add 1 to make the point not be too minuscule. When making all of the points in buildPoints, I now factor in the color and size, and when updating, I factor in the size, as well, because this is used to decide coordinates. To get the correct size, I store the size of each point in a list while building them and retrieve those when necessary.
Step 4 was to test everything on the Australia (and New Zealand!) data set. Here are screenshots of the latitude being x, the longitude being y, the basin id being the z axis, the color being based on the cell location values, and the id being based on the size column.
Step 5 was to test it on the data set from project 2:
For my first extension, adding labels to the axes, I used the same kind of strategy I had used in project 3 for putting x, y, and z onto them, but this time, I needed to fetch the correct name. First, in setLetters, I created blank text objects and stored them in a list field. Then, in buildPoints, I configure their text to have the xAxisCol, yAxisCol, and possibly the zAxisCol for their text, which are strings I stored after the user chose the column names.
For the second extension, making info appear when over a point, I used the <Motion> event and bound it to the display tooltip function. For this, I have a label as a field, and if I am over a point, I change it to show the x, y, and possibly z axis information. To do this, I get the object the mouse is over (with index ?0 to make it work because tkinter works in mysterious ways) and find its index within the list of objects. Then, I use that value and the corresponding header as input to the get_value_numeric method. Also, the logic for this extension was a challenge to figure out. In the end, I had it so that if the mouse enters the dot, the value of the field pointOverVal changes, but only if it is different from what it was before. This means that this if statement is only entered upon first entry into the point. Otherwise, you are moving within the dot, so the field doesn't change. Lastly, if you aren't over any points, the label's text changes to be blank again.