Artsynth models are created from a hierarchy of components. Each component is an instance of ModelComponent, which contains a number of methods used to maintain the component hierarchy. These include methods for naming and numbering components:
Each component can be assigned a name, which can be any sequence of characters that does not begin with a digit, does not contain the characters ’.’, ’/’, ’:’, ’*’, or ’?’, and does not equal the string "null". For components which are not assigned a name, getName() will return null.
Artisynth may also be configured so that components names must be unique for all components which are children of the same parent.
Even if a component does not have a name, it has a number, which identifies it with respect to its parent. Numbers are assigned automatically when a component is added to its parent, and persist unchanged until the component is removed from its parent. This persistence of numbers is important to ensure that components keep the same path name as long as they are connected to the hierarchy.
Names and/or numbers can be used to form a path name for each component that identifies its place in the hierarchy. If a component does not have a name, its number is used in the path instead. Some example path names look like:
ModelComponent contains a number of other methods for navigating and maintaining hierarchy structure:
getParent() returns the component’s parent, which is a CompositeComponent (Section 1.3). Conversely, if getParent() returns null, the component is not attached to any parent and is not connected to the hierarchy unless it is the top-level RootModel component (Section 2.3).
When a model component (or one of its ancestors) is added or removed from the component hierarchy, its methods connectToHierarchy(hcomp) or disconnectFromHierarchy(hcomp) are called, with hcomp indicating the hierarchy component to which the component (or ancestor) was attached or detached. When either of these methods are called, the component will still be connected to the hierarchy, and so hierarchy-dependent initialization or deinitialization can be performed, like setting (or removing) back pointers to references, etc.:
The methods getHardReferences() and getSoftReferences() are described in Section 1.2.
It is also necessary to notify components in the hierarchy when there are changes in structure or component properties, so that the necessary adjustments can be made, including the clearing of cached data. Notification is done using the method notifyParentOfChange(), which propagates an appropriate change event up the hierarchy. It will typically do this by calling the componentChanged() method of the parent (see Section 1.3).
The method hasState() should return true if the component contains state information. This is always true if the component contains dynamic state information such as positions or velocities, but components may sometimes contain additional state information (such as contact state). Structural changes involving the addtion or removal of state-bearing components should be announced to the system by calling notifyParentOfChange() with a ComponentChangeEvent for which stateChanged() returns true.
A ModelComponent also maintains a number of flags:
All of these flags are false by default.
indicates whether or not a component is selected. Components can be selected using various selection mechanisms in the Artisynth interface, such as the navigation panel or the viewer. When selected, its isSelected() method will return true.
indicates, if true, that a component should not be removed from its parent. It is used to fix required child components of composite components that contain otherwise removable children (Section 1.4).
is available for use by graph-processing algorithms involving the component hierarchy, to indicate when a component has been visited or otherwise processed. This flag should be used with care to avoid side effects.
setSelected() should only be used by the SelectionManager, and should not be called by applications. Programmatic component selection should be performed by calling the addSelected() or removeSelected() methods of the SelectionManager.
For convenience, ModelComponentBase provides a base implementations of all the ModelComponent methods. Most ArtiSynth components inherit from ModelComponentBase.
Model components can reference additional components outside of the parent-child relationships of the hierarchy. For example, a point-to-point spring contains two references to its end-points, which are themselves model components. As another example, components which implement the ExcitationComponent interface can maintain references to other ExcitationComponents to use as excitation sources. References can be considered to be either hard or soft. A hard reference is one which the component requires in order for its continued existence to be meaningful. The end-point references for a point-to-point spring are usually hard. A soft reference is one that the component can do without, such as the excitation source inputs mentioned above. The methods getHardReferences() and getSoftReferences() are used to report all hard and soft references held by a component.
getHardReferences() and getSoftReferences() should report only references held by the component itself, and not those held by any of its descendents.
The distinction between hard and soft references is used by the system when components in the hierarchy are deleted. A component that holds a hard reference to a deleted component is generally deleted as well. However, when only soft references are deleted, then the updateReferences() method of the referring component is called to update the component’s internal structures. updateReferences() should also store information about the update, to allow the update to be undone in case the method is called later with its undo argument set to true. A typical implementation pattern for updateReferences() is shown by the following example, in which maspack.util.ListRemove is used to remove selected items from a list of soft references, and store information needed to undo this later:
When updating, the method uses ComponentUtils.areConnected() to determine which soft references have been deleted from the hierarchy. A ListRemove object is used to assemble the remove requests and then perform the remove all at once and store information about what was removed for possible later undoing. The remove object is appended to the end of undoInfo. If no undo was needed, then NULL_OBJ is stored instead because Deque objects don’t accept null arguments. Undo information is stored at the end of the deque and removed from the front. This allows multiple updates, including that for the super class, to be performed in sequence.
Most of the above methods are self-explanatory. It is important to note the difference between indices and numbers when identifying child components. An index is the location of the child within the list of children, starting from 0, and can change as children are added or removed. A number, on the other hand, as described above, is assigned automatically to a child when it is added to the parent and persists as long as it remains.
The componentChanged() method is called to indicate structure or property changes. Appropriate actions may include clearing cached data, and propogating the event further up the hierarchy (using notifyParentOfChange()).
MutableCompositeComponent is a subinterface of CompositeComponent which allows child components to be added and removed by an ArtiSynth application. It is a generic class parameterized by a class type C which must be an extension of ModelComponent. Its definition is:
A default implementation of CompositeComponent is provided by CompositeComponentBase. It is a non-generic class that provides a base for composite components whose composition is created at construction time and is not intended to change during the running of an ArtiSynth application.
ComponentList is a much more flexible class which implements MutableCompositeComponent and provides for collections of components whose composition may be built and changed by an application. ComponentList is used widely to store the many lists of components that comprise a working ArtiSynth model.
In particular, MechModel and FemModel3d, the primary ArtiSynth classes for implementing mechanical and finite element models, are themselves subclasses of ComponentList which contain lists of mechanical components (such as particles, rigid bodies, and force effectors for MechModel, and nodes, elements, and geometry for FemModel3d).
In the case of MechModel, applications can create and add their own component lists to the model itself:
By default, child components that belong to a MutableCompositeComponent (which includes ComponentList) may be selected by the ArtiSynth application for deletion. This may be undesirable, particularly if internal structures depend on certain child components. Components that should not be removed from their parents should have their fixed flag set to true in the composite component constructor, either by calling setFixed(), or by adding the component using the addFixed() method of ComponentList.
The class ComponentListImpl is available as an internal implementation class for constructing instances of either CompositeComponent or MutableCompositeComponent. It provides most of the implementation methods needed for a mutable component list, which can be exposed in the client class using delegate methods. Components implementing only CompositeComponent may choose to expose only some of these methods. For details, one should consult the source code for CompositeComponentBase or ComponentList.