For each program component, a figure illustrates the classes which comprise the component, and their relationship among each other. Objects are generally represented as rectangles labeled with the class name. Classes which are derived from one or more parent classes have a solid arrow pointing from the derived class to the parent class (an is a relationship). Classes which use some capability of another class, but are not either derived from nor contain the other class, have a dashed line pointing to the class which is being used (a uses a relationship). Classes which contain one or more instances of another class indicate this by having the rectangle for the contained class located within the containing class (a has a relationship).
VMD has a few important base classes, for which a single global instance is created either during program initialization or as a result of a user request. For several of these base classes an instance of a specialized class derived from the base class is created, and the address of the instance assigned to a pointer of type base *, with the instance accessed through virtual functions. In the figures describing each program component, important base classes are indicated in bold font. If a global instance of a base class exists, the name of the global variable is shown in typewriter font in parentheses below the name of the base class in the figure.
Figure 5.1 illustrates the utility objects used in VMD program development. Several of these objects are C++ templates; instances of these classes are creates for different types, for example a Stack of integers or a Stack of char * items. These template and the other global objects are each detailed in section 6.2.
The Inform class is deserving of particular notice, since it is one of the most widely used objects in VMD . This class provides a streams-like object which is used to print messages to the VMD console. There are four global instances of this class, which are used for the following purposes:
Used to display informative messages, which provide some data or message to the user which is either requested or ``typically'' provided. For example, the messages describing the current status of the process of reading a molecule from a file or a network connection are printed by sending text to the msgInfo instance of an Inform object.
Used to display warning messages. These are messages which do not indicate a fatal program condition, but which caution the user that things may not be going as they expect.
Error messages are displayed by sending text to the msgErr instantiation.
Where debugging messages are printed. It is possible to exclude the source code lines which print debugging messages when VMD is compiled, to produce a smaller and faster executable, by eliminating the DEBUG compilation option.
msgInfo << "This is message number " << intVariable << "." << sendmsg;The sendmsg manipulator ends the message; this will cause the message to be printed to the console, prepended by a string indicating the type of messages, and appended by a newline.
There are also several utility functions which are not part of any class; these are described in section 6.1.
Figure 5.2 illustrates the structure and relationship of the many objects which are used by VMD for storing and rendering graphical objects. These objects are described in detail in section 6.3. Images are displayed through the use of four main objects (and subclasses thereof):
DisplayDevice is the base class for objects which do the actual rendering of a Scene . Each derived class of this parent must provide versions of virtual functions which prepare the device for drawing (i.e., clear a screen or open a file), render a list of drawing commands, and update the display after drawing, among many other functions. This class is where library-specific drawing commands are encapsulated, for example there exists a GLDisplayDevice subclass which is used to draw images using the GL graphical library from Silicon Graphics, Inc. A DisplayDevice can also be defined which renders a Scene to a file instead of a monitor, i.e., to a postscript file, a raytracing program input script, or a bitmap image file.
DispCmd objects are used by Displayable objects to construct a list of drawing commands (tokens) in a format which can be processed by a DisplayDevice . When rendering an image, a DisplayDevice does not work directory with a Displayable ; instead, the DisplayDevice is given a simple byte array into which drawing commands have been assembled. Each Displayable contains one or more of these drawing lists; they are stored by the Scene and processed by the render routine of a DisplayDevice . The DispCmd objects act to put the data into these drawing lists in the proper format for the operation specified. Each DispCmd object appends its data to the current end of a provided drawing list in this format and order:
Each Displayble object contains a display list, as well as information about the item to be drawn such as whether to display or hide the object, it's current transformation (how much to rotate, scale, and translate the object), whether it is a 2D or a 3D object, whether it is fixed (set to ignore any requests to scale, translate, or rotate the object), and a list of children. A Displayable registers with one or more Scene objects, so that the Scene then will contain a list of all the Displayable objects which should be drawn when requrested. The Scene is responsible for providing the physical memory storage for a display list, through a request by a Displayable . Each molecule and other graphical item in VMD is a Displayable subclass. These derived objects supply the methods to fill the object's display list with drawing commands for the item to be drawn, i.e., the commands to draw the points for each atom and the lines for each bond in a molecle.
A key feature of each Displayable is that it may contain any number of child Displayable objects, and may also be a child of some other parent Displayable . Each child may be individually translated, rotated, etc., and may be turned on or off individually; but operations to a parent such as a transformation typically affect all the children of that parent as well. For example, if a parent is currently being hidden, so will be all the children of that parent. Also, only Displayable objects which have no parent register with a Scene ; all children of a parent are drawn properly when the parent is drawn, so it is only necessary for the very top-level parent to be stored in the Scene .
Since it has been discussed quite a bit already, it is somewhat obvious at this point that Scene objects are used to maintain a database on what should be drawn to a user-specified DisplayDevice . A Scene contains routines for applying transformation such as rotate, scale, etc. to all the (non-fixed) Displayable objects which have registered with it, and contains routines to manage the memory used for display lists. Each Scene contains actually two categories of lists of items, and in each category there are two lists, one for 2D objects and one for 3D objects. These two types of lists are:
A Scene is the object primarily responsible for collecting all the objects that are to be drawn and for giving these objects to a DisplayDevice for rendering. This is done as follows:
As shown in figure 5.2, there are many classes derived from Displayable , and actually in fact from Displayable3D . Global Displayable objects of note are LightList , which contains a list of Light objects; Axes , which displays a set of XYZ axes in a corner of the display; Stage , which displays a checkerboard panel to one side of the objects in the Scene ; VMDTitle , which displays the VMD title credits and rotating letteres; and ColorList , which is described next. The Light objects each represent one light used to illuminate the objects in a Scene which have defined material characteristics for their surfaces.
Colors in VMD
are handled mainly by the ColorList
object. This maintains a list of 16 unique colors in the VMD
``colormap'', as well as a color scale of 32 colors arranged in a selectable pattern of bluegreen
red or blue
white
red. For each color there are two versions, a solid color and a semi-transparent color. Each color may be changed through user commands, and for each color there are corresponding material characteristics which are used when solid objects are drawn and the Displayable
drawing the solid objects requests that materials be used. The ColorList
also maintains several lists of names which are used to specify a color: using the NameList
template, a set of color categories are stored in the ColorList
, and for each color category there are any number of color objects, which consist of a name and index. For example, in the color category ``Stage'' there are two objects, an ``Even'' object (for the even-numbered stage checkerboard squares) and an ``Odd'' object (for the odd-numbered squares). The index for these objects indicates which of the 16 VMD
colors to use to color that square. Displayble
objects may request to have new categories created, and to add new color objects to each category. This capability is provided by the ColorUser
class, from which each Displayable
is derived, thus all objects can automatically access the colors through the functionality of the ColorUser
class.
The action of picking graphical objects with a pointer, like clicking on an atom with the mouse, is handled by the Display objects as well. Picking objects requires that VMD know where all the points are in space that may be selected, where the pointer is located when a button is pressed, and what to do once an item is picked. This is managed by objects derived from three `picking' base classes: Pickable , PickList , and PickMode , which are discussed below.
Each Displayable object is derived from the Pickable base class; a Pickable is an object which contains a drawing list with special DispCmd tokens which indicate ``this is a point in space which may be selected''. These picking tokens do not result in anything being drawn on the screen; they are used by the Displayable::(void *) routine to determine if a pointer is over a selectable item. Each picking token contains also an integer tag, which is returned by the picking routine when the pointer is found over an item. When a Displayable wants to put picking tokens in its display list, and then wants to be have its display list be checked for pickable points, it must register with a PickList object (described below). This is done by calling the routine Pickable::_with_picklist(PickList *)
in the constructor of the Displayable , using the pointer to the Scene in which the Displayable is registered (this is because Scene is derived from PickList ). There are several virtual functions which must be provided by an object derived from Pickable , which will be discussed below.
There are two types of pointers which may be used to pick items, 2D or 3D. 2D pointers (i.e. the mouse) report their position in `relative scaled coordinates', that is, the X and Y position of the mouse is provided to the rest of the program as values in the range 0 ... 1, so that the lower-left corner of the graphics window is (0,0), and the upper-right corner is (1,1). The coordinates of 3D pointers, on the other hand, are given to the rest of the program as just the location of the pointer in 'eye' coordinates, i.e. the 3D position after it has been transformed by any internal transformation matrix of the pointer. Each pointing device (the mouse, 3D spatial trackers, etc) must be in a certain picking mode, which determines what action is done when an item is picked; new modes can be easily added to a central source by any object that wants to extend the usability of the picking mechanism. A picking operation consists of three phases:
Each picking mode is embodied by a special derivation of the PickMode abstract base class. An example, PickModeQuery , has been added which does the very simple job of just printing out the name of the Pickable object when the picking is ended (and only if the pointer position does not move much between the start and end). Each PickMode simply contains three virtual functions:
A PickList object contains a list of all the current picking modes which the mouse may be in; these all have unique id's from 0 ... num_modes - 1. The Mouse object (described in a later section) gets this list and adds a submenu to the graphics window pop-up menu. When the mouse is in a picking mode, the cursor changes to a crosshair. The Scene is derived from PickList ; it is the PickList which maintains all the coordinating data to manage all the objects which can be picked, and all the different picking modes. The PickList maintains two lists:
To set up a Displayable to operate properly as a useful Pickable , these things must be added to the Displayable (see Axes.C and Axes.h for a good example of how to do these things).
A key thing that may be needed during the pick_move phase for both Pickable and PickMode objects is the ability to convert a 2D screen coord (the relative scaled coords x and y, valued 0 ... 1) to a 3D world coordinate. The difficult of course is the abiguity in what the Z-axis coordinate should be. The routine DisplayDevice::_3D_from_2D(float *A3D, float *B2D, float *B3D)
takes a 3D world coordinate (at point A, A3D), and a 2D relative scaled coordinate (the screen position of point B, B2D), and returns the 3D world coordinate for point B (B3D). This works assuming the eye is looking along the Z axis. The coordinate returned is the point where the line formed by the 2D's projection back into 3D space intersects the plane parallel to the XY plane which contains point A (i.e. the point will have the same Z-coordinate as the given point A).
The objects used for displaying and rendering graphical objects in VMD are quite general, and can be used to draw essentially anything. The objects used by VMD to create, store, and manipulate molecules, which are illustrated in figure 5.3, are much more specific to the purpose of VMD , which is to visualize to dynamic properties of biopolymers (in particular proteins and nucleic acids). The heart of this category of object classes is the Molecule class, which is actually inherited from a number of base classes and for which several subclasses exist. MoleculeList is an object which maintains a list of all current molecules. There are also several helping objects which store data about particular components of each molecule.
At the very top level of the Molecule hierarchy is the Animate class, which stores a list of Timestep objects. Nothing is known about the molecule at this level other than the number of atoms; a Timestep stores simply arrays of floating-point values for each of these atoms for each discrete timestep in the trajectory of the molecule. The Animate class also maintains the current frame in the trajectory, and the direction (i.e., fast-forward, reverse, pause) and speed of animation. The Timestep objects, one for each frame of animation in each molecule, are stored simply as pointers in a ResizeArray instance within the Animate object. A Timestep is currently quite simple, and stores data as publicly-available floating point arrays which are allocated when a new Timestep is created (or for some data which may be optionally stored for each step, by the users request). This is done primarily for speed since this data is accessed quite often. It may be helpful to improve this class in the future, by making a much more general method to store different types of atomic data for timesteps which would not require a change to the Timestep class each time. For example, the Timestep may just store a list of pointers to something like a TimeStepData class instance, where each TimeStepData would store some number of floating-point values.
At the next level, the BaseMolecule object is inherited from the Animate object. This object stores all the basic information which comprises the structure of the molecule. Data about the coordinates are stored by Animate , while BaseMolecule stores how the atoms are connected, what residues and segments exist, etc. When it is created, a BaseMolecule is empty, indicating no atoms or anything present. A virtual function BaseMolecule::() is used by BaseMolecule and all other class derived from BaseMolecule ; this function is called when a new molecule is to be created, and derived classes do their creation tasks after which they call create for the parent class. BaseMolecule does NOT contain any data or functions for the drawing of the molecule, just for storing the structure. After a new molecule has been read in from some files or from a network connection, the structure of the molecule is analyzed and stored in a retrievable format. Several small classes help in this storage; they include the following:
This object stores the data for one atom in a molecule, including the name, segment, type, charge, mass, etc. A list of these Atom
objects are stored in a BaseMolecule
. Atoms are numbered 0 N-1, where N is the number of atoms in the molecule; a pointer to the nth Atom
object may be obtained through the routine * BaseMolecule::(
)
. Bonds are not stored as separate objects; instead, in each Atom
object there is noted the number of bonds in which the Atom
participates, and a list of the indices of the other atoms to which it is connected. Note that a bond is effectively stored twice (to speed rendering of the molecule), once for each atom which defines the bond.
Each residue such as an amino acid or nucleic acid is referenced by a single Residue object, which stores the indices of the corresponding atoms and information about how the residue is connected to other residues. A list of all the residues in a molecule is kept in the BaseMolecule class.
A fragment is defined as a contiguous string of residues. This may be as short as one (i.e., a water molecule), or as long as an entire protein from N-terminus to C-terminus. A Fragment object stores a list of residues which form a fragment, noting their order and connectivity. A BaseMolecule stores a list of all the fragments which are found within the molecule.
A BaseMolecule also contains several lists of names which are basically NameList templates. They store the lists of unique names which occur in the molecule, i.e., the list of all atom names. These lists are maintained as public data member for speedy access by other parts of the program.
From BaseMolecule and from Displayable3D the DrawMolecule object is derived. This level of the Molecule hierarchy stores all the information about how to draw the molecule. A molecule in VMD is drawn as a composition of one or more representations of the molecule structure, which are contained within a DrawMolItem object (described later). A DrawMolecule stores a list of all the different representations ( DrawMolItem ) of the molecule that the user has selected, and contains routines to add, change, or delete these representations. Since it is also a Displayable , a DrawMolecule can specify its own display list, but currently all molecule drawing commands are contained within DrawMolItem objects.
A DrawMolItem is also derived from Displayable3D ; its function is to maintain the display list with the proper drawing commands to render one specified representation of a molecule. When a new DrawMolItem is created, it is given the molecule for which it is to render an image, and instances of the following three objects which describe exactly what the representation is to be:
Stores the color index number which is to be used for each atom in the molecule when it is drawn; essentially, how to color the molecule. The coloring can be done in any number of ways, for example each residue a different color, or shaded through the color scale from the midpoint of the molecule outwards.
Stores what shape to draw the molecule as; essentially, how to draw the molecule. For example, bonds may be represented as thin lines, or solid cylinders, or not drawn at all.
Which atoms to draw. This objects takes as input a text string with an atom selection command, and determines from the string which atoms of the molecule the user wishes to have drawn.
The main base class Molecule is then derived from DrawMolecule . This is the level at which most other objects in VMD work with molecules, as pointers to instances of a Molecule class. In fact relatively little functionality is includes at this level. What this class does do, in fact, is provide the routines for reading in or writing out of animation frames from or to different coordinate file formats (i.e., PDB or DCD files). This is done through the use of a CoorFileData object, which encapsulates the information on how to read/write such a coordinate file (this includes storing which frames are to be read or written, the coordinate file format, and the current status of such an operation). Since Molecule is a subclass of Displayable , it has a prepare virtual routine which is called each time the Scene is to be drawn to the current DisplayDevice . Molecule uses this call to prepare to read/write a single coordinate set from/to the current coordinate file, if one is being processed. Thus, a coordinate file is not processed in one single operation, instead one frame is processed each time the Scene is drawn. This allows VMD to continue to animate and check for user commands while a coordinate file is being read or written. For the actual coordinate file reading or writing, the CoorFile base class and derived ICoorFile and OCoorFile classes abstract the action of taking a set of XYZ positions for a molecule and reading or writing a trajectory file. Specific versions of these classes for PDB and DCD files are used, and any other number of trajectory formats may be supported by developing new subclasses of ICoorFile and OCoorFile , with also an update to CoorFileData .
Up to the point just described are all the classes necessary to store and manipulate a molecule. However, there are several different ways for a molecule to be imported into VMD , and each method has a specific subclass of Molecule to provide the functions to read in the proper data and store it into the standard internal format of the Molecule class hierarchy. Currently, the following subclasses of Molecule exist:
Used to create a new molecule by reading structure information from a file, such as a PDB or PSF file. This object provides routines which understand the format of these data files, and which convert the data within the files to the internal molecular structure format used by BaseMolecule
. This action is done when the create routine is called; after reading in the file and successfully creating the molecular structure, the create routine for Molecule
is called, which then calls create for DrawMolecule
,
This reads a molecular structure directly from a molecular dynamics simulation program running as a separate process on the same or another computer. The data is transferred directly over the network, without using any intermediate files, using the MDComm software (see section 1.3). Otherwise, it functions identically to MoleculeFile .
Thus, the steps in creating a new molecule in VMD are as follows:
The object which keeps track of all the currently-loaded Molecule objects is MoleculeList , of which there is exactly one in VMD assigned to the global variable moleculeList (although there is no reason why there could not be more than one). MoleculeList is an important object: it manages all the molecules, contains routines to allow an operation to be performed on a number of molecules at the same time, and supplies information on how the molecules are related to each other. MoleculeList is derived from Displayable as well: it is the top-level parent Displayable with all Molecule objects as child Displayable s. Thus, turning off the MoleculeList turns off all the molecules, and similarly rotating or scaling the MoleculeList does so to all the molecules. There are no drawing commands currently for the MoleculeList itself (although they could be added for something which indicates relationships between the molecules). This is a useful trick in VMD : have a container class which is derived from Displayable , but which has no drawing commands of its own; instead, have it contain several child Displayable objects which form components of the complex object which is to be drawn. By applying rotations, translations, etc. to the container class, all the child components are similarly transformed, and they may be separately altered or turned on or off.
To create, maintain, and access a connection to a molecular dynamics simulation running on a remote computer (which may just happen to be the same computer running VMD ), the objects illustrated in figure 5.4 are used.
The Remote object is used to encapsulate the functionality of the MDComm application library for initializing, accessing, and controlling a remote simulation. This object contains all the data necessary to create a new simulation or to attach to an already-running simulation. It also contains member functions for querying whether a simulation may be run on another computer, for retrieving the list of available MD programs or jobs on that computer, for obtaining and modifying parameters necessary to start a new job, and for actually starting, attaching to, or stopping an MD simulation.
There are three phases in the task of displaying a molecule in VMD which is being simulated by a separate MD process; they must be accomplished in this order:
As mentioned, once the connection is established and the structure sent over the network to VMD , a new Molecule can be created. At the very beginning of the procedure just outlined for creating a connection, an instance of a Remote is created to proceed through the phases. After this is complete and the connection is successful, the Remote instance is given to a new MoleculeRemote object, which uses the data in Remote to construct a new molecule in VMD just as if the data were being read from a file. If another simulation is to be started or attached to, another Remote instance must be created. Any number of simulations may be attached to by VMD during a single session, even all the same time. However, there can only be one Remote object being used to initialize a new connection at any one time. Thus, there is one global instance of a Remote object, referred to by the global variable remote; this is used for setting up a simulation, and if the setup is successful the instance is used to create a new MoleculeRemote , after which a brand new instance of a Remote is created and assigned to remote.
MoleculeRemote acts very much like a MoleculeFile , except structure data is retrieved from the provided Remote instance. It also provides a version of the prepare virtual routine (originally defined in the Displayable class). When prepare is called, the MoleculeRemote checks for and processes any new data is available from the remote connection (i.e., new coordinate sets recently computed by the simulation). It also maintains any special items used for graphical display of the simulation, for example the DrawPatch object. DrawPatch is a Displayable , and is used to graphically depict the shape and status (via different coloring schemes) of the volumes of space in which the molecule moves in the simulation. This acts very much like DrawMolItem , by being a child Displayable to the MoleculeRemote which creates it.
The final object used specifically for remote simulation control is the RemoteList , which functions very much like a MoleculeList but which instead keeps a list of MoleculeRemote pointers. If a molecule is read from just some data files, it is stored by the MoleculeList but not the RemoteList (since it is not from a remote connection). If the molecule is from a remote simulation, the pointer to the molecule is stored by both MoleculeList (as a Molecule * pointer) and RemoteList (as a MoleculeRemote * pointer). This allows VMD to distinguish which molecule is part of a presently or previously active simulation. Even if the simulation is terminated, the reference in RemoteList is maintained since the molecule was at some point part of a simulation.
A major design point for VMD is to make it relatively easy to add completely different user interface (UI) methods, and allow for each interface to provide a means for accomplishing tasks that may also be accomplished by using the other interfaces as well. Figure 5.5 illustrates the objects which are used to realize this design. There are four main or base-class level objects used in this category:
Since there are to be several different UI components, there needs to be a way to avoid duplication of the code required to carry out the tasks requested by the user manipulating the user interface. This is the purpose of the Command object: each subclass of Command represents a single operation or tasks which the user may request to be done via a user interface of some form. These Command objects may take parameters to tell them exactly how to perform the task, but are designed to be rather specific about exactly what they should do. For example, CmdMolNew is the Command subclass which contains all the code necessary to create a new molecule via the algorithm described earlier, while the CmdRotate object knows how to apply a specified rotation to the current Scene . Each Command has a unique code, defined in the file Command.h, and requires derived classes to do the following things:
*cmdText << "rot " << axis;
*cmdText << ( byOrTo == CmdRotate::BY ? " by " : " to ");
*cmdText << deg;
*cmdText << ends;
There are many many actions which need to be done each time through the main execution loop of VMD (section 5.2). The CommandQueue object is used to queue and execute all the actions that need to be done. This is essentially a FIFO queue, and there is just one instance of this in VMD (stored in the global variable commandQueue). This object also contains routines for logging a VMD session. New Command objects are added to the queue via the routine CommandQueue::( * ) , and are appended to the end of the queue; the routine CommandQueue::( * ) then executes the top Command in the queue, and then deletes the Command instance. After the Command is executed, but before it is deleted, CommandQueue informs the specified UIList (described later) that the action was done (why this is so is also described later). Since the Command is deleted after it is executed, an instance of the Command must be created via new, and then left to CommandQueue to be deleted. This is done because due to the asynchronous nature of this method of executing commands, it is not known exactly when the data in the Command will be needed, and thus it is unknown when the storage space may be freed up for other use. The only object which knows this is CommandQueue , and so it must be given new copies of these Command objects which it must delete when done.
The objects which create these Command objects are derived from the UIObject base class. This base class forms the heart of all the different types of UI components which VMD provides. For example, the text console UI ( UIText ), the mouse interface ( Mouse ), and all the GUI forms ( FormsObj and derivations thereof) are derived from UIObject . All the UIObjects , when they are initialized, register with a UIList object, which maintains the list of all UIObject s and can work with them as a group. The UIObject is given a unique ID code when it registers, which it uses to identify later if any actions being done were a result of a request from itself.
Each UIObject basically works with a subset of all the possible Command objects which VMD contains. Typically a UI component displays some graphical feedback or status of the current state of the program, such as displaying via a slider or lighted button what the value of some variable is. When an action is performed the UI components must be informed because this graphical status must be updated to reflect any changes. Any number of different UI components may require such an update, but since the number of Command s which can result in a change to the particular graphical display of each UIObject is much smaller than the total number of available actions, it would be very inefficient to have every UI component notified when each action is performed. Instead, the UIObjects each maintain a list of the integer codes for the Command s in which they are interested. When a Command is executed, the UIList is told about the result of the action, and then the UIList in turn notifies only those UIObject s which have indicated they are interested in the Command . However, a UIObject can create any available Command instance, and give it to the CommandQueue to be executed. When a new Command is created, the ID of the UI which is creating it is also given to the Command , so that later when the UI components are notified of the action they can tell who requested the activity.
The purpose of each UIObject is to provide a means for the user to input commands, and to display to the user the current status of the program. The virtual routine UIObject::_event() is called once for each UI during the main execution loop to allow the UI component to check for user events (such as keyboard entries, mouse button presses, or manipulations of GUI components such as buttons or sliders). If such an event is found, a new Command is created for the event (events are simply derived from Command , and contain the data specifying the type of event) and put on the CommandQueue . After all UIObjects are checked for events, the CommandQueue is told to start executing its queued actions, continuing until the queue is empty. When an event action is processed, typically it results in some other form of Command to be requested, which is done by creating the proper special derivationof Command for the action and giving it to the CommandQueue . Eventually all events are processed, and the actions requested by them are then processed, and finally the queue is empty. As each Command is processed the requested action is done and all the UIObjects which expressed an interest in the action are notified, which allows them to update their display. When the queue is empty, VMD proceeds to then redraw the Scene . This execution loop is summarized in section 5.2.
The UIList is what manages all the UIObject components. In VMD there is just one UIList instance, uiList. It contains methods which can initialize and reset all the UIObject components at once, and contains methods used by the UIObject s to register and retrieve their integer ID values. When a Command is executed by the CommandQueue , the routine UIList:: act_on_command( * ) is executed, and the UIList then does likewise for all UIObject s which are interested.
It is relatively simple to create a new UIObject ; each on-screen menu is a separate UIObject as is the mouse, the text console (which almost never needs to be updated due to a command being executed), and the 3D UI. Each UIObject can contain the ability to execute as many or as few actions as is desired. New UIObject s should be new'ed in the VMDinitUI routine, after the UIList and CommandQueue global instances are created.
The objects responsible for controlling the external spatial tracking devices, and for displaying and using the 3D pointers, are currently in the experimental stage, and will be described later when their design is closer to being final.