Supply leading comments with a short descriptive text.
Visualization function should be neutral in respect of the rendering software, even if you have one concrete software package
in mind at the time you are writing this function. Don't call any renderer, but create geometric primitives like
Visual::Graph or Visual::Polytope; if their capabilities don't suffice, define your own ones (see below the specific
notes about this).
Let the user as much flexibility in the appearance control as possible. Many geometric primitive classes define decoration
attribute lists; declare them as optional arguments of the user function and pass the attribute lists to the constructors of
the geometric primitive objects. See e.g. VISUAL how to code this.
The last executed statement of the visualization function must be the either visualize($vis_object) , if it creates
a single visual object, or compose(...) with several geometric primitives making up the picture. Homogeneous arrays of
geometric primitives (that is, containing objects of the same type) can be passed via reference, this will significantly speed up the processing.
If you want to let the user add some optional details to the picture, try to define supplemental methods in the geometric
primitive classes (see e.g. VERTEX_COLORS) instead of introducing optional arguments or even separate visualization
functions. The Visual::Container class is especially useful for deriving geometric primitives rich on supplemental
methods. If you find optional arguments a better solution, specify the whole argument list in the args: line in the leading
comments of the function and describe each argument. The decoration attributes don't need to be documented, this is made
automatically.
If you decide to introduce a new decoration attribute for some geometric primitive class, try to make it not too specific for
a single back-end software. Inspect the draw methods for this primitive in all back-end interfaces and add the handling for
the new attribute whereever possible.
Introducing a new geometric primitive class, you should keep in mind the following:
All geometric primitives have to be derived, directly or indirectly, from Visual::Object. Since it is implemented via Struct, the derivation definition must be expressed using Struct too:
package Visual::MyClass;
use Struct [ '@ISA' => 'Visual::Object' ], # or 'Visual::OtherClass'
'$my_field1', '@my_field2', ... ;
Visual::Object is a very abstract class: it solely defines the field $Name holding the title of the drawing, as well as
the array @attached keeping other geometric primitives making up the picture. The latter is filled by visualize function
and should not be accessed outside of it.
Decoration lists are automatically inherited (due to the namespace mode). If you want to
add new attributes, you should redeclare the list:
Unless your new geometric primitive has very special purpose and can only be processed by dedicated back-end software package,
inspect all back-end interfaces existing in polymake and try to implement the draw methods wherever possible. Think of the
mean polymake user who cannot afford to install all the same third-part software as you have.
Collections of geometric primitives
Visual::Container is an abstract geometric primitive which can hold any number of other
geometric objects of arbitrary types (or anonymous arrays thereof) along with default values for their decoration attributes.
As long as supplemental methods are expected to be called on the collection, it is kept together.
As the container object eventually comes into the mouth of the visualize function, it simply opens itself and hands over the
elements contained in it as if they were passed to visualize separately. Deriving application-specific classes from Visual::Container you can easily control which supplemental methods can be called for various "basic" types of pictures.
A Visual::Container object can be created with a constructor call: