A visualization back-end class is an interface to some external software package capable of rendering geometric drawings whatever nature, further called target software. It must conform to the expectations made by visualize function which supplies it with geometric primitives, on the other side it is responsible for starting the target software and cleaning up the resources (e.g. temporary files) after its termination.

Drawing functions

The conversation between the visualize function and the back-end class begins with a call to a new_drawing method. It must be a static method in the interface class, that is, it must expect the class name as its first argument. The second argument is optional, it may contain the title of the picture. (What this title can be used for is absolutely upon you.) The only required behavior is to create and return some object representing the coming picture. Depending on the kind of software you are interfacing, this object will eventually represent a window (in the GUI sense), a page of a document file, or something similar. Let's refer to it here as a window object.

This window object is now fed with all the geometric primitives making up the picture, one after another. For each primitive the draw method is called, thus it should be overloaded for every kind of geometric primitive the target software can show in a reasonable way. Moreover, all instantiations of draw must be declared as global methods, otherwise visualize won't ever make notice of the interface class.

In general, the window object should be able to consume several geometric primitives and draw them on the same page (or in the same GUI window). This ability must be expressed by the trailing @ token in the draw method signature. This trailing argument will never get any senseful value, but its declaration signals to the visualize function that the interface package can really be used for making composite drawings. In the unfortunate case the target software can't combine several objects into one picture (or, as in the case with the Gale diagrams, there is just no point in drawing several of them together), the signature of the corresponding draw method must lack the @ token.

Note that each draw method can analyze the received geometric primitives and convert them into the target representation, but it should not undertake any user-visible actions like starting the visualization software. The latter should be postponed to the end of the user cycle, as described in the following section.

Session management

In general, the interface class should expect the new_drawing method be called several times during one user cycle. Depending on the capabilities of the target software, this can result in the corresponding number of GUI windows being popped up, creating a document file containing several pages, etc. If the target software does not support such multi-view configurations, your interface must arrange for running it in several parallel processes (if even this is not possible, e.g. due to a limited number of free licenses, your interface should die with an appropriate message, but not try to draw a partial picture). In both cases the real start should be postponed to the very end of the user cycle; the only duty of the new_drawing function in respect of the session management is to register an interface instance in the at_end activity list of the $main::scope variable. The repeated calls to new_drawing must recognize the existance of this instance and append the new window objects to it.

Eventually, at the end of the user cycle, the method finish of the interface class will be called. This is the right place to start the visualization program (e.g. using BackgroundProcess class).

For the simplest case of target software not being capable of managing many views at once there is a useful class SimpleViewer which implements almost all of the formalities. If you derive your interface class from SimpleViewer, the only method you have to write (besides of draw methods, naturally) is command which constructs the command line with all necessary switches and arguments. Have a look in the apps/polytope/rules/splitstree.rules for an example of its usage.

Even in more complicated cases the code of SimpleViewer can help to understand the details of interaction with the session and scope management. It can be found in modules/common/perllib/BackgroundViewer.pm.

Software organization

The minimal requirement to a visualization back-end class is that it should be defined in a separate rule file, starting with a CONFIGURE section. It should check the presence of the target software (when appropriate, the minimal required version too), and in the case of a failure disable the entire rule file by throwing an exception with an explanation text. The rest of the rule file can contain the overloaded draw methods, the new_method function, and internal stuff whatever kind. If the internal functions become fat, you can put them in a separate pm file (or even several files) in the perllib subdirectory and require it as usual.

If the interface can be used from different applications, it is advisable to split the rule files in a common and application-dependent parts. Then the common conversion routines, session management, auto-configuration, and draw methods for basic geometric objects like Visual::PointSet or Visual::Polygons, come into modules/common, while the draw methods for more special objects like Visual::Polytope come into the application where they are defined. The application-specific rule files should start with a little configuration section declaring their dependence on the common part. For example, if you give all rule files the same name (which is anyway recommended), the configuration statement looks like this:

CONFIGURE : common::

The common rule file should also introduce a preference label for the interface. The user should be able to choose the target software consciously, not relying on the occassional order of rule inclusions. Assign this label, refined by appropriate sublevel labels, to all draw methods defined in the interface.