next up previous contents
Next: Main VMD execution Up: Program Structure Previous: Program Structure

Program component descriptions

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.

Utility objects

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:

To use an Inform object, text and data are given to object using insertion ( <<) operators, i.e.,
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.

Display objects

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):

  1. DisplayDevice objects, which are responsible for rendering a list of drawing commands to a given device or file;
  2. DispCmd objects, of which there are several subclasses; each DispCmd subclass represents a single drawing command, such as a command to draw a line or to set the current color.
  3. Displayable objects, which each represent a single graphical item with a list of drawing commands that may be rendered to a DisplayDevice ;
  4. Scene objects, which store lists of Displayable objects. When requested, a Scene takes a given DisplayDevice and requests the device to render each of the Displayable objects it stores.

 

Device-Specific Rendering.

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.

Drawing Tokens.

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:

  1. Integer code specifying the drawing operation (codes are defined in the file DispCmds.h).
  2. Integer stating the size of the command, in bytes (not including the code or this size count).
  3. Data necessary for the drawing command (i.e., XYZ positions of the endpoints of a line or a cylinder).
Why are things done this way? There are a few reasons:

#Displayable#1799> objects.

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 .

The #Scene#1844> .

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:

  1. Pointers to all the Displayable objects which have registered with the Scene .
  2. Pointers to the beginning of each display list (essentially treated as arrays of chars). There may be more of these lists than there are registered Displayable objects, since each Displayable (regardless of whether it is a parent, child, or both) provides the Scene object with a copy of the pointer to its display list.

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:

  1. The routine scene->prepare(DisplayDevice *) is called by the user. The Scene will call a routine for each registered Displayable that allows the object to prepare to be drawn. This preparation may include, for example, changing which frame in an animation should be shown, or updating the current position of a 3D pointer, or most anything else that needs to be done each time the Scene is drawn.
  2. The routine scene->draw(DisplayDevice *) is called by the user. This routine proceeds in the following steps:
    1. Prepare the DisplayDevice for drawing 3D objects.
    2. For each 3D display list stored by the Scene , call the routine display->render(void *) with the pointer to the display list.
    3. If stereo is being used, repeat the following step for the other eye.
    4. Prepare the DisplayDevice for drawing 2D objects.
    5. For each 2D display list stored by the Scene , call the routine display->render(void *) with the pointer to the display list.
    6. Update the DisplayDevice after all drawing is complete.

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.

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 bluegreenred or bluewhitered. 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.

Picking Items.

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:

  1. Start: when a button is pressed by a pointer, a command is issued which checks to see if something is under the current pointer position. If so, a new picking operation is started, and continues until the button (or whatever the pointer is using) is released.
  2. Moving: As the button is held down, if the start of the picking operation did indeed find something under the pointer, commands are executed as the pointer moves to allow objects to be continually manipulated by the pointer.
  3. End: When the button is released, some final action may be required, and this is signaled by a command to end the current picking session.
The effect on objects during these phases may be dependent on which button was pressed (two buttons, left and middle, are assumed), and what the current mode was when the button was pressed. PickMode objects are used to handle the different actions required for different picking modes.

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:

  1. PickMode::_start(DisplayDevice *, Pickable *, int button, int tag, int dim, float *pos)
    When a pick is successfully started (which means the tag of the point which was picked has been determined), this routine is called for the PickMode object corresponding to the current pick mode, to let that object perform some special action based on the selection. The item selected is provided, as is the button pressed, the dimension (2 or 3) and position of the pointer, and the DisplayDevice used to find the selected item (this is necessary to allow access to routines which convert 2D screen positions to 3D world coordinates). Finally, the tag of the point in the Pickable that was actually clicked on is provided (more on this later).
  2. PickMode::_move(same args)
    Again, called only for the PickMode object of the current mode for the pointer used; this is called every time the pointer moves to a new position.
  3. PickMode::_end(same args)
    Same as the others, just called when the pointer button is released.

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:

  1. All the Pickable objects which have registered themselves as items which contain points which can be selected with a pointer.
  2. All the PickMode objects which are used to provide different action capabilities to the same pointer, in an extendible fashion.
The pointer objects call routines in PickList to get the current number of names of picking modes, to check if the current pointer position is over a pickable point (and to find out which one), to tell the program that the pointer is moving while an object is being picked, and to tell the program that the button has been released following a picking operation. Thus, PickList is the ``coordinator'' for all picking operations. PickList contains a routine similar to the draw routine, but which instead checks for picked item and executes the proper action if one is found. The algorithm used is:
  1. For each Pickable registered, check if it is interested in the current mode. If so, call Pickable::_start(see later) . If not, skip the Pickable and go to the next. 2) Then, call PickMode::_start(f) or the PickMode object corresponding to the current pick mode.

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).

