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.

software architecture diagram

External Modules

External Modules are modules which have been developed by others for general use in the Python community.

Numeric

Numeric is an established python module for doing numerical mathematics. Graphite uses the array manipulation provided by Numeric.

PIDDLE

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.


Secondary Modules

These modules were developed for Graphite, but are stand-alone modules which are generally useful.


Property

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


StringFormat

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

StringFormat XML tags StringFormat XML characters 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

Example Builtin Line Subclasses


Main Graphite Module

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:
  1. create a Graph
  2. attach one or more Datasets
  3. attach or configure one or more PlotFormats
  4. add any overlays (extra labels, arrows, etc.)
  5. 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