5 Simulation Control

5.2 Custom properties

Because of the usefulness of properties in creating control panels and probes (Sections 5.1) and Section 5.4), model developers may wish to add their own properties, either to the root model, or to a custom component.

This section provides only a brief summary of how to define properties. Full details are available in the “Properties” section of the Maspack Reference Manual.

5.2.1 Adding properties to a component

As mentioned in Section 1.4, properties are class-specific, and are exported by a class through code contained in the class’s definition. Often, it is convenient to add properties to the RootModel subclass that defines the application model. In more advanced applications, developers may want to add properties to a custom component.

The property definition steps are:

Declare the property list:

The class exporting the properties creates its own static instance of a PropertyList, using a declaration like

   static PropertyList myProps = new PropertyList (MyClass.class, MyParent.class);
   @Override
   public PropertyList getAllPropertyInfo() {
      return myProps;
   }

where MyClass and MyParent specify the class types of the exporting class and its parent class. The PropertyList declaration creates a new property list, with a copy of all the properties contained in the parent class. If one does not want the parent class properties, or if the parent class does not have properties, then one would use the constructor PropertyList(MyClass.class) instead. If the parent class is an ArtiSynth model component (including the RootModel), then it will always have its own properties. The declaration of the method getAllPropertyInfo() exposes the property list to other classes.

Add properties to the list:

Properties can then be added to the property list, by calling the PropertyList’s add() method:

   PropertyDesc add (String name, String description, Object defaultValue);

where name contains the name of the property, description is a comment describing the property, and defaultValue is an object containing the property’s default value. This is done inside a static code block:

   static {
      myProps.add ("stiffness", "spring stiffness", /*defaultValue=*/1);
      myProps.add ("damping", "spring damping", /*defaultValue=*/0);
   }

Variations on the add() method exist for adding read-only or inheritable properties, or for setting various property options. Other methods allow the property list to be edited.

Declare property accessor functions:

For each property propXXX added to the property list, accessor methods of the form

   void setPropXXX (TypeX value) {
      ...
   }
   TypeX getPropXXX() {
      TypeX value = ...
      return value;
   }

must be declared, where TypeX is the value associated with the property.

It is possible to specify different names for the accessor functions in the string argument name supplied to the add() method. Read-only properties only require a get accessor.

5.2.2 Example: a visibility property

An model illustrating the exporting of properties is defined in

  artisynth.demos.tutorial.SimpleMuscleWithProperties

This model extends SimpleMuscleWithPanel (Section 4.5.2) to provide a custom property boxVisible that is added to the control panel. The class definition, excluding import statements, is shown below:

1 public class SimpleMuscleWithProperties extends SimpleMuscleWithPanel {
2
3    // internal property list; inherits properties from SimpleMuscleWithPanel
4    static PropertyList myProps =
5       new PropertyList (
6          SimpleMuscleWithProperties.class, SimpleMuscleWithPanel.class);
7
8    // override getAllPropertyInfo() to return property list for this class
9    public PropertyList getAllPropertyInfo() {
10       return myProps;
11    }
12
13    // add new properties to the list
14    static {
15       myProps.add ("boxVisible", "box is visible", false);
16    }
17
18    // declare property accessors
19    public boolean getBoxVisible() {
20       return box.getRenderProps().isVisible();
21    }
22
23    public void setBoxVisible (boolean visible) {
24       RenderProps.setVisible (box, visible);
25    }
26
27    public void build (String[] args) throws IOException {
28
29       super.build (args);
30
31       panel.addWidget (this, "boxVisible");
32       panel.pack();
33    }
34 }

First, a property list is created for the application class SimpleMuscleWithProperties.class which contains a copy of all the properties from the parent class SimpleMuscleWithPanel.class (lines 4-6). This property list is made visible by overriding getAllPropertyInfo() (lines 9-11). The boxVisible property itself is then added to the property list (line 15), and accessor functions for it are declared (lines 19-25).

Figure 5.3: Control panel created by the model SimpleMuscleWithProperties, showing the newly defined property boxVisible.

The build() method calls super.build() to perform all the model creation required by the super class, and then adds an additional widget for the boxVisible property to the control panel.

To run this example in ArtiSynth, select All demos > tutorial > SimpleMuscleWithProperties from the Models menu. The control panel will now contain an additional widget for the property boxVisible as shown in Figure 5.3. Toggling this property will make the box visible or invisible in the viewer.

5.2.3 Coordinate panels

Figure 5.4: Coordinate panel added to the model MultiJointedArm.

A special subclass of ControlPanel is a CoordinatePanel, which supports the addition of widgets for controlling joint coordinate values. A CoordinatePanel is associated with the MechModel containing the coordinates’ joints. When a coordinate value changes, the MechModel is used to update other model components that may be affected, such as the wrap paths for MultiPointSprings and the locations of attached components.

At present, CoordinatePanels does not take into account couplings between coordinate values that may be induced by kinematic loops with the model. This will be remedied in the future.

A CoordinatePanel may be created with the constructor

CoordinatePanel (String name, MechModel mech)

which assigns it a name and MechModel. Coordinate widgets may then be added using the methods

CoordinateWidget addCoordinateWidget (JointBase joint, String cname)
CoordinateWidget addCoordinateWidget (String label, JointBase joint, String cname)
CoordinateWidget addCoordinateWidget (JointBase joint, int cidx)
CoordinateWidget addCoordinateWidget (String label, JointBase joint, int cidx)
CoordinateWidget addCoordinateWidget (JointBase joint, int cidx, double min, double max)
CoordinateWidget addCoordinateWidget (String label, JointBase joint, int cidx, double min, double max)
CoordinateWidget addCoordinateWidgets (JointBase joint)

where joint specifies the joint containing the coordinate and cname or cidx give either its name or index. If present, label specifies the widget label (which otherwise takes the coordinate’s name) and min and max give the widget’s value range (which is otherwise inferred from the coordinate itself). The last method, addCoordinateWidgets() adds widgets for every coordinate within the specified joint.

These methods return CoordinateWidget, which is a subclass of DoubleFieldSlider. Other types of widgets may be added to a CoordinatePanel using the addWidget() methods inherited from ControlPanel.

CoordinatePanel widgets represent their values for revolute coordinates in degrees.

When added to the example MultiJointedArm (Section 3.5.16), the following code fragment creates the coordinate panel shown in Figure 5.4:

import artisynth.core.gui.CoordinatePanel;
...
      CoordinatePanel panel = new CoordinatePanel ("coordinates", myMech);
      panel.addCoordinateWidgets (ujoint);
      panel.addCoordinateWidgets (hinge);
      addControlPanel (panel);