8 Contact and Collision

8.5 Contact rendering

As mentioned in Section 8.2.1, additional collision behavior properties exist to enable and control the rendering of contact artifacts. These include intersection contours, contact points and normals, contact forces and pressures, and penetration depths. The properties that control these are supplemented by generic render properties (Section 4.3) to specify colors, line widths, etc.

By default, contact rendering is disabled. To enable it, one must set the collision manager to be visible, which can be done using a code fragment like the following:

  RenderProps.setVisible (mechModel.getCollisionManager(), true);

CollisionManager and CollisionBehavior both export contact rendering properties. Setting these in the former controls rendering globally for all collisions, while setting them in the latter controls rendering for the collidable pair associated with the behavior. Some artifacts, like contact normals, forces, and color maps, must be drawn with respect to either the first or second collidable. By default, the first collidable is chosen, but the second collidable can be requested using the renderingCollidable property described below. Normals are drawn toward, and forces drawn such that they are acting on, the indicated collidable. For collision behaviors specified using the setCollisionBehavior() methods, the first and second collidables correspond to the collidable0 and collidable1 arguments. Otherwise, for default collision behaviors, the first collidable will be the one whose collidable bodies have the lowest collidable index (Section 8.3.4).

Properties exported by both CollisionManager and CollisionBehavior include:

renderingCollidable

Integer value of either 0 or 1 specifying the collidable for which forces, normals and color maps should be drawn. 0 or 1 selects either the first or second collidable. The default value is 0.

drawIntersectionContours

Boolean value requesting drawing of the intersection contours between collidable meshes, using the generic render properties edgeWidth and edgeColor. The default value is false.

drawIntersectionPoints

Boolean value requesting drawing of the interpenetrating vertices on each collidable mesh, using the generic render properties pointStyle, pointSize, pointRadius, and pointColor. The default value is false.

drawContactNormals

Boolean value requesting drawing of the normals associated with each contact constraint, using the generic render properties lineStyle, lineRadius, lineWidth, and lineColor. The default value is false. The length of the normals is controlled by the contactNormalLen property of the collision manager, which will be set to an appropriate default value if not set explicitly.

drawContactForces

Boolean value requesting drawing of the forces associated with each contact constraint, using the generic render properties lineStyle, lineRadius, edgeWidth, and edgeColor (or lineColor if edgeColor is null). The default value is false. The forces are drawn as line segments starting at each contact point and running parallel to the contact normal, with a length given by the current contact impulse value multiplied by the contactForceLenScale property of the collision manager (which has a default value of 1). The reason for using edgeWidth and edgeColor instead of lineWidth and lineColor is to allow the application to set the render properties such that both normals and forces can be visible if both are being rendered at the same time.

drawFrictionForces

Boolean value requesting the drawing of the forces associated with each contact’s friction force, in the same manner and style as for drawContactForces. The default value is false. Note that unlike contact forces, friction forces are perpendicular to the contact normal.

drawColorMap

An enum of type CollisionBehavior.ColorMapType requesting that a color map be drawn over the contact regions showing a scalar value such as penetration depth or contact force pressure. The collidable (0 or 1) onto which the color map is drawn is controlled by the renderingCollidable property (described below). The range of values used for generating the map is controlled by the colorMapRange property of either the collision manager or the behavior, as described below. The values are mapped onto colors using the colorMap property of the collision manager.

Values of CollisionBehavior.ColorMapType include:

NONE

The color map is disabled. This is the default value.

PENETRATION_DEPTH

The color map shows penetration depth of one collidable mesh with respect to the other. If one or both collidable meshes are open, then the colliderType should be set to AJL_CONTOUR (Section 8.2.1).

CONTACT_PRESSURE

The color map shows the contact pressures over the contact region. Contact pressures are determined by examining the forces at the contact constraints and then distributing these over the surrounding faces. This information is most useful and accurate when using vertex penetration contact (Section 8.4.1.2).

