Two-Dimensional Mathlets

While LiveGraphics3D was designed to display three-dimensional graphics, it is also often used to display two-dimensional graphics. In principle, two-dimensional graphics may be specified as a special case of three-dimensional graphics with all z-coordinates set to 0. However, there are some additional issues that require special attention.

As an example, we will look at a typical two-dimensional problem: the convex hull of several points, say five, in two dimensions. Our basic approach to this problem will be very simple: we just show all triangles defined by all possible triples of points. Since the user should be able to drag all points, their coordinates have to be defined by independent variables. Here is a first try:

<html><body>
<applet archive="live.jar" code="Live.class" width="500" height="300">
<param name="INDEPENDENT_VARIABLES"
       value="{x0 -> 0.0, y0 -> 0.,
               x1 -> 1.0, y1 -> 0.1,
               x2 -> 0.5, y2 -> 0.5,
               x3 -> 0.8, y3 -> 1.0,
               x4 -> 0.2, y4 -> 0.9}"
<param name="INPUT"
       value="Graphics3D[{{PointSize[0.04], RGBColor[1, 0, 0],
                        Point[{x0, y0, 0}], Point[{x1, y1, 0}], 
                        Point[{x2, y2, 0}], Point[{x3, y3, 0}], 
                        Point[{x4, y4, 0}]
		},	(* Done with points; now include triangles *)
		{  
			RGBColor[0.7, 0.7, 0.7],
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x2, y2, 0}}],
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x3, y3, 0}}],
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x4, y4, 0}}],
                        Polygon[{{x0, y0, 0}, {x2, y2, 0}, {x3, y3, 0}}],
                        Polygon[{{x0, y0, 0}, {x2, y2, 0}, {x4, y4, 0}}],
                        Polygon[{{x0, y0, 0}, {x3, y3, 0}, {x4, y4, 0}}],
                        Polygon[{{x1, y1, 0}, {x2, y2, 0}, {x3, y3, 0}}],
                        Polygon[{{x1, y1, 0}, {x2, y2, 0}, {x4, y4, 0}}],
                        Polygon[{{x1, y1, 0}, {x3, y3, 0}, {x4, y4, 0}}],
                        Polygon[{{x2, y2, 0}, {x3, y3, 0}, {x4, y4, 0}}]
		}},	(* Done with primitives; now include options *)
		PlotRange -> {{0,1}, {0,1}, {-0.1,0.1}},
		Boxed -> False]" >
</applet>
</body></html>
Resulting Applet:

Needless to say, this first attempt is not an attractive mathlet. You can drag the red points in the xy-plane and get a rough idea of what their convex hull is, but the overlapping triangles are ugly and obscure the fifth point. We will have to address several points.

As always the user is able to rotate this scene. For two-dimensional graphics, however, general three-dimensional rotations are quite pointless. We can disable them by setting the applet parameter MOUSE_DRAG_ACTION to NONE. (See the example below.)

For two-dimensional graphics, lighting of polygons is usually not required. We can disable lighting by specifying the option Lighting -> False.

Since our two-dimensional graphics are in the xy-plane, the best point of view is on the z-axis. This is specified with the option ViewPoint -> {0, 0, 1000}. The length of this vector determines the distance of a virtual camera to the center of the bounding box specified by PlotRange. By specifying a large vector, the effects of the perspective projection are minimized resulting in an almost orthogonal projection. (For a typical perspective projection the length should be between 3 and 4.) In order to ensure that the y-axis is pointing upwards we also specify ViewVertical -> {0, 1, 0} (the length of this vector is meaningless). With these settings, the x-axis has to point to the right since LiveGraphics3D uses a right-handed coordinate system (and we have specified a point of view on the positive z-axis).

In the example, all primitives (polygons and points) are in the same plane; therefore, the depth sorting is numerically unstable and occlusions of points and triangles are unpredictable. We solve this problem with two "tricks": First, the z-coordinate of all points are slightly increased such that they are all in front of the triangles and never occluded by them. With the help of multiple z values, you are free to specify an arbitrary occlusion order for two-dimensional primitives. Note, however, that you should use orthogonal projection, i.e., a large vector for ViewPoint, in order to avoid perspective effects for z values different from 0.

Secondly, we disable edges of polygons by specifying an empty directive EdgeForm[]. This does not avoid the unstable sorting; however, we do not need to care about it any longer since all triangles have exactly the same color.

With these enhancements, our example takes this form:

<html><body>
<applet archive="live.jar" code="Live.class" width="500" height="300">
<param name="MOUSE_DRAG_ACTION" value="NONE">
<param name="INDEPENDENT_VARIABLES"
       value="{x0 -> 0.0, y0 -> 0.,
               x1 -> 1.0, y1 -> 0.1,
               x2 -> 0.5, y2 -> 0.5,
               x3 -> 0.8, y3 -> 1.0,
               x4 -> 0.2, y4 -> 0.9}"
