Intersecting Objects

As discussed in the previous section, LiveGraphics3D employs the very simple "painter's algorithm" to render primitives. Unfortunately, this algorithm will often fail to produce correct occlusions, in particular for intersecting objects. In this section we discuss some typical problems and common solutions.

In order to illustrate the problem for a line intersecting a polygon, consider a line segment between two user-specified points {x0,y0,z0} and {x1,y1,z1} and a polygon in the plane z = 0 defined by the points {0,0,0}, {1,0,0}, {1,1,0}, and {0,1,0}. Here is a mathlet that visualizes the scene:

<html><body>
<applet archive="live.jar" code="Live.class" width="500" height="300">
<param name="INDEPENDENT_VARIABLES"
       value="{x0 -> 0.5, y0 -> 0.5, z0 -> -1,
               x1 -> 0.5, y1 -> 0.5, z0 -> 1}">
<param name="INPUT"
       value="Graphics3D[{PointSize[0.04], RGBColor[1, 0, 0],
                         Point[{x0, y0, z0}], Point[{x1, y1, z1}],
                         Thickness[0.01], RGBColor[0, 0, 0],
                         Line[{{x0, y0, z0}, {x1, y1, z1}}],
                         RGBColor[0.7, 0.7, 0.7],
                         Polygon[{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, 
                                {0, 1, 0}}]
                },	(* Done with primitives; now include options *)
		PlotRange -> {{0,1}, {0,1}, {-1,1}},
		Boxed -> False]" >
</applet>
</body></html>
Resulting Applet:

By rotating the scene and/or dragging the points defining the line, you can convince yourself that either the polygon will occlude the line or the line will occlude the polygon. If the line intersects the polygon, this will result in an incorrect image.

One very simple solution to this kind of problem is to only render the outline of the polygon. By using four Line primitives (instead of one) and also coloring them in the same way as the user-specified line, almost all incorrect occlusions are avoided:

<html><body>
<applet archive="live.jar" code="Live.class" width="500" height="300">
<param name="INDEPENDENT_VARIABLES"
       value="{x0 -> 0.5, y0 -> 0.5, z0 -> -1,
               x1 -> 0.5, y1 -> 0.5, z0 -> 1}">
<param name="INPUT"
       value="Graphics3D[{PointSize[0.04], RGBColor[1, 0, 0],
                         Point[{x0, y0, z0}], Point[{x1, y1, z1}],
                         Thickness[0.01], RGBColor[0, 0, 0],
                         Line[{{x0, y0, z0}, {x1, y1, z1}}],
                         Thickness[0.005], RGBColor[0, 0, 0],
                         Line[{{0, 0, 0}, {1, 0, 0}}],
                         Line[{{1, 0, 0}, {1, 1, 0}}], 
                         Line[{{1, 1, 0}, {0, 1, 0}}], 
                         Line[{{0, 1, 0}, {0, 0, 0}}]
                },	(* Done with primitives; now include options *)
		PlotRange -> {{0,1}, {0,1}, {-1,1}},
		Boxed -> False]" >
</applet>
</body></html>
Resulting Applet:

While this approach is often suitable, a better solution is required in many cases. In general, most other solutions are based on avoiding intersections by splitting primitives at intersection points. In our example, the line segment from {x0,y0,z0} to {x1,y1,z1} should be split into two parts, say from {x0,y0,z0} to {x2,y2,z2} and from {x2,y2,z2} to {x1,y1,z1}. All we need to do is to compute the intersection point {x2,y2,z2} of the line with the plane z=0. This is rather straightforward and implemented with the help of dependent variables in the example below. But wait a moment! What if the line segment does not intersect the plane z=0 at all, i.e., if z0 and z1 are both either smaller than 0 or greater than 0? In this case we should actually not split the original line. There are multiple ways to handle this case. In the example below, we simply set the coordinates of the intersection point to {x0,y0,z0} in case the line does not intersect the plane at z=0. Thus, the line segment between {x0,y0,z0} to {x2,y2,z2} will collapse to the single point {x0,y0,z0} and we don't need to bother about it.

<html><body>
<applet archive="live.jar" code="Live.class" width="500" height="300">
<param name="INDEPENDENT_VARIABLES"
       value="{x0 -> 0.5, y0 -> 0.5, z0 -> -1,
               x1 -> 0.5, y1 -> 0.5, z0 -> 1}">