The color map itself is drawn as a patch formed from the collidable’s collision mesh, using faces and vertices associated with the collision region. The vertices of the patch are set to colors corresponding to the associated value (e.g., penetration depth or pressure) at that vertex, and the surrounding faces are colored appropriately. The resolution of the color map is thus determined by the resolution of the collision mesh, and so the application must ensure this is high enough to ensure proper results. If the mesh has only a few triangles (e.g., a rectangle with two triangles per face), the color interpolation may be spread over an unreasonably large area. Examples of color map usage are given in Sections 8.5.2 and 8.5.3.

colorMapRange

Composite property of the type ScalarRange that controls the range of values used for color map rendering. Subproperties include interval, which gives the value range itself, and updating, which specifies how this interval should be updated: FIXED (no updating), AUTO_EXPAND (interval is expanded as values increase or decrease), and AUTO_FIT (interval is reset to fit the values at every render step).

When the colorMapRange value for a CollisionBehavior is set to null (which is the default), the colorMapRange of the CollisionManager is used instead. This has the advantage of ensuring that the same color map range will be used across all collision interactions.

colorMapInterpolation

An enum of type Renderer.ColorInterpolation that explicitly specifies how colors in any rendered color map should be interpolated. RGB and HSV (the default) specify interpolation in RGB and HSV space, respectively. HSV interpolation is the default as it is generally better suited to rendering maps that are purely color-based.

In addition, the following properties are exported by the collision manager:

contactNormalLen

Double value specifying the length with which contact normals should be drawn when drawContactNormals is true. If unspecified, a default value is calculated by the system.

contactForceLenScale

Double value specifying the scaling factor for contact or friction force vectors when they are drawn in response to drawContactForces drawFrictionForces being true. The default value is 0.

colorMap

Composite property of type ColorMapBase specifying the actual color map used for color map rendering. The default value is HueColorMap.

Generic render properties within the collision manager can be set in the same way as the visibility, using the RenderProps methods presented in Section 4.3.2:

  Renderable cm = mechModel.getCollisionManager();
  RenderProps.setEdgeWidth (cm, 2);
  RenderProps.setEdgeColor (cm, Color.Red);

As mentioned above, generic render properties can also be set individually for specific behaviors. This can be done using code fragments like this:

  CollisionBehavior behav = mechModel.getCollisionBehavior (bodA, bodB);
  RenderProps.setLineWidth (behav, 2);
  RenderProps.setLineColor (behav, Color.Blue);

To access these properties on a read-only basis, one can do

  RenderProps props = mechModel.getCollisionManager().getRenderProps();
  ... OR ...
  RenderProps props = behav.getRenderProps();

Because of the manner in which ArtiSynth handles contacts, rendered contact information may sometimes appear to lag the simulation by one time step. That is because contacts are computed at the end of each time step i, and then used to compute the contact forces during the next step i+1. The contact information displayed at the end of step i is thus based on contacts detected at the end of step i-1, along with contact forces that are computed during step i and used to calculate the updated positions and velocities at the end of that step.

8.5.1 Example: rendering normals and contours

A simple model illustrating contact rendering is defined in

  artisynth.demos.tutorial.BallPlateCollide

This shows a ball colliding with a plate, while rendering the resulting contact normals in red and the intersection contours in blue. This demo also allows the user to experiment with compliant contact (Section 8.7.1) by setting compliance and damping properties in a control panel.

Figure 8.13: BallPlateCollide showing contact normals (red) and collision contour (blue) of the ball colliding with the plate.

The complete source code is shown below:

