Charles Petzold

The Petzold.Media3D Library: Algorithmic Transforms

August 20, 2007
Roscoe, N.Y.

In WPF 3D, it's great to have high-level classes such as TranslateTransform3D, ScaleTransform3D, and RotateTransform3D for performing common types of transforms. It's also great to have MatrixTransform3D that encompasses all those transforms, and also performs skewing. As an extra bonus MatrixTransform3D allows non-affine transforms as well, so you can easily implement 3D taper transforms, as I demonstrated here and here.

However, there are times you'd like to apply transforms to 3D figures that can't be represented with simple matrices. These I call "algorithmic transforms" because they must be expressed an algorithm applied to a collection of 3D points.

In the Petzold.Media3D library (available here), the ModelVisualBase class (described here) has a facility to apply algorithmic transforms to 3D primitives. ModelVisualBase defines a property named AlgorithmicTransforms of type AlgorithmicTransformCollection. This is a freezable collection created simply by deriving from FreezableCollection<AlgorithmicTransform>. The AlgorithmicTransform class inherits from Animatable and defines just one method:

Any class that derives from AlgorithmicTransform needs to implement a Transform method that performs an in-place transform of the collection of points that define the vertices of a figure in 3D space. For example, a derived class could override Transform like this:

That's a transform that squares all the X coordinates, and it cannot be expressed in the standard matrix form.

More commonly, a class that derives from AlgorithmicTransform will define several properties that serve as parameters for the transform. Of course, you'll want to back those properties with dependency properties so they can be targets of bindings and animations.

Notice that the ModelVisualBase class defines the AlgorithmicTransforms property as a collection; you can have multiple algorithmic transforms applied to the same figure in a chain: The first one gets the collection of vertices normally generated for the figure; each transform gets the transformed collection from the previous algorithmic-transform object in the collection. The final collection is used for the MeshGeometry3D. Because AlgorithmicTransforms is a freezable collection, any change to any dependency property in any object in the collection signals a property-changed event, and the class recalculates the triangle mesh based on the new transform.

I wanted the Petzold.Media3D library to have several useful classes that derive from AlgorithmicTransform but I only got one finished, which I called Twister. (I also derived from AlgorithmicTransform to simulate a turning page of a book in the Goblin Market application in Chapter 9 of 3D Programming for Windows and runnable from this page

The Twister class defines a twisting transform, which is similar to a rotation transform except that different degrees of rotation are applied to the coordinates. The Twister class defines an Angle property (with a default value of 0) and an Axis property that defines the Vector3D object around which the rotation occurs. The default value of Axis is (0, 1, 0). Twister also defines a Center property with a default value of (0, 0, 0). The plane orthogonal to the Axis vector and which contains the Center point is not transformed at all. All planes parallel to that plane are rotated by a number of degrees equal to the Angle property times the distance of the plane from the center plane.

For example, for default values of Axis and Center, the Y=0 plane — that is, the plane of all points (x, 0, z) — is not rotated at all. The Y=1 plane is rotated Angle degrees; the Y=2 plane is rotated 2 * Angle degrees. the Y=–0.5 plane is rotated –0.5 * Angle degrees.

You can run the following XAML file in XamlCruncher 2.0 with the Petzold.Media3D library loaded:


Or, you can just gaze upon the XAML file and run an XBAP created from that XAML file here:


The Cuboid class from the Petzold.Media3D library creates a cuboid based on an Origin (the lower-left-rear corner) and Width, Height, and Depth properties. In this example, I've set the Origin to (–1.5, –0.5, –0.5) and Width to 3, but the Height and Depth have default values of 1. The AlgorithmicTransforms collection is set to a Twister object with an Axis of (1, 0, 0). The Angle property is bound to a Slider with a range of –180 to 180. The center of the cuboid on the YZ plane isn't rotated, but the ends at X values of 0.5 and –0.5 are rotated under control of the Slider up to 270 (that is 1.5 times 180) degrees in either direction:

This transform is smoothest when the figure is defined by a large number of triangles. In this example, I've left the Slices (divisions along the X dimension), Stacks (Y dimension), and Slivers (Z dimension) properties at their default values of 10. (The Cuboid class has a problem when these values do not equal each other; I'll try to fix that problem shortly.)

The second example uses the Cylinder class and covers it with a bitmap of my face. The cylinder sits upright on the Y axis and is rotated by an animation around the axis (0, 1, 0):


If you try to run this XAML file in XamlCruncher, you'll also need to fix the reference to the PetzoldTattoo.jpg file. (It's included in Chapter 5 of the downloadable code for the book.) Here's an XBAP based on that same XAML file:


The coordinates are set so that the bottom doesn't rotate at all, but the top rotates a total of 1080 degrees in either direction:

When I first tried this little program, I left the Stacks property for Cylinder at its default value of 1 and I thought something was broken because the center of the cylinder got very small. But if you work out how the coordinates of the triangles are being transformed, that result is entirely predicatble.

Buy my book and we'll both be happy! quantumbooks
Barnes & Noble Amazon Canada Amazon UK
Amazon Fran├žais Amazon Deutsch Amazon Japan