<param name="DEPENDENT_VARIABLES"
       value="{x2 -> If[(z0 < 0 && z1 < 0) || (z0 > 0 && z1 > 0), 
                        x0, 
                        x0 + (x1 - x0) * (0 - z0) / (z1 - z0)],
               y2 -> If[(z0 < 0 && z1 < 0) || (z0 > 0 && z1 > 0), 
                        y0, 
                        y0 + (y1 - y0) * (0 - z0) / (z1 - z0)],
               z2 -> If[(z0 < 0 && z1 < 0) || (z0 > 0 && z1 > 0), 
                        z0, 
                        0]}">
<param name="INPUT"
       value="Graphics3D[{PointSize[0.04], RGBColor[1, 0, 0],
                         Point[{x0, y0, z0}], Point[{x1, y1, z1}],
                         Thickness[0.01], RGBColor[0, 0, 0],
                         Line[{{x0, y0, z0}, {x2, y2, z2}}],
                         Line[{{x2, y2, z2}, {x1, y1, z1}}],
                         RGBColor[0.7, 0.7, 0.7],
                         Polygon[{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, 
                                 {0, 1, 0}}]
                },	(* Done with primitives; now include options *)
		PlotRange -> {{0,1}, {0,1}, {-1,1}},
		Boxed -> False]" >
</applet>
</body></html>
Resulting Applet:

This approach effectively avoids intersecting objects; however, it does not guarantee correct occlusions: By placing one of the points close to the polygon, you can easily generate an incorrect rendering. However, for many applications this way of splitting lines generates acceptable results.

If you have a closer look at the code above, you might realize that the computation of x2 and y2 includes a division by the difference between two user-specified coordinates, which might result in a division by zero. Do we have to take care of this problem? And what about square roots of potentially negative numbers?

In general, you don't need to care about these cases: whenever a division by zero occurs or the result of any function is not defined in the real domain, LiveGraphics3D will reject the last user input and revert to the last valid configuration. The only exception is the configuration defined by the initial values of the independent variables: if any expression cannot be evaluated for these values, LiveGraphics3D will reject the input and abort the execution. Note that no user action is involved in this case; thus, it is usually easily checked by starting a mathlet.

If it is not possible (or not worth the effort) to compute the intersection between a line and a surface, you can still try to split the line primitive into a set of smaller line primitives. These are more likely to be sorted correctly. Moreover, the visual error is usually reduced this way.

Apart from the intersection of lines with polygons, polygons intersecting other polygons will also raise occlusion errors. For an example, consider the following mathlet:

<html><body>
<applet archive="live.jar" code="Live.class" width="300" height="300">
<param name="INPUT"
       value="Graphics3D[{
		Polygon[{{-1, 0, 1}, {1, 0, 1}, {1, 0, -1}, {-1, 0, -1}}],
		Polygon[{{0, -1, 1}, {0, 1, 1}, {0, 1, -1}, {0, -1, -1}}]
              }, Boxed -> False]">
</applet>
</body></html>
Resulting Applet:

In order to avoid these unpleasant occlusion errors, you will usually have to split the polygons at the line of intersection:

<html><body>
<applet archive="live.jar" code="Live.class" width="300" height="300">
<param name="INPUT"
       value="Graphics3D[{
		Polygon[{{-1, 0, 1}, {0, 0, 1}, {0, 0, -1}, {-1, 0, -1}}], 
		Polygon[{{0, 0, 1}, {1, 0, 1}, {1, 0, -1}, {0, 0, -1}}],
		Polygon[{{0, -1, 1}, {0, 0, 1}, {0, 0, -1}, {0, -1, -1}}], 
		Polygon[{{0, 0, 1}, {0, 1, 1}, {0, 1, -1}, {0, 0, -1}}]
      }, Boxed -> False]">
</applet>
</body></html>
Resulting Applet:

Unfortunately, the computation of intersections between polygons tends to be rather complicated. Thus, we can only recommend this approach for static objects. Moreover, you should consider computing these intersections with the help of other programming tools. If you have access to Mathematica, you could use the free package LiveGraphics3D.m which is linked from the documentation of LiveGraphics3D (Kraus). This package was employed to split intersecting polygons for many of the mathlets that are part of the online encyclopedia MathWorld (Weisstein).


Next Page: Two-Dimensional Applets

Table of Contents

  1. Introduction
  2. LiveGraphics3D Overview
  3. LiveGraphics3D Input
  4. Parametrized Graphics
  5. Moving Lines and Polygons
  6. Including Text
  7. Labeling Axes and Plots
  8. Animations
  9. Occlusions of Objects
  10. Intersecting Objects
  11. Two-Dimensional Mathlets
  12. Stereo Images
  13. Generating Graphical Input
  14. Advanced Examples
  15. Future Directions
  16. References