1 package artisynth.demos.tutorial;
2
3 import java.awt.Color;
4
5 import artisynth.core.gui.ControlPanel;
6 import artisynth.core.mechmodels.CollisionManager;
7 import artisynth.core.mechmodels.MechModel;
8 import artisynth.core.mechmodels.RigidBody;
9 import artisynth.core.workspace.RootModel;
10 import maspack.matrix.RigidTransform3d;
11 import maspack.render.RenderProps;
12 import maspack.render.Renderer;
13
14 public class BallPlateCollide extends RootModel {
15
16    public void build (String[] args) {
17
18       // create MechModel and add to RootModel
19       MechModel mech = new MechModel ("mech");
20       addModel (mech);
21
22       // create and add the ball and plate
23       RigidBody ball = RigidBody.createIcosahedralSphere ("ball", 0.8, 0.1, 1);
24       ball.setPose (new RigidTransform3d (0, 0, 2, 0.4, 0.1, 0.1));
25       mech.addRigidBody (ball);
26       RigidBody plate = RigidBody.createBox ("plate", 5, 5, 0.4, 1);
27       plate.setDynamic (false);
28       mech.addRigidBody (plate);
29
30       // turn on collisions
31       mech.setDefaultCollisionBehavior (true, 0.20);
32
33       // make ball transparent so that contacts can be seen more clearly
34       RenderProps.setFaceStyle (ball, Renderer.FaceStyle.NONE);
35       RenderProps.setShading (ball, Renderer.Shading.NONE);
36       RenderProps.setDrawEdges (ball, true);
37       RenderProps.setEdgeColor (ball, Color.WHITE);
38
39       // enable rendering of contacts normals and contours
40       CollisionManager cm = mech.getCollisionManager();
41       RenderProps.setVisible (cm, true);
42       RenderProps.setLineWidth (cm, 3);
43       RenderProps.setLineColor (cm, Color.RED);
44       RenderProps.setEdgeWidth (cm, 3);
45       RenderProps.setEdgeColor (cm, Color.BLUE);
46       cm.setDrawContactNormals (true);
47       cm.setDrawIntersectionContours (true);
48
49       // create a control panel to allow contact regularization to be set
50       ControlPanel panel = new ControlPanel();
51       panel.addWidget (mech.getCollisionManager(), "compliance");
52       panel.addWidget (mech.getCollisionManager(), "damping");
53       addControlPanel (panel);
54    }
55 }

The build() method starts by creating and adding a MechModel in the usual way (lines 19-20). The ball and plate are both created as rigid bodies (lines 22-28), with the ball pose set so that its origin is above the plate at (0, 0, 2) and its orientation is perturbed so that it will not land on the plate symmetrically (line 24). Collisions between the ball and plate are enabled at line 31, with a friction coefficient of 0.2. To allow better visualization of the contacts, the ball is made transparent by disabling the drawing of faces, and instead enabling the drawing of edges in white with no shading (lines 33-37).

Rendering of contacts and normals is established by setting the render properties of the collision manager (lines 39-47). First, the collision manager is set visible (which it is not by default). Then lines (used to render the contact normals) and edges (used to render to intersection contour) are set to red and blue, each with a pixel width of 3. Drawing of the normals and contour is enabled at lines 46-47.

Lastly, for interactively controlling contact compliance (Section 8.7.1), a control panel is built to allow users to adjust the collision manager’s compliance and damping properties (lines 49-53).

To run this example in ArtiSynth, select All demos > tutorial > BallPlateCollide from the Models menu. When run, the ball will collide with the plate and the contact normals and collision contours will be drawn as shown in Figure 8.13.

To enable compliant contact, set the compliance to a non-zero value. A value of 0.001 (which corresponds to a contact stiffness of 1000) causes the ball to bounce considerably when it lands. To counteract this bouncing, the damping should be set to a non-zero value. Since the ball has a mass of  0.21, formula (8.2) suggests that critical damping (for which \zeta=1) can be achieved with D\approx 30. This does in fact stop the bouncing. Increasing the compliance to 0.01 results in the ball penetrating the plate by a noticeable amount.

8.5.2 Example: rendering a color map

As described above, it is possible to use the drawColorMap property of the collision behavior to render a color map over the contact area showing a scalar value such as penetration depth or contact pressure. A simple example of this is defined in

  artisynth.demos.tutorial.PenetrationRender

which sets drawColorMap to PENETRATION_DEPTH in order to display the penetration depth of one hemispherical mesh with respect to another.

As mentioned above, proper results require that the collision mesh for the collidable on which the map is being drawn has a sufficiently high resolution.

