I recently needed to brush up on WPF and had the idea of adding a Clipping Plane to Rod Stephens' excellent WPF Menger Sponge. While I was at it, I added some other features which I will describe via the following links:
- Clipping Plane
- Progress Bar
- Menu Bar
Download and Build
Download the MengerSpongeClipping.zip file and extract it. Open the
MengerSpongeClipping project with Visual Studio. Build it by pressing F6; the project should build successfully with no errors. Press F5 to run the
MengerSpongeClipping project in debug mode.
Using the Code
To clip the Menger Sponge, simply click the "Clip" button. You can orient the clipping plane using the sliders as illustrated in the above image. You can rotate the clipping plane about the x, y, or z axes using the "Rotate Plane" Sliders, and you can translate it along the x, y, or z axes using "Translate Plane" sliders. For this version, the Clipping Plane is assumed to be infinite in all directions, so the translation is for demonstration purposes and does not affect the actual clipping. (In a future version, I plan on having the clipping algorithm assume a finite Clipping Plane, and therefore take into account the translation of the Clipping Plane, so parts of the Sponge that are outside are not clipped.) The positive side of the Clipping Plane is an opaque green; the negative side is an opaque blue.
My implementation of the clipping plane is simple: the distance from the plane to each rectangle center is computed, and rectangles with distance values less than zero (that is, behind the opaque blue side) are discarded. The method
DrawClippingPlane() performs the following steps:
- Rotate the Clipping Plane
- Translate the Clipping Plane
- Get the transformed coordinates of the Clipping Plane
- Determine Equation of the Clipping Plane in the form Ax + By + Cz + D = 0
DistanceFromPlaneToPoint() uses the Equation of the Clipping Plane to determine the distance to the rectangle's center. The method
RemoveClippedRectangles() (described below) computes the distance and removes clipped rectangles from
RectanglesMade. The method
DrawClippedSponge() (also described below) redraws the Menger Sponge.
I used Mr. Stephens' WPF Cylinder code to draw the x, y, z axes. You can toggle them on and off by pressing the "A" (the Axis Toggle Key), or by using "View" from the Menu Bar. To label the axes, I used Eric Sink's fantastic
CreateTextLabel3D() method; I added another parameter, a
Transform3DGroup transform. See TextLabel3D.cs for details.
In WPF (and DirectX and OpenGL), a directional light is a light that projects its effect along a specified vector. The
Color lightColor1 and
Color lightColor2 are inputs to the
DirectionalLight() constructor. Since it can sometimes be hard to visualize where the light is pointing by specifying a vector, I used Mr. Stephens' WPF Cone code to draw "flashlights" (a flashlight is known as a torch in the UK) that shows where the directional light is pointing. There are two directional lights named appropriately enough Light 1 and Light 2. The flashlights are disabled (not displayed) by default for clarity. To enable (display) them, press the "F" key (the Flashlight Toggle Key) or use "View" from the Menu Bar. Whether the flashlights are enabled or not does not affect the actual lighting; in other words, they are only used for making the lighting vectors easier to visualize.
You can experiment with the two lights by setting the sliders for the direction vectors and combo box for the colors. If you select "None" from the colors combo box, the corresponding
DirectionalLight() is turned off, and the flashlight is displayed in wireframe code supplied by Mr. Stephens. (I spent some time trying to use 3D Tools wireframe, but to no avail.) In the illustration below, I've enabled the flashlights, zoomed out a little (using the minus key) and disabled the Clipping Plane (for clarity) by pressing the "P" key (the Clipping Plane Toggle Key). Next, I set the direction vectors as shown, set Light 1 (in the background) to blue and Light 2 (in the foreground) to red. Note how the red light illuminates the X and Z faces of the Menger Sponge to red, and the Y face is a shade of purple due to the combination of blue and red:
In the next illustration, after I rotate the scene 195 degrees using the arrow keys (note how theta goes from 60° to 255°), and tilt it from phi=30 degrees to phi=40 degrees, the effect of the Light 1, the blue light (now in the foreground) can be seen illuminating the -X and -Z faces of the Menger Sponge to blue:
You can add textures to the Menger Sponge by selecting an image file from the "Texture" combo box as shown below. In order to use this feature, you first need to modify app.config to point to the image files, for example:
<setting name="ImageFileLocation" serializeAs="String">
The code for this is from yet another useful article by Mr. Stephens on textures.
WPF Progress Bar
Since the number of rectangles drawn is determined by the
SpongeDepth, I added a WPF Progress Bar to show the progress of re-drawing the clipped Menger Sponge, as illustrated below. The code demonstrates the use of a
BackgroundWorker object. To get the full effect if you're running on a fast machine, you might want to run the program in Debug mode by pressing F5 since there is some console output in debug mode which slows the process somewhat.
When you click the "Clip" button, the
BtnClip_Click() callback creates the
BackgroundWorker object in order to perform the clipping operation on a separate thread. Note the use of
RemoveClippedRectangles() which is needed since the method
DrawClippedSponge() accesses the
spongeMesh object which is owned by a different thread. The method
RemoveClippedRectangles() calculates the progress.
The Menu Bar has "File", "View" and "Help" menu items. "File->Save As..." allows you to save your settings to a text file, and "File->Open" allows you to retrieve them. File->Reset will reset the Clipping Plane rotations and translations, Camera movements, lighting settings, and textures to their initial values (as will pressing the Escape key). "View" allows you to toggle the Axes, Clipping Plane, Sponge, and Plane Normal. You can also use the "A", "P", "S", and "N" toggle keys, respectively. Since the Plane Normal is small (a unit vector), you will have to turn off the Sponge (and Axes, if the Plane Normal is aligned with an axis) in order to see it.