Graphite Design Overview
This design overview specifies the high-level software architecture
for Graphite, and a class diagram for the main module.
It is hoped that this design overview will stimulate feedback from
Graphite is divided into 5 python modules whose uses relationship is
shown in the software architecture diagram.
A brief description of each module follows:
Numeric is an established python module for doing numerical
mathematics. Graphite uses the array manipulation(???) provided by
PIDDLE is a python module for generating cross-platform
two-dimensional graphics. It provides Graphite with a large number of
backend formats such as PostScript, PDF, GIF, TK, QuickDraw (Mac),
Windows, etc. All graph objects are described in terms of 3D
primitives which are then translated into PIDDLE canvas drawing
The Properties module describes a base class which contains
properties. This class disallows accidentally creating new members to
the class which is a common python problem. Also, various properties
of a class can be specified with a list or range.
For example, the line style property which a Line class would have a
list of possible values: hashed, dotted, etc.
The String Format module will allow for LaTeX like
character-by-character text formatting.
The main Graphite module uses an object-oriented approach to divide a complex graph into simpler parts which can be manipulated individually.
It is not generally necessary for users to understand the details of each object class, but a rough idea of what the classes are and do will often be helpful.
The main classes of Graphite objects are shown in the
class diagram, using the same notation as in
Patterns. Detailed understanding of the diagram is not necessary for this discussion, but for the sake of completeness, a brief description of the notation is as follows. Classes are shown as boxes, with the name of the class in bold type. Important data members and functions are shown in the lower part of a box. Arrows connecting boxes indicate relationships between the classes. A filled circle at the head of an arrow indicates a one-to-many relationship. A diamond at the start of an arrow indicates a part-of (aggregate) relationship.
A brief description of each class follows.
A Graph is the object which ties everything together; it is a combination of a Frame, one or more Datasets, one or more PlotFormats, and possibly some extra drawing primitives as overlays. It has a draw() method that draws the graph into a PIDDLE canvas (and from there, it may be displayed on screen or exported into a variety of formats). The general schema for creating graphs is as follows:
- create a Graph
- attach one or more Datasets
- attach or configure one or more PlotFormats
- add any overlays (extra labels, arrows, etc.)
- call the Graph's draw() method
In addition, a Graph can be cloned and pickled (i.e., saved to disk and later reloaded), allowing one to define standard graph layouts which need only data. A set of common predefined graphs will be included with the standard Graphite distribution.
One should note three separate coordinate systems used by Graphite. There are data coordinates, i.e., the space occupied by the raw data. These are transformed by the axes into view coordinates, which range from 0 to 1 in X, Y, and Z. Finally, view coordinates get converted to device coordinates. These distinctions are important because some positions are specified in data coordinates, while others are specified in view or device coordinates.
PlotFormat is a general category which will include many subtypes, such as LinePlot, ScatterPlot, BarPlot, MeshPlot, etc. There are some common attributes (such as line style), and some unique to each type; all PlotFormats have sensible default values for all attributes. PlotFormats are attached to the graph in an order that corresponds to the order of the Datasets applied to the same graph. If there are fewer PlotFormats than Datasets, the PlotFormats will be cycled through as needed. Because PlotFormats and Datasets are attached separately, it is easy to change the format of a graph without changing the data, or vice versa.
A PlotFormat works by taking a Dataset given to it by the Graph, and returning a set of 3D drawing primitives. These are then combined with all the other primitives and rendered. To extend Graphite's capabilities with a new plot type, it should suffice to create a new subclass of PlotFormat; all details of data handling, rendering, and so on will be handled by other classes.
A Dataset is an object which contains all the information needed to specify a single set of datapoints. It is a very simple object which is really little more than a container for numeric sequences. It has fields for the common uses of a sequence, e.g.: x, y, z, yerr, etc. At its purest level, a Dataset is prepared by assigning a sequence of numbers to each relevant field (e.g., for a scatter plot, you'd assign one sequence to x and a corresponding sequence to y). However, we expect to have "shortcut" methods which will allow you to quickly create common Datasets, e.g. from a two-dimensional array.
Each Dataset is associated (by position in the Graph) with one PlotFormat object. So to make a graph with two different data formats (e.g., two lines, one in thick red and the other in dashed green), there would be to Datasets and two PlotFormats. A Dataset is sometimes called a "data series" in other graphing packages.
In addition to storing numeric data, a Dataset may also store a function which will be sampled at graph time to obtain the numbers to plot. This takes advantage of Python's functional programming capabilities, and is often useful for generating a quick graph of a function without manual sampling.
The Frame of a Graph defines the look of the graph boundaries. This includes the eye (camera) position, whether the a perspective projection is used, whether the graph area should be enclosed in a box, and so on. The frame also includes several Axis objects; at least one for the x, y, and z axes, and sometimes an x2, y2, and/or z2 (secondary axes).
An Axis defines the numeric range covered by one axis of the plot, as well as that axis's appearance: line style, TickMarks, and label. Note that one Axis can have many sets of TickMarks, each with a different size, style, and label; this is how Graphite implements major and minor tickmarks and grid lines.
A TickMarks object defines a set of tick marks for one axis. Tickmarks may extend an adjustable distance (given in view coordinates) inside and outside the graph frame (including all the way across the graph, for making grid lines), and may have labels. By default, numeric labels will be generated based on the axis range.
In a 3D graph, each tick mark is actually two lines, extending in the two directions orthogonal to the axis. For example, tick marks on the X axis extend both in Y and in Z (see Sample Line Plots for an example).
. . . . . .