Figure 8.14: PenetrationRender showing the penetration depth of the bottom mesh with respect to the top, with red indicating greater depth. A translation dragger fixture at the top is being used to move the top mesh around, while the penetration range and associated colors are displayed on the color bar at the right.

The complete source code is shown below:

1 package artisynth.demos.tutorial;
2
3 import java.awt.Color;
4
5 import maspack.geometry.PolygonalMesh;
6 import maspack.geometry.MeshFactory;
7 import maspack.matrix.RigidTransform3d;
8 import maspack.render.*;
9 import maspack.render.Renderer.FaceStyle;
10 import maspack.render.Renderer.Shading;
11 import artisynth.core.mechmodels.*;
12 import artisynth.core.mechmodels.CollisionManager.ColliderType;
13 import artisynth.core.mechmodels.CollisionBehavior.ColorMapType;
14 import artisynth.core.util.ScalarRange;
15 import artisynth.core.workspace.RootModel;
16 import artisynth.core.renderables.ColorBar;
17 import maspack.render.color.JetColorMap;
18
19 public class PenetrationRender extends RootModel {
20
21    // Convenience method for creating colors from [0-255] RGB values
22    private static Color createColor (int r, int g, int b) {
23       return new Color (r/255.0f, g/255.0f, b/255.0f);
24    }
25
26    private static Color CREAM = createColor (255, 255, 200);
27    private static Color GOLD = createColor (255, 150, 0);
28
29    // Creates and returns a rigid body built from a hemispherical mesh.  The
30    // body is centered at the origin, has a radius of ’rad’, and the z axis is
31    // scaled by ’zscale’.
32    RigidBody createHemiBody (
33       MechModel mech, String name, double rad, double zscale, boolean flipMesh) {
34
35       PolygonalMesh mesh = MeshFactory.createHemisphere (
36          rad, /*slices=*/20, /*levels=*/10);
37       mesh.scale (1, 1, zscale); // scale mesh in the z direction
38       if (flipMesh) {
39          // flip upside down is requested
40          mesh.transform (new RigidTransform3d (0, 0, 0, 0, 0, Math.PI));
41       }
42       RigidBody body = RigidBody.createFromMesh (
43          name, mesh, /*density=*/1000, /*scale=*/1.0);
44       mech.addRigidBody (body);
45       body.setDynamic (false);  // body is only parametrically controlled
46       return body;
47    }
48
49    // Creates and returns a ColorBar renderable object
50    public ColorBar createColorBar() {
51       ColorBar cbar = new ColorBar();
52       cbar.setName("colorBar");
53       cbar.setNumberFormat("%.2f");      // 2 decimal places
54       cbar.populateLabels(0.0, 1.0, 10); // Start with range [0,1], 10 ticks
55       cbar.setLocation(-100, 0.1, 20, 0.8);
56       cbar.setTextColor (Color.WHITE);
57       addRenderable(cbar);               // add to root model’s renderables
58       return cbar;
59    }
60
61    public void build (String[] args) {
62       MechModel mech = new MechModel ("mech");
63       addModel (mech);
64
65       // create first body and set its rendering properties
66       RigidBody body0 = createHemiBody (mech, "body0", 2, -0.5, false);
67       RenderProps.setFaceStyle (body0, FaceStyle.FRONT_AND_BACK);
68       RenderProps.setFaceColor (body0, CREAM);
69
70       // create second body and set its pose and rendering properties
71       RigidBody body1 = createHemiBody (mech, "body1", 1, 2.0, true);
72       body1.setPose (new RigidTransform3d (0, 0, 0.75));
73       RenderProps.setFaceStyle (body1, FaceStyle.NONE); // set up
74       RenderProps.setShading (body1, Shading.NONE);     // wireframe
75       RenderProps.setDrawEdges (body1, true);           // rendering
76       RenderProps.setEdgeColor (body1, GOLD);
77
78       // create and set a collision behavior between body0 and body1, and make
79       // collisions INACTIVE since we only care about graphical display
80       CollisionBehavior behav = new CollisionBehavior (true, 0);
81       behav.setMethod (CollisionBehavior.Method.INACTIVE);
82       behav.setDrawColorMap (ColorMapType.PENETRATION_DEPTH);
83       behav.setRenderingCollidable (1); // show penetration of mesh 0
84       mech.setCollisionBehavior (body0, body1, behav);
85
86       CollisionManager cm = mech.getCollisionManager();
87       // works better with open meshes if AJL_CONTOUR is selected
88       cm.setColliderType (ColliderType.AJL_CONTOUR);
89       // set other rendering properities in the collision manager:
90       RenderProps.setVisible (cm, true);    // enable collision rendering
91       cm.setDrawIntersectionContours(true); // draw contours ...
92       RenderProps.setEdgeWidth (cm, 3);     // with a line width of 3
93       RenderProps.setEdgeColor (cm, Color.BLUE); // and a blue color
94       // create a custom color map for rendering the penetration depth
95       JetColorMap map = new JetColorMap();
96       map.setColorArray (
97          new Color[] {
98             CREAM,                       // no penetration
99             createColor (255, 204, 153),
100             createColor (255, 153, 102),
101             createColor (255, 102, 51),
102             createColor (255, 51, 0),
103             createColor (204, 0, 0),     // most penetration
104          });
105       cm.setColorMap (map);
106       // set color map range to "auto fit".
107       cm.setColorMapRange (new ScalarRange(ScalarRange.Updating.AUTO_FIT));
108
109       // create a separate color bar to show depth values associated with the
110       // color map
111       ColorBar cbar = createColorBar();
112       cbar.setColorMap (map);
113    }
114
115    public void prerender(RenderList list) {
116       super.prerender(list); // call the regular prerender method first
117
118       // Update the color bar labels based on the collison manager’s
119       // color map range that was updated in super.prerender().
120       //
121       // Object references are obtained by name using ’findComponent’. This is
122       // more robust than using class member variables, since the latter will
123       // be lost if we save and restore this model from a file.
124       ColorBar cbar = (ColorBar)(renderables().get("colorBar"));
125       MechModel mech = (MechModel)findComponent ("models/mech");
126       RigidBody body0 = (RigidBody)mech.findComponent ("rigidBodies/body0");
127       RigidBody body1 = (RigidBody)mech.findComponent ("rigidBodies/body1");
128
129       CollisionManager cm = mech.getCollisionManager();
130       ScalarRange range = cm.getColorMapRange();
131       cbar.updateLabels(0, 1000*range.getUpperBound());
132    }
133 }

