Charles Petzold



Using the Matrix3DProjection Class in Silverlight 3

July 23, 2009
Roscoe, N.Y.

About two years ago, I did a blog entry entitled Non-Affine Transforms in 2D?, which used the 3D capabilities in WPF to simulate a 2D non-affine transform. The demo program let you independently grab the four corners of a bitmap and pull them into arbitrary locations.

Early this year, I created a 2D non-affine transform structure for Silverlight in the blog entry Non-Affine Transforms in Silverlight, and used it to manipulate all the individual Point objects in a collection of Polygons that contributed to a piece of Office clip art.

All this time, I didn't know that I was priming myself for the new Matrix3DProjection class in Silverlight 3. You use this class to apply non-affine 3D transforms to regular 2D Silverlight objects, as is demonstrated by the following program that basically merges the demo programs in the two previous blog entries but is simpler than either of them:


Matrix3DProjectionDemo.html

The program lets you choose a big Button, a collection of Polygon objects defining a piece of Office clip-art, or an Image displaying a bitmap of my face and shoulders. Drag the blue dots to deform the element. The program works best if you maintain a convex quadrilateral!

Some background: Normally in a 2D graphics system like WPF or Silverlight, you can apply graphical transforms based on combinations of translation, scaling, rotation, and skewing, but these are all affine transforms: A rectangle is never transformed into anything other than a parallelogram.

2D graphics transforms are represented as 3×3 matrices, but for affine transforms, the last row of the matrix always consists of the numbers 0, 0, and 1. If those values were changable, non-affine transforms would be available, and you could define transforms that taper the sides of transformed rectangles so the result isn't necessarily a parallelogram.

In 3D graphics, transforms are represented as 4×4 matrices, and non-affine transforms are customarily allowed because they are required for perspective effects. The non-affine transform effectively tapers the size of the image away from the camera. (My book 3D Programming for Windows: Three-Dimensional Graphics Programming for the Windows Presentation Foundation goes into much detail on 3D transforms, which is crucial information even if you're not specifically doing WPF 3D. Also, the book could really use a few more sales to convince Microsoft Press to let me write more books in the future.)

Silverlight 3 defines a new Projection property on UIElement of type Projection, an abstract class with two derivatives: PlaneProjection (which I haven't worked with yet but seems to create a 3D non-affine transform from 12 settable properties) and Matrix3DProjection, which lets you specify the transform directly as a Matrix3D object. The Matrix3D structure has been imported into Silverlight from WPF, and it is now the only member of the new Silverlight System.Windows.Media.Media3D namespace. (The demo program displays the resultant Matrix3D defined by the location of the four corners of the deformed element.)

It seems a little strange to be working with a 3D matrix that is only applied to 2D elements, but I think it makes more sense than opening up the existing 2D Matrix structure to allow non-affine transforms, or creating a new matrix structure just for this purpose.

Deforming a rectangular object to fit an arbitrary convex quadrilateral is just one application of the new Silverlight 3D transform. The first blog entry cited above discusses the math I've used; the algorithm is implemented in the CalculateNewTransform method in MainPage.xaml.cs in the downloadable source code.