Graphite Design Overview
Graphite is divided into six python modules whose uses relationship is
shown in the software architecture diagram.
This document describes the modules in varying amounts of detail depending
on their importance in graphite.
It is hoped that this design overview will stimulate feedback from
potential users.
External Modules are modules which have been developed by others for
general use in the Python community.
Numeric is an established python module for doing numerical
mathematics.
Graphite uses the array manipulation provided by
Numeric.
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
functions.
These modules were developed for Graphite, but are stand-alone modules
which are generally useful.
The Property module describes a base class which contains
properties. Properties are object attributes with have restricted
values. Also, any subclass of the PropHolder base class disallows accidental
creation of new class members to
which is a common python problem. Each allowable member must
be declared in the list of properties for the subclass.
Currently a property can be an IntProperty, FloatProperty, EnumProperty,
BoolProperty, ClassProperty, or ListProperty.
To use this module, any class can inherit from the PropHolder base
class and list a set of Property objects it will contain. For each Property
object one must specify a default value and a help string. Special
restrictions just as a list for enumerated properties and ranges for
integer and float properties can also be specified.
Graphite contains many classes which are PropHolders. For example:
class LineStyle(PropHolder):
"""Class: LineStyle
Purpose: encapsulates settings used to draw a line
"""
# declare properties of this class
_properties = {
'width': IntProperty(1, "width of the line, in points", minval=0.1,maxval=10),
'color': ClassProperty(Color, black, "PIDDLE color of the line") \
}
def __init__(self, width=1, color=black):
PropHolder.__init__(self)
self.width = width
self.color = color
The StringFormat module allows for character-by-character formatting
of strings. It imitates the PIDDLE string drawing and
string metrics interface. The string formatting is done with specialized
XML syntax within the string. Therefore, the interface for the
StringFormat module consists of wrapper functions for the PIDDLE
string interface and various XML tags and characters.
StringFormat functions
- drawString(canvas, s, x, y, [font], [color], [angle])
- stringWidth(canvas, s, [font])
- fontHeight(canvas, [font])
- fontAscent(canvas, [font])
- fontDescent(canvas, [font])
StringFormat XML tags
- < b > < /b > - bold
- < i > < /i > - italics
- < u > < /u > - underline
- < super > < /super > - superscript
- < sub > < /sub > - subscript
StringFormat XML characters
- Greek Letter Symbols specified in
MathML
Graphite uses the StringFormat functionality to allow for formatted
text labels.
3D Primitive Module
Each Primitive subclass abstracts a 3D drawing element.
Currently Graphite has Box, Line, Symbol and Text Primitive subclasses.
Styles
Each Primitive subclass has an associated Style class.
This class encapsulates style information for the primitive like
font, color, width, etc.
Symbols and Lines
Symbols and Lines are two specific primitives.
To represent a wide variety of symbol and line types, Graphite will
have base Symbol and Line classes which can then be
subclassed. A number of built-in subclasses will be provided.
Each Symbol and Line subclass know how to draw itself given a
SymbolStyle or LineStyle respectively. Symbols are given a canvas
(x,y) coordinate. Lines are given a list of canvas (x,y) coordinates
to connect.
Example Builtin Symbol Subclasses
- CircleSymbol()
- BoxSymbol()
- LetterSymbol('A')
- ...
Example Builtin Line Subclasses
- DashedLine()
- DottedLine()
- DashDotLine()
- ...
Graphite use 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
Design
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.
Graph
A Graph is the object which ties everything together; it is a
combination of a Frame (Axes), 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 i
which need only data. A set of common predefined graphs will be included i
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 i
data. These are transformed by the axes into view/frame 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.
The Graph frame 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. This information will be specified in the Graph object.
PlotFormat
PlotFormat is a general category which will include many subtypes,
such as PointPlot, BarPlot, MeshPlot, etc. (See the
feature overview list for the
planned built-in plot formats). 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.
Dataset
A Dataset is an object which contains all the information needed to specify a single set of data points. 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.
Axis
An Axis defines the numeric range covered by one axis of the plot,
as well as that axis' 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.
Each Axis can specify a list of drawing locations. There will be
constants for putting axes along the graph frame. Datasets are mapped to
a set of 3 axes through an axis mapping. The list of axes mappings
are associated with Datasets with the same mechanism used in assigning
PlotFormats to Datasets (round-robin).
TickMarks
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).
http://Graphite.sourceforge.net/designoverview.html
Last Updated:
1/10/00
. . . . . .
mstrout@cs.ucsd.edu