Plotter package

Given a data set, one would like to represent that data with a 3-D object, and there are obviously a very large number of ways to do this. What we want is to capture some abstract ideas that will make this task easier. Some things one would like to do:
The current implementation is closely tied to the Java3D library, but some effort has been taken to avoid explicit references whereever possible. The three essential classes are ObjectGroup, PlotStrategy, and PlotCommand.

An ObjectGroup object wraps the constructed 3D representation. The idea is to provide three levels of access. The application level can toss around ObjectGroups without knowing anything about how they are constructed. The viewer can extract a 3D representation suitable for sending to the renderer. Finally, specific tools can access and manipulate the internal structure of the representation to make incremental adjustments to the 3D representation.

A PlotStrategy object defines how to construct a 3D representation of the data. PlotStrategy itself is an abstract class that defines generic building methods. These methods are overridden by subclasses to produce custom visualizations. The components of a PlotStrategy are enumerated below.

The PlotCommand object mediates communication between the data source and the plotting routine. A PlotCommand contains a DataReader, a predicate, and a PlotStrategy. PlotCommand provides methods identical to DataReader's data access methods but without the predicate. A PlotCommand builds an ObjectGroup by calling its PlotStrategy with a reference to itself; data access commands are passed on to the DataReader after appending the predicate. In this way a PlotCommand completely encapsulates a 3D representation of a particular subset of a particular data set. The ObjectGroup is itself stored in the PlotCommand for later reference. In future versions, the PlotCommand will be the key functional object.

PlotStrategy components

Building a 3D representation of data, as described by the PlotStrategy object, can be broken down into some components that are generically useful. The following components are currently defined:

PositionStrategy
returns the (x,y,z) positions of the objects to be plotted.
PolygonStrategy
returns the locations of the vertices of a polygon. These can be combined with the positions from a PositionStrategy to represent every object as a complex ploygon.
ColoringStrategy
returns a color for each object
AlphaColoringStrategy
an alpha color contains 4 numbers: the usual rgb values plus an alpha value. Alpha values are useful when two objects would overlap on the screen. Instead of simply showing the closest one, the alpha values can be compared to determine which object is more "significant" and should be plotted. An AlphaColoringStrategy takes a set of normal colors (from a ColoringStrategy) and adds an alpha value for each object.
AppearanceStrategy
An Appearance object defines various rendering parameters for a 3D object, in constrast to vertex-by-vertex parameters. An AppearanceStrategy returns an appropriate Appearance object. These usually have more to do with the plotting strategy than with the data set.
A PlotStrategy uses concrete instances of some or all of the above classes to help it do its work. A PositionStrategy and AppearanceStrategy are mandatory; the others are optional. Additional components will likely be added in the future.

A PlotManager object stores PlotStrategy objects keyed by name. This is a convenient way to store PlotStrategies for later use. A PlotCommand can be constructed with a PlotManager instead of a PlotStrategy, and the correct PlotStrategy to be used will be dynamically determined by examining the supplied predicate.

Exception handling

All kinds of terrible things can happen when trying to assemble a 3D representation. For example, suppose that a call for positions returns 1000 positions, but a call for colors returns only 998 colors. What should happen now?
First, we've adopted the standard that everything should be checked whereever it could matter. This means that an assembler should check everything coming from its components before assembling. In particular, all plotter classes should check any incoming data for null values and empty arrays before trying to use them. In practice the cost for this is low, since checking a large array is much faster than building it in the first place.
Second, the plotter should not throw fatal errors and crash the system. If something bad happens, the user should be notified as to what went wrong, and the plotter should try to go along as best as possible. One way this is handled is via the PlotterMessageHandler. This is a singleton class that provides methods for handling messages and exceptions in a uniform way. The default behavior is to simply write a message to stdout, but this can be easily reconfigured. Classes in the plotter package should route all their exceptions and complaints to this class and carry on. For example, in the above case the builder should send a warning message to PlotterMessageHandler, then set the first 998 colors and leave the other two missing. This carries all the way to the top application level, where scenes should be checked for non-zero content before passing them on.
This is not the only way to handle errors, and possibly not the best way, but we've decided on this as the most reasonable for now.

As you can probably see, there is still room for more design in this package. Its structure is continually revised by incorporating new abstract ideas. Much of the current design is chosen to allow these future revisions without forcing a complete rewrite of the whole package.
Home   NVisF   Design
This page last updated Jan 2 2001.