To improve visibility, the example uses two rigid bodies, each created from an open hemispherical mesh using the method createHemiBody() (lines 32-47). Because this example is strictly graphical, the bodies are set to be non-dynamic so that they can be moved around using the viewer’s graphical dragger fixtures (see the section “Transformer Tools” in the ArtiSynth User Interface Guide). Rendering properties for each body are set at lines 67-68 and 73-76, with the top body being rendered as a wireframe to improve visibility.

Lines 80-84 create and set a collision behavior between the two bodies with the drawColorMap property set to PENETRATION_DEPTH. Because for this example we want to show only the penetration and don’t want a collision response, we set the contact method to be Method.INACTIVE. At line 88, the collider type used by the collision manager is set to ColliderType.AJL_CONTOUR, since this provides more reliable penetration calculations for open meshes. Other rendering properties are set for the collision manager at lines 90-107, including a custom color map that varies between CREAM (the color of the mesh) for no penetration and dark red for maximum penetration. The updating of the color map range in the collision manager is set to AUTO_FIT so that it will be recomputed at every time step. (Since the collision manager’s color map range is set to “auto fit” by default, this is shown for illustrative purposes only. It is also possible to override the collision manager’s color map range by setting the colorMapRange property in specific collision behaviors.)

At line 111, a color bar is created and added to the scene, using the method createColorBar() (lines 50-59), to explicitly show the depth that corresponds to the different colors. The color bar is given the same color map that is used to render the depth. Since the depth range is updated every time step, it is also necessary to update the corresponding labels in the color bar. This is done by overriding the root model’s prerender() method (lines 115-132), where we obtain the color map range for the collision manager and use it to update the color bar labels. (Note that super.prerender(list) is called first, since the color map ranges are updated there.) References to the color bar, MechModel, and bodies are obtained using the CompositeComponent methods get() and findComponent(). This is more robust that storing these references in member variables, since the latter would be lost if the model is saved to and reloaded from a file.

