Coordinate Systems for 3D Graphics

In this section we will talk about a simple wireframe graphics system. We will use this to illustrate points about transformations. The code is in the directory source code.
This first topic was the Java Vecmath packages. We used this has both an example of what javadoc produces and as an introduction to inheritance. This files are in ~bellenot/jdk1.2beta4/java3d/html on math and cs, and they are on-line at Sun.

In particular we looked at javax.vecmath's Tuple3f and its subcases Color3f, Point3f, TexCoord3f and Vector3f. The Vector3f will be heavily used in the example. Inheritance allows us to use Tuple3f methods like epsilonEquals and scaleAdd on Vector3f objects. We also make use of Vector3f methods of traditional vector analyis like the dot and cross product or normalizing vectors to unit length.


To summarize about the Device.java code. We saw several Java features, in particular interfaces, Vectors (dynamically resizing arrays not our 3D Vector3f's), and some basic Swing (java gui code) which was not really explained. The main feature of the code was all the transformations from model view to world view to view view to device coordinates. Most of this code can be stolen for the first project


This has ended being more than a single lecture. I have added some more files to our source directory. Some folks have been having asking questions about when and why to make an interface. So I have tried to add some examples to show how interfaces are useful.

The first example is Spin.java. I copied the code in Device.java changed all the class names from Device to Spin and "improved" the code in two ways.

In the main program, I used a TorusKnot (in TorusKnot.java) instead of the Helix in Device. By using the interface Function3f in the ParameterCurve constructor, no changes were needed in the ParameterCurve class. [If I didn't have the Function3f interface the constructor would have had a "Helix" parameter.]

The second improvement, is the Spinning ViewPoint. This is done by adding a thread and implementing Runnable. This requires implementing a method "public void run" which basically just calls rotate. the rotate method rotates the viewPoint around the origin and keeps the viewNormal pointed towards the origin. Again no changes are needed to the ParametericCurve draw method because we used the WireMesh interface. [Again if I didn't have the WireMesh interface, the draw method would have had to have a Device parameter.

The second example uses inheritance (A stronger link than sharing an interface.) I copied Spin.java to Spin2.java. Rather than implementing the WireMesh interface, I made Spin2 a subclass of Device. [The code says it extends Device2 and not Device, but we will see why later.] Since Device is a subclass of JComponent, Spin2 is still a subclass of JComponent. Since Device implements WireMesh, Spin2 implements WireMesh. So the next step was to delete everything in Spin2 already in Device with the exception of the main routine.

If the draw method in ParameterCurve used a Device parameter, then passing it a Spin2 would have still worked. This is because a Spin2 is also a Device. [In the actual code, the parameter is a WireMesh and Spin2 is one of those two.]

So why the Device2 class? In is instructive to run the unix diff utility here. Try diff Device2.java Device.java. You will notice two kinds of changes. The trivial name changes from Device to Device2 and the key changes in the type of methods. Methods Device2 that we need to access in Spin2 had to be changed from private to protected. Private means "my class only", protected means "my class and my subclasses only".

Before leaving Spin2, we need to notice that it exposes a weakness. My goal was to change as little of the source as possible. My goal should have been, to change the source the best way possible. In this case, the angleCount field and the rotate method should have been added directly to Device. Now none of the old methods needed to be changed from private to protected. That is rotate should belong in the base class Device and not in the subclass Spin2.


The Runnable interface is used to implement the spinning of the viewpoint in Spin and Spin2. Near the bottom of main, I create a new Thread which will use the run method from Spin (or Spin2). The new Thread is started with the t.start() command, but the current thread does not stop, it goes on to execute the next line of main.

The new thread eventually calls Spin::run, the first step in run is to get a reference to the new Thread object. At this point it is the new thread running and not the old one which is continuing to execute the main function. The new thread sleeps for 300 milliseconds, then rotated the view and next tells the JComponent super class that the current drawing is out of date. There are many different repaint methods in the super classes of Spin. I believe this repaint does actually call the painting routine, but others meerly label a region as dirty. A third thread would later call the actual paint routine. There are always several threads running in any Java execution.

Note that the frame rate is small (less than 4 frames per second) and the spinning looks jerky. Try decreasing the sleep time til it looks "smooth". (Well there are other motions here too.) TV is around 30 frames per second, but smaller frame rates will often seem smooth.