Molecule objects

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:

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:

An instance of all three of these objects, taken together as a group, completely define how a represenation of a molecule should be drawn, and so each DrawMolItem requires one of these objects to allow it to construct the list of drawing commands for the representation. Each DrawMolItem is a child Displayable of the parent DrawMolecule , and may be independently turned on or off. By doing it this way, a complex image of the molecule may be constructed by separate more basic components, which may be separately manipulated, while the set of components are kept with the same transformation (rotation, scaling, and centering) applied to the molecule as a whole.

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:

Thus, the steps in creating a new molecule in VMD are as follows:

  1. Create a new empty Molecule object, by creating an instance of one of the subclasses in the list above and assigning it to a Molecule * variable. This new molecule is empty, with 0 atoms, 0 bonds, etc.
  2. Give information to this new object on how the molecule is to be created. Much of this data is actually specified as arguments to the constructor of the special subclass used.
  3. Call the function create for the new Molecule , which will do most of the work in creating the new molecule. During this process, the following actions are done:
    1. Data in the original format is read in from the proper source.
    2. Each atom is added one by one to the molecular structure via routines in BaseMolecule . The lists of unique names for molecule components are constructed during this process.
    3. After all atoms are read, the bonds between atoms are stored. With the atoms and namelists, this completely defines the basic structure of the molecule. This step may involve finding the bonds through a nearest-neighbor search (necessary when the structure is read from a PDB file).
    4. ``Extra'' data helpful in displaying the molecule is read, such as which atoms are hydrogen-bond donors or acceptors.
    5. After all data is read and the basic structure defined, the create routine in BaseMolecule analyzes the molecule and creates the lists of Residues , Fragments , and anything else which helps define the structure.
    6. An initial representation of the molecule is created by DrawMolecule , and the display list necessary to draw the molecule is constructed.
  4. After it has been created, a request to read in any trajectory files is given to the new molecule if necessary.

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.

Remote connection objects

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:

  1. Select a computer for the MD simulation, and connect to it to retrieve the list of running jobs there and the list of available MD simulation packages. When this is done by a Remote object, the lists are stored and may be retrieved by other objects in VMD . If the proper daemon process is not running on the selected computer, this step will fail.
  2. If the user is to connect to a job which is already running, this phase is skipped; if the user wishes to start a new job, this phase is where the parameters for the job are retrieved from the remote computer and modified to the users requirements. This requires first selecting which one of the possible MD simulation programs the user wishes to run, and then having the Remote object retrieve the list of optional and required parameters for that program. This list is stored Remote , which also supplies routines to query and change these settings. Once this is accomplished, a routine in Remote is called to start a new simulation (a similar routine exist to attach to an already-running job). After this is called, the simulation starts, loads its data, and this connection procedure proceeds to the final phase.
  3. Once a connection is made to a running application (as opposed to the state for the earlier phases where a simulation was being initialized), the molecular structure is transferred from the remote process to VMD , and coordinate sets are then sent over as they are calculated. The structure sent via the network is stored in an internal format in the Remote object (basically, as a struct defined by the MDComm library); this data is then given to a MoleculeRemote object (described later) and used to create a Molecule in VMD . The Remote object also has routines for changing the state of the simulation after it is running, and for disconnecting from the simulation (which leaves it running, and in a state where VMD can attach to it again later if preferred) as well as for killing the simulation process.

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.

User interface objects

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:

  1. In the constuctor, the data necessary to perform the command must be given to the class and stored for the time when the command will be executed.
  2. In a virtual function Command::_text() , use a streams output technique to place within the protected variable `` ostrstream *cmdText'' a string which is the text command equivalent of the requested operation. For example, for CmdRotate , if deg is the amount specified to rotate the scene, the function contains lines such as these:
    *cmdText << "rot " << axis;
    *cmdText << ( byOrTo == CmdRotate::BY ? " by " : " to ");
    *cmdText << deg;
    *cmdText << ends;
  3. Provide a version of the protected virtual function Command::_execute() , which is called when the Command is requested to perform the actions it must do. More completely, to execute a Command the routine Command::() is called, which then calls do_execute.
Since the Command will contain a text version of the requested action, it is relatively simple to create a text log of a VMD session: each time a Command is executed, the string for that command is simply written to a file.

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.

Tracker objects

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.



next up previous contents
Next: Main VMD execution Up: Program Structure Previous: Program Structure



Andrew Dalke
Wed May 15 02:25:03 CDT 1996