To run this example in ArtiSynth, select All demos > tutorial > PenetrationRender from the Models menu. When run, the meshes will collide and render the penetration depth of the bottom mesh, as shown in Figure 8.14.

When defining the color map for rendering (lines 99-108 in the example), it is recommended that the color corresponding to zero be set to the face color of the collidable mesh. This will allow the color map to blend properly to the regular mesh color.

8.5.3 Example: rendering contact pressures

Color map rendering can also be used to render contact pressures, which can be particularly useful for FEM models. A simple example is defined in

  artisynth.demos.tutorial.ContactPressureRender

which sets the drawColorMap property of the collision behavior to CONTACT_PRESSURE in order to display a color map of the contact pressure. The example is similar to that of Section 8.5.2, which shows how to render penetration depth.

The caveats about color map rendering described in Section 8.5 apply. Pressure rendering is most useful and accurate when using vertex penetration contact. The resolution of the color map is limited by the resolution of the collision mesh for the collidable on which the map is drawn, and so the application should ensure that this is sufficiently fine. Also, to allow the map to blend properly with the rest of the collidable, the color corresponding to zero pressure should match the default face color for the collidable.

Figure 8.15: ContactPressureRender showing the contact pressure as a spherical FEM model falls onto an FEM sheet. The color map is drawn on the sheet model, with redder values indicating greater pressure.

The complete source code is shown below:

1 package artisynth.demos.tutorial;
2
3 import java.awt.Color;
4
5 import artisynth.core.femmodels.FemFactory;
6 import artisynth.core.femmodels.FemModel.SurfaceRender;
7 import artisynth.core.femmodels.FemModel3d;
8 import artisynth.core.femmodels.FemNode3d;
9 import artisynth.core.materials.LinearMaterial;
10 import artisynth.core.mechmodels.CollisionBehavior;
11 import artisynth.core.mechmodels.CollisionBehavior.ColorMapType;
12 import artisynth.core.mechmodels.CollisionManager;
13 import artisynth.core.mechmodels.MechModel;
14 import artisynth.core.renderables.ColorBar;
15 import artisynth.core.util.ScalarRange;
16 import artisynth.core.workspace.RootModel;
17 import maspack.matrix.RigidTransform3d;
18 import maspack.render.RenderList;
19 import maspack.render.RenderProps;
20 import maspack.render.color.JetColorMap;
21
22 public class ContactPressureRender extends RootModel {
23
24    double density = 1000;
25    double EPS = 1e-10;
26
27    // Convenience method for creating colors from [0-255] RGB values
28    private static Color createColor (int r, int g, int b) {
29       return new Color (r/255.0f, g/255.0f, b/255.0f);
30    }
31
32    private static Color CREAM = createColor (255, 255, 200);
33    private static Color BLUE_GRAY = createColor (153, 153, 255);
34
35    // Creates and returns a ColorBar renderable object
36    public ColorBar createColorBar() {
37       ColorBar cbar = new ColorBar();
38       cbar.setName("colorBar");
39       cbar.setNumberFormat("%.2f");      // 2 decimal places
40       cbar.populateLabels(0.0, 1.0, 10); // Start with range [0,1], 10 ticks
41       cbar.setLocation(-100, 0.1, 20, 0.8);
42       cbar.setTextColor (Color.WHITE);
43       addRenderable(cbar);               // add to root model’s renderables
44       return cbar;
45    }
46
47    public void build (String[] args) {
48       MechModel mech = new MechModel ("mech");
49       addModel (mech);
50
51       // create FEM ball
52       FemModel3d ball = new FemModel3d("ball");
53       ball.setDensity (density);
54       FemFactory.createIcosahedralSphere (ball, /*radius=*/0.1, /*ndivs=*/2, 1);
55       ball.setMaterial (new LinearMaterial (100000, 0.4));
56       mech.addModel (ball);
57
58       // create FEM sheet
59       FemModel3d sheet = new FemModel3d("sheet");
60       sheet.setDensity (density);
61       FemFactory.createHexGrid (
62          sheet, /*wx*/0.5, /*wy*/0.3, /*wz*/0.05, /*nx*/20, /*ny*/10, /*nz*/1);
63       sheet.transformGeometry (new RigidTransform3d (0, 0, -0.2));
64       sheet.setMaterial (new LinearMaterial (500000, 0.4));
65       sheet.setSurfaceRendering (SurfaceRender.Shaded);
66       mech.addModel (sheet);
67
68       // fix the side nodes of the surface
69       for (FemNode3d n : sheet.getNodes()) {
70          double x = n.getPosition().x;
71          if (Math.abs(x-(-0.25)) <= EPS || Math.abs(x-(0.25)) <= EPS) {
72             n.setDynamic (false);
73          }
74       }
75
76       // create and set a collision behavior between the ball and surface.
77       CollisionBehavior behav = new CollisionBehavior (true, 0);
78       behav.setDrawColorMap (ColorMapType.CONTACT_PRESSURE);
79       behav.setRenderingCollidable (1); // show color map on collidable 1 (sheet);
80       behav.setColorMapRange (new ScalarRange(0, 15000.0));
81       mech.setCollisionBehavior (ball, sheet, behav);
82
83       CollisionManager cm = mech.getCollisionManager();
84       // set rendering properties in the collision manager:
85       RenderProps.setVisible (cm, true);    // enable collision rendering
86       // create a custom color map for rendering the penetration depth
87       JetColorMap map = new JetColorMap();
88       map.setColorArray (
89          new Color[] {
90             CREAM,                       // no penetration
91             createColor (255, 204, 153),
92             createColor (255, 153, 102),
93             createColor (255, 102, 51),
94             createColor (255, 51, 0),
95             createColor (204, 0, 0),     // most penetration
96          });
97       cm.setColorMap (map);
98
99       // create a separate color bar to show color map pressure values
100       ColorBar cbar = createColorBar();
101       cbar.updateLabels(0, 15000);
102       cbar.setColorMap (map);
103
104       // set color for all bodies
105       RenderProps.setFaceColor (mech, CREAM);
106       RenderProps.setLineColor (mech, BLUE_GRAY);
107    }
108 }

To begin, the demo creates two FEM models: a spherical ball (lines 52-56) and a rectangular sheet (lines 59-66), and then fixes the end nodes of the sheet (lines 69-74). Surface rendering is enabled for the sheet (line 65), but not for the ball, in order to improve the visibility of the color map.

Lines 77-81 create and set a collision behavior between the two models, with the drawColorMap property set to CONTACT_PRESSURE. Because for this example we want the color map to be drawn on the second collidable (the sheet), we set the setColorMapCollidable property to 1 (line 79); otherwise, the default value of 0 would cause the color map to be drawn on the first collidable (the ball). (Alternatively, we could have simply defined the collision behavior as being between the surface and the ball instead of the ball and the sheet.) The color map range is explicitly set to lie between [0,15000] (line 80); this is in contrast to the example in Section 8.5.2, where the range is auto-updated on each step. The color range is also set explicitly in the behavior, but if multiple objects were colliding it would likely be preferable to set it in the collision manager (with the behavior’s value left as null) to ensure a uniform render range across all collisions. Other rendering properties are set for the collision manager at lines 83-97, including a custom color map that varies between CREAM (the color of the mesh) for no pressure and dark red for maximum pressure.

At line 100, a color bar is created and added to the scene, using the method createColorBar() (lines 36-45), to explicitly show the pressure that corresponds to the different colors. The color bar is given the same color map and value range used to render the pressure. Finally, default face and line colors for all components in the model are set at lines 105-106.

To run this example in ArtiSynth, select All demos > tutorial > ContactPressureRender from the Models menu. When run, the FEM models will collide and render the contact pressure on the sheet, as shown in Figure 8.15.