<param name="INPUT"
       value="Graphics3D[{{PointSize[0.04], RGBColor[1, 0, 0],
                        Point[{x0, y0, 0.1}], Point[{x1, y1, 0.1}], 
                        Point[{x2, y2, 0.1}], Point[{x3, y3, 0.1}], 
                        Point[{x4, y4, 0.1}] (* z = 0.1 for all points *)
		},
		{  
                        EdgeForm[], (* Disable edges of Polygons *)
			RGBColor[0.7, 0.7, 0.7],  
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x2, y2, 0}}],
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x3, y3, 0}}],
                        Polygon[{{x0, y0, 0}, {x1, y1, 0}, {x4, y4, 0}}],
                        Polygon[{{x0, y0, 0}, {x2, y2, 0}, {x3, y3, 0}}],
                        Polygon[{{x0, y0, 0}, {x2, y2, 0}, {x4, y4, 0}}],
                        Polygon[{{x0, y0, 0}, {x3, y3, 0}, {x4, y4, 0}}],
                        Polygon[{{x1, y1, 0}, {x2, y2, 0}, {x3, y3, 0}}],
                        Polygon[{{x1, y1, 0}, {x2, y2, 0}, {x4, y4, 0}}],
                        Polygon[{{x1, y1, 0}, {x3, y3, 0}, {x4, y4, 0}}],
                        Polygon[{{x2, y2, 0}, {x3, y3, 0}, {x4, y4, 0}}]
		}},
		PlotRange -> {{0,1}, {0,1}, {-0.1,0.1}},
		Boxed -> False, 
                Lighting -> False, (* No lighting *)
                ViewPoint -> {0, 0, 1000}, (* Projection of xy plane *) 
                ViewVertical -> {0, 1, 0}, (* Y-axis upwards *)
]" >
</applet>
</body></html>
Resulting Applet:

We can show the actual convex polyline of the convex hull by adding thick lines between all pairs of points in an additional "depth layer" with z coordinates smaller than 0. All lines within the convex hull will be occluded by the triangles, while at least half of the thick lines at the edges of the convex hull are visible. The additional primitives are defined in this list:

                (* This list is inserted in the previous code
                after (or before) the list of polygons *)
		{  
                        Thickness[0.02], (* Thick lines *)
			RGBColor[0, 0, 0], (* Black lines *)  
                        Line[{{x0, y0, -0.1}, {x1, y1, -0.1}}],
                        Line[{{x0, y0, -0.1}, {x2, y2, -0.1}}],
                        Line[{{x0, y0, -0.1}, {x3, y3, -0.1}}],
                        Line[{{x0, y0, -0.1}, {x4, y4, -0.1}}],
                        Line[{{x1, y1, -0.1}, {x2, y2, -0.1}}],
                        Line[{{x1, y1, -0.1}, {x3, y3, -0.1}}],
                        Line[{{x1, y1, -0.1}, {x4, y4, -0.1}}],
                        Line[{{x2, y2, -0.1}, {x3, y3, -0.1}}],
                        Line[{{x2, y2, -0.1}, {x4, y4, -0.1}}],
                        Line[{{x3, y3, -0.1}, {x4, y4, -0.1}}]
		}

A nice effect can be accomplished by additionally coloring the triangles white with the help of the directive RGBColor[1, 1, 1], i.e., making them indistinguishable from the background.

Resulting Applet:

This completes our two-dimensional example. It should be noted that we haven't actually constructed a convex hull but rather used a trick to display it. A more "constructive" way of approaching this problem would be to check for each line between two points whether all other points are on the same side of it. Here is an example for the line between {x0,y0,0} and {x1,y1,0}:

      { 
            If[Sign[x1 y0 - x2 y0 - x0 y1 + x2 y1 + x0 y2 - x1 y2] == 
               Sign[x1 y0 - x3 y0 - x0 y1 + x3 y1 + x0 y3 - x1 y3] && 
               Sign[x1 y0 - x3 y0 - x0 y1 + x3 y1 + x0 y3 - x1 y3] == 
               Sign[x1 y0 - x4 y0 - x0 y1 + x4 y1 + x0 y4 - x1 y4], 
               Line[{{x0, y0, 0}, {x1, y1, 0}}], 
               {}]
            (* Analogously for all other pairs of points. *)
      }

In general this approach is preferable, among other reasons because it can be adapted for convex hulls in three dimensions. However, it results in rather large expressions, which we wanted to avoid in this introduction.


Next Page: Stereo Images

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