ArtiSynth Reference Manual

1 Component Hierarchy

1.1 Model Components

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:

  // get the name for this component
  String getName();
  // set the name for this component
  void setName (String name);
  // get the number of this component
  int getNumber();
  // set the number of this component (for internal use only)
  void setNumber (int num);

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:

  models/jawmodel/axialSprings/lat
  models/mech/models/tongue/bundles/2/elementDescs/12

ModelComponent contains a number of other methods for navigating and maintaining hierarchy structure:

  // get the parent of this component
  CompositeComponent getParent();
  // sets the parent of this component (internal use only)
  void setParent(CompositeComponent parent);
  // called by the system after a component, or one of its ancestors,
  // is added to the component hierarchy by attaching it to hcomp
  void connectToHierarchy (CompositeComponent hcomp);
  // called by the system before a component, or one of its ancestors,
  // is removed from the component hierarchy by detaching it from hcomp
  void disconnectFromHierarchy();
  // get all hard references for this component (see below)
  void getHardReferences (List<ModelComponent> refs);
  // get all soft references for this component (see below)
  void getSoftReferences (List<ModelComponent> refs);
  // called to update the component when soft references are removed
  void updateReferences (boolean undo, Deque<Object> undoInfo);
  // notify the parent of a change in this component
  void notifyParentOfChange (ComponentChangeEvent e);
  // returns true if this component contains state information
  boolean hasState();

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

  connectToHierarchy () {
     ... perform hierarchy-dependent initialization ...
  }
  disconnectFromHierarchy (CompositeComponent parent) {
     ... undo hierarchy-dependent initialization ...
  }

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 StructureChangeEvent for which stateIsChanged() returns true.

A ModelComponent also maintains a number of flags:

   // returns true if a component is selected
   boolean isSelected();
   // sets whether or not a component is selected (system use only)
   void setSelected (boolean selected);
   // returns true if a component should not be removed from its parent
   boolean isFixed();
   // sets whether or not a component should be removed from its parent
   void setFixed (boolean fixed);
   // returns true if a component is marked
   boolean isMarked();
   // sets whether or not a component is marked
   void setMarked (boolean marked);

All of these flags are false by default.

selected

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.

fixed

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

marked

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.

Important:
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.

Finally, all ModelComponents implement the interface Scannable, which provides methods for writing and scanning to and from persistent storage. Details are given in Section 3.

For convenience, ModelComponentBase provides a base implementations of all the ModelComponent methods. Most ArtiSynth components inherit from ModelComponentBase.

1.2 Component References

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.

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

   ArrayList<ModelComponent> mySoftRefs;
   ...
   void updateReferences (boolean undo, Deque<Object> undoInfo) {
      super.updateReferences (undo, undoInfo);
      if (undo) {
         // undo the update
         Object obj = undoInfo.getFirst();
         if (obj != ModelComponentBase.NULL_OBJ) {
            ((ListRemove<ModelComponent>)obj).undo();
         }
      }
      else {
         // remove soft references which arent in the hierarchy any more:
         ListRemove<ModelComponent> remove = null;
         for (int i=0; i<mySoftRefs.size(); i++) {
            if (!ComponentUtils.areConnected (this, mySoftRefs.get(i)) {
               // reference isnt in the hierarchy; request its removal
               if (remove == null) {
                  remove = new ListRemove<ModelComponent>(mySoftRefs);
               }
               remove.requestRemove (i);
            }
         }
         if (remove != null) {
            remove.remove();
            undoInfo.addLast (remove);
         }
         else {
            undoInfo.addLast (ModelComponentBase.NULL_OBJ);
         }
      }
   }

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.

1.3 Composite Components

CompositeComponent is a subinterface of ModelComponent which can contain children. Its main methods include:

  // returns the number of child components
  int numComponents();
  // gets a child component by name
  ModelComponent get (String name);
  // gets a child component by index
  ModelComponent get (int idx);
  // gets a child component by number
  ModelComponent getByNumber (int num);
  // returns the index of a child component
  int indexOf (ModelComponent c);
  // finds a descendent component with a specified path relative to this component
  ModelComponent findComponent (String name);
  // called when a change occurs in one of the descendants.
  void componentChanged (ComponentChangeEvent e);

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:

public interface MutableCompositeComponent<C extends ModelComponent>
extends CompositeComponent {
   // add a component; return false if not possible
   public boolean add (C comp);
   // add a set of components at specified index locations
   // (mainly for internal system use)
   public void addComponents (ModelComponent[] comps, int[] indices, int num);
   // remove a component; return false if not found
   public boolean remove (C comp);
   // removes a set of components and stores their original
   // index locations (mainly for internal system use)
   public void removeComponents (ModelComponent[] comps, int[] indices, int num);
}

1.4 CompositeComponentBase, ComponentList, and ComponentListImpl

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:

   MechModel mech;
   ....
   ComponentList<Particle> bigParticles =
      new ComponentList<Particle>(Particle.class, "big");
   ComponentList<Particle> smallParticles =
      new ComponentList<Particle>(Particle.class, "small");
   mech.add (bigParticles);
   mech.add (smallParticles);

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.