Plotting with Data Pipelining.. The Crude Introduction
This is a prototype component of an efficient, compact, general-purpose 2D plotter written entirely in Java. It's based in part on the data pipeline approach used by the Visualization Toolkit, which itself does rather well at handling medical datasets easily and efficiently. Considering the size of the datasets intended for this library, and the interpreted nature of Java, any little bit of speed counts.
Getting more into the guts, the overall block plan of this demo looks like this:
Heck, let's take a peek at the source code implementing the diagram:
import java.util.Observer; import java.util.Observable; import java.awt.*; import java.applet.Applet; class DataTestSource extends DataSource { public double [] data = { 0,1, 1,2, 3,5, 6,12, 8,4 }; /* just a bunch of tuples squished into memory */ public double [] extent = { 0,8, 1,12 }; /* X extents, Y extents */ public int[] dim = { 2, 5 }; /* 5 2-tuples */ public double[] getData() { return data; } public double[] getExtents() { return extent; } public int[] getDimensions() { return dim; } /* if the data ever changes, just call setChanged(); notifyObservers(); to start a refresh signal to all the clients of the DataSource */ } public class test1 extends Applet { DataTestSource dts = new DataTestSource(); CanvasView screen = new CanvasView(); DataLinearScaleFilter scale = new DataLinearScaleFilter(); DataXYLineRenderer painter = new DataXYLineRenderer(); public void init() { super.init(); // honor thy parent class setLayout( new GridLayout(1,1) ); // one big Canvas in our applet. later, make it more interesting scale.SetSource( dts ); // maximize to available extents of the dataset scale.SetDestination( screen.dataView() ); // scale the data to the extents of the window we're painting // whenever window changes size, the LinearScaler on out // to the screen recalculates (doesn't happen when we're the applet, though) scale.SetInput( dts ); // plug the data into the rescaler painter.SetInput( scale ); // make polygons from the rescaled data screen.SetRenderer(painter); // and draw that on a canvas add( screen ); // throw it in the applet container } }
Individual components can optionally maintain caches, in order to minimize how far back down the pipeline you have to reach, in order to update your screen. For example, the Renderer keeps a list of lines and points to plot, and only regenerates it when it gets a signal from its server (the LinearRescaler in this case). If you only change the color of the rendering, the source and rescaling filter never get called. The LinearRescaler only throws out its cache of rescaling constants if there's a domain change and a client asks for an update.
Getting on to more interesting steps, let's throw in some labelling:
Add a few classes to make the Canvas' mouseMove look like another DataSource, throw in a LinearScaler going the other direction, and hook it to a second LabelAdapter (you don't want the mouse to update all the labels!) and you get this, more useful graphing applet:
The design for this one, while significantly more featured, is graphically tractable: click for diagram
Test3 includes a Scaling dialog and a zero line.
The scaling dialog is a filter which allows the user to change its Extents, and passes through everything else. The Zero line filters the incoming Data, prepending it with (xmin,0) (xmax,0) (nan,nan) in order to create a zero line broken from the rest of the dataset.
In any case, if you'd like to look at all the source code, and/or play around with implementing Filters and Sources and Renderers, the source can be found here. Enjoy!
RayG $Id: $