BLOG.CSHARPHELPER.COM

### Understand three-dimensional drawing with WPF, XAML, and C#

(As predicted, my work schedule is heating up so I may not be able to post as often as usual for a while.)

The previous examples I've posted that use XAML to draw three-dimensional objects are pretty cool, but they're really basically toys. You can sit down and work out the coordinates needed to draw a cube or (with a bit of work) a tetrahedron. However, if you want to draw anything really complicated, such as a three-dimensional surface or a complex world like the one used in World of Warcraft, you're going to need the computer to generate the coordinates of the objects you're drawing. You could write a program to calculate those coordinates and use them to generate XAML code, but that would be cumbersome. It would add an extra step and wouldn't let the drawing program change the coordinates at runtime.

Obviously the solution is to make the program generate (and possibly modify) the objects at runtime. Before posting examples that generate graphics at runtime, however, I should explain a few issues I've glossed over in previous XAML 3-D posts. Those issues include:

### The Coordinate System

WPF uses a right-handed coordinate system to determine the orientation of the X, Y, and Z axes in relation to each other. It's called a "right-handed" coordinate system because you can use your right hand to verify the the axes' relationships. If you extend the fingers of your right hand so they point along the positive X axis and curl them toward the positive Y axis, your thumb points along the positive Z axis. (See Figure 1.) This is called the "right-hand rule."

If you extend your fingers along the positive X axis and cannot curl them toward the positive Y axis, you probably need to turn your hand over to make it work.

In a different version of the right-hand rule, you point your index finger along the X axis and your middle finger along the Y axis. Then your thumb points along the Z axis. (See Figure 2.)

In many three-dimensional programs, the axes are rotated so the X axis points right, the Y axis points up, and the Z axis points toward the viewer as shown in Figure 3. That way if you drop the Z coordinate, you get the usual two-dimensional situation with the X axis pointing right and the Y axis pointing up. You can use either version of the right-hand rule to verify that this is a right-handed coordinate system.

Do you need to orient the axes in that way? Not really. As long as you're consistent, use a right-handed coordinate system, and orient the camera and lights in the same way, you can rotate the axes around to suit your intuition. For example, you could make the X axis point right, the Y axis point away from the viewer, and the Z axis point up. You should, however, make sure your axes satisfy the right-hand rule because that rule is used for outward orientation and backface removal described shortly.

### Outward Oriented Surfaces and Backface Removal

Suppose you're drawing a scene such as a groups of cubes, and imagine you're looking at the scene from a particular position. The program must draw only the parts of the scene that are visible from your viewing position. If a cube lies behind another cube, you shouldn't draw the first cube. In a harder situation, a be might be partly another cube. In that case the program must draw only the parts of the first cube that should be visible.

In general, determining which parts of a scene are visible to a viewing position is pretty difficult, but there is one special case that is easy. For any closed solid, you know that any part of the solid that is on the solid's far side is hidden from view. For example, you can't see the far side of a sphere.

Parts of a solid that are on the opposite side from the viewing position are called "backfaces." Three-dimensional drawing programs use relatively simple "backface removal" tests to get rid of those faces without drawing them. (A step such as this, which quickly eliminates some faces from consideration, is called "culling.")

The relatively simple backface removal test relies on some geometric calculations that depend on the fact that the triangles that make up the solid are "outwardly oriented." That means if you look at a triangle from the outside of the solid (as opposed to looking at it through the solid), the points that make up the triangle should be arranged in counter-clockwise order. If you look at the triangle through the solid, they will be in clockwise order. The orientation of the points as seen from the viewing position lets the program know whether it is looking at the triangle on the front or back of the solid, and that lets it perform backface removal.

 Tip: If your scene looks weird, for example if triangles unexpectedly appear and disappear as you rotate it, then you may have some triangles oriented incorrectly.

Another way of thinking about the orientation is to use the right-hand rule again, this time in a new way. If you curl your fingers so they follow the direction of the points that make up the triangle, your thumb gives the triangle's orientation. To be properly outwardly oriented, your thumb should point away from the solid.

 Aside: Suppose an object is not a closed solid. For example, it might be a surface like a rumpled sheet or it might be a box without a lid. In that case you might want to prevent backface removal so the triangles are visible from both sides. You can do that in WPF by giving the object a back material. In WPF a GeometryModel3D object represents a group of triangles to be drawn. Each object has a material that represents the drawing characteristics of the object. For example, the material determines the object's color, texture pattern, glossiness, and other properties. To make the object's triangles visible from both sides, set the object model's BackMaterial property to the material you want it to display for backfaces.

### Cameras

A camera represents the program's viewing position. To completely specify a camera, you need to give its position and orientation. The position simply specifies the camera's X, Y, and Z coordinates.

You can specify the orientation in several ways. One of the more intuitive is to give it look and up directions. The look direction determines the direction in which the camera is pointed. The up direction determines the camera's "roll" or "tilt." (For example, you could tilt the camera on its side.) Figure 4 shows a camera aimed at a target object. The Up direction is indicated by a solid arrow. The Look direction is indicated by a dashed arrow.

One more property you should specify for a camera is its field of view. This determines how wide as area the camera can "see." Making the field of view small produces a result similar to a telephoto lens. The camera doesn't see much of what's in front of it and it enlarges that area to fit the display. Making the field of view large produces a result similar to a fish-eye lens. The camera "sees" a lot of what's in front of it and distorts it to make it fit the available viewing area.

The following code shows how you might define a camera.

```TheCamera = new PerspectiveCamera();
TheCamera.Position = new Point3D(10.0, 20.0, 0.0);
TheCamera.LookDirection = new Vector3D(-10.0, -20.0, 0.0);
TheCamera.UpDirection = new Vector3D(0.0, 1.0, 0.0);
TheCamera.FieldOfView = 30;```

This code places the camera at (10, 20, 0). It sets LookDirection to the negative of the position values so the camera is looking back toward the origin. The up direction is <0, 1, 0> so the top of the camera is directly above the bottom. That's the most usual orientation. Finally the code sets the camera's field of view to 30, which usually produces a good result.

### Lights

Lights are partly responsible for the appearance of objects. For example, if you shine a red light on a white object, the result is red.

WPF has several kinds of lights. For now I'll describe the two most useful: ambient lights and directional lights.

#### Ambient Lights

An ambient light source represents light that is applied equally to everything in the scene. If you look under a desk or chair, you can still see the floor even if there is no light shining directly on it.

In the real world, light reflects off of all of the objects in an area and provides indirect illumination of everything. The model used by WPF isn't perfectly correct because it applies equally to everything in the scene. In the real world, objects receive light reflected from nearby objects and that effects their appearance. For example, if you place a white marshmallow next to a bright red apple, the marshmallow will appear slightly pink. If you put the marshmallow next to a black cat, it will receive less reflected light and appear dull gray.

If you want a surface to be visible at all times no matter what other light is available, include some ambient light. The following code creates a gray ambient light.

`AmbientLight ambient_light = new AmbientLight(Colors.Gray);`

#### Directional Lights

The color of an object depends in part on the angle with which the light strikes the object's surface. If you shine a white light on a piece of white paper so the light strikes the paper at a 90° angle, the paper appears white. In contrast if you move the light so it strikes the paper at a 30° angle, the paper appears light gray.

Because ambient light comes from no particular direction (or every direction, if you prefer), it effects all surfaces equally.

In contrast a directional light provides light shining in a single direction so it effects surfaces that are arranged perpendicularly to that direction more than other surfaces.

Note that WPF doesn't handle shadows or transparency. That means a light cannot shine through a transparent object and one object cannot block the light and cast a shadow on another object. An object can block its own light, however. For example, suppose you are drawing a cube with top side parallel to the X-Z plane (a horizontal surface), and you have a directional light shining downward. The top of the cube will be brightly lit, but the bottom side is blocked from the light by the object (it's a backface when seen from the position of the light) so it isn't illuminated by that light.

Sometimes you may want to use multiple directional light sources to get the best results from a scene, but using more lights slows rendering so don't go crazy.

The following code shows how a program might create a directional light.

```DirectionalLight directional_light1 =
new DirectionalLight(Colors.Gray, new Vector3D(-1.0, -3.0, -2.0));```

This code creates a light shining in the direction <-1, -3, -2>.

### Materials

An object's displayed color depends on the light that shines on it. It also depends on the object's material. If you shine an orange light on a white sphere, the result is orange. If you shine the same light on a bright green sphere, the result is dark green. (The orange has a weak green component so it doesn't bring out all of the object's bright green color.)

WPF has several kinds of materials.

Emissive materials generate their own light so they appear brighter than the available light would normally make them. They do not emit light that can illuminate other objects in the scene, however.

Specular materials are shiny and can have bright spots where the angle of the light bounces off the material towards the camera.

Diffuse materials are the simplest. A diffuse object's color depends on its innate color and the lighting model.

The following code shows how a program might use a diffuse material.

```// Make the surface's material using a solid green brush.
DiffuseMaterial surface_material = new DiffuseMaterial(Brushes.LightGreen);

// Make the mesh's model.
GeometryModel3D surface_model = new GeometryModel3D(mesh, surface_material);

// Make the surface visible from both sides.
surface_model.BackMaterial = surface_material;```

This code first creates a light green diffuse material. It then creates a GeometryModel3D object to represent a set of triangles. It associates the model with the material and a mesh that was previously with point and triangle vertex data. Finally this example sets the model's BackMaterial to the same material so the triangles are visible from both sides.

### Conclusion

Here are the key points to take from all this:

• The axes are arranged according to the right-hand rule. Usually X is right, Y is up, and Z is "out."
• Orient triangles according to the right-hand rule so the program can use backface removal.
• Cameras have position, look direction, and up direction.
• Ambient light applies to all surfaces equally.
• Directional lights apply most to surfaces perpendicular to their direction.
• Together materials and lights determine an object's appearance.
• If you want to see backfaces, set a mode's BackMaterial property to a material.

Now that you know a bit about these issues, you may want to go back and review some of my earlier three-dimensional WPF examples. In their descriptions I didn't talk about the coordinate system, camera, lights, or materials, but if you look at the XAML code you should be able to figure them out.

With this background you're also ready to move on to more advanced examples. My next few posts show how programs can generate and display models at runtime. I'm sure I'll have more examples at some point that demonstrate different materials and lighting models, but for now feel free to modify the examples to experiment with them on your own.

### Rainbowize an image in C#

The example Use an ImageAttributes object to apply general color tones to an image in C# uses a ColorMatrix and an ImageAttributes object to convert an image into a color scale. For example, its techniques let you convert an image to sepia tones, gray tones, or other less typical color schemes such as yellow tones.

This example uses the same technique to convert parts of an image into different tones to produce a rainbow effect. The following code shows how the program colors its image.

```// Process the image.
private void Form1_Load(object sender, EventArgs e)
{
// Create the output image.
Image original = picImage.Image;
int wid = original.Width;
int hgt = original.Height;
Bitmap bm = new Bitmap(wid, hgt);
using (Graphics gr = Graphics.FromImage(bm))
{
// Define target colors.
Color[] color =
{
//Color.Red, Color.Orange, Color.Yellow,
//Color.Green, Color.Blue, Color.Indigo,
//Color.Violet,

Color.Red, Color.OrangeRed, Color.Yellow,
Color.Green, Color.Blue, Color.Indigo,
Color.Fuchsia,
};
const float scale = 2.0f;

// Draw.
for (int i = 0; i < color.Length; i++)
{
// Create the ColorMatrix.
ColorMatrix cm = new ColorMatrix(new float[][]
{
new float[] {color[i].R / 255f * scale, 0, 0, 0, 0},
new float[] {0, color[i].G / 255f * scale, 0, 0, 0},
new float[] {0, 0, color[i].B / 255f * scale, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1},
});
ImageAttributes attr = new ImageAttributes();
attr.SetColorMatrix(cm);

// Draw the next part of the image.
int x = (int)(i * original.Width / color.Length);
Point[] points =
{
new Point(x, 0),
new Point(wid, 0),
new Point(x, hgt),
};
Rectangle rect = new Rectangle(x, 0, wid - x, hgt);
gr.DrawImage(original, points, rect, GraphicsUnit.Pixel, attr);
}
}

// Display the result.
picImage.Image = bm;

// Save the result.
bm.Save("Rainbow.png", ImageFormat.Png);
}```

The program starts by creating a Bitmap the same size as the original image, and by making a Graphics object to work with the Bitmap. It defines an array containing the colors it will use to tone the image and then enters a loop that executes once for each of the colors.

For each color the program makes a ColorMatrix that uses the red, green, and blue color components of the color to scale the output color in the image. For example, suppose one of the colors in the array is yellow, which has large red and green components and a small blue component. The program initializes the ColorMatrix so it increases the result's red and green components and reduces the blue component. When the code applies this matrix to a pixel in the image, the result is shifted toward a yellow tone.

After it creates the ColorMatrix, the program makes an ImageAttributes object and sets its ColorMatrix to the one it created.

Next the program defines an array of points representing the next piece of the image to draw. If this is color number i, it draws the i-th part of the image. It draws all the way to the right edge of the image so the colored pieces of image overlap. That prevents any pixels between slices from being omitted by rounding errors. (It's also a little inefficient because the program recolors the rightmost pixels once for each color. The method is quite fast, however, so I'm not going to worry about it.)

The program creates a Rectangle to define the area in the original image that should be drawn into the new Bitmap. It then uses the DrawImage method to draw the selected part of the image into the Bitmap while applying the ColorMatrix to its pixels.

After it finishes drawing each color, the program displays the result and saves it into a PNG file.

### Probabilistically pick large random prime numbers C#

The example Probabilistically determine whether a number is prime in C# explains an algorithm for determining whether a number is prime with any desired level of certainty. After you add that method to your algorithmic toolkit, finding large primes is easy. Simply pick a large random number and see if it's prime. Then repeat until you find a prime.

The following code shows how this example finds prime numbers. (I've modified it slightly to remove progress reporting statements.)

```// Probabilistically find a prime number within the range [min, max].
private int FindPrime(int min, int max, int num_tests)
{
// Try random numbers until we find a prime.
for ( ; ; )
{
// Pick a random odd p.
int p = Rand.Next(min, max + 1);
if (p % 2 == 0) continue;

// See if it's prime.
if (IsProbablyPrime(p, num_tests)) return p;
}
}```

This method enters an infinite loop. Each time through the loop, it picks a random number in the desired range. If the number is even, the loop continues with no further testing. (The primality test will determine that even numbers are not prime, but it can be slow so there's no need to be blindly stupid here. Note that the program is looking for large primes so it doesn't bother to consider the value 2.)

If the number is not even, the program calls the IsProbablyPrime method described in the earlier example. If IsProbablyPrime returns true, then this method returns the candidate number, which is probably prime.

Now when you need to find large primes p and q for use with the RSA algorithm, you can simply call this method.

There's one other important issue to think about here: how long will it take this method to find large primes? After all, this wouldn't be a very useful algorithm if it took a million years to find a prime. Fortunately the "prime number theorem" states that primes are everywhere dense. That means in every part of the number line there are a lot of primes so it won't take you too long to pick one randomly. In one set of 10 trials, the program picked as few as 1 and as many as 23 random numbers to find a prime, but on average it needed only 10.9 random numbers to find a prime.

When the program runs, it can usually discard several non-primes very quickly. It only needs to try a couple of random tests in the IsProbablyPrime method to decide that a number is composite. When it picks a number that actually is prime, the program takes longer because it must perform all of the tests you requested to achieve the desired level of certainty.

My book Essential Algorithms: A Practical Approach to Computer Algorithms contains more information about primality testing, prime number finding, and RSA. To see a description and table of contents, go here.

### Probabilistically determine whether a number is prime in C#

This is a cool little algorithm that uses some clever mathematics. This algorithm and several related algorithms are described in my book Essential Algorithms: A Practical Approach to Computer Algorithms. I think it's a really good book (and it's gotten very good reviews) so if you're interested in this sort of thing (or other algorithms), check it out!

The example also demonstrates a crucial technique in C# programming when you're performing integer arithmetic.

Before I describe the program, you should know a bit about why it's important to decide whether a number is prime.

Some modern cryptographic algorithms (in particular RSA) rely on large prime numbers. Without going into all of the details, in RSA you pick two large prime numbers p and q and publish their product n = p × q for everyone to see.

Now suppose your friend Alice wants to meet you for lunch at her favorite restaurant but doesn't want to invite Bill, who's a really annoying conversationalist, always orders something really expensive, and then demands to split the bill equally. Alice uses the public value n to encrypt the invitation and sends it to you.

Unfortunately Bill works in your company's IT department and he intercepts the message. If he can figure out what p and q are, he can decipher the invitation and show up unexpectedly. Basically the security of RSA depends on the fact that it's hard to factor products of large primes p and q.

If p and q are small, say only a few billion, Bill can easily write a program to try all possible prime factors up to the square root of n and eventually figure out what p and q are. To prevent that, you need to use really big prime numbers. Instead of using 32-bit ints or a 64-bit longs, you might use 100-bit integers. The product n will have 200 bits and its square root will have 100 bits. (It will be around 1.3×1030.) Even if Bill has a computer that can test 1 trillion (109) factors per second, it would take him roughly 1.3×1030 / 109 = 1.3 × 1021 seconds or about 4 × 1013 years to find p and q. By then you and Alice will be safely done with lunch.

Unfortunately if it Bill a long time to factor n, it will also take you a long time to determine whether p and q are prime. Fortunately there are tests you can use to determine whether a number is prime without actually factoring it. That's where this example begins.

Fermat's "little theorem" says that, if P is prime and 1 ≤ n < P, then nP-1 = 1 mod P. In other words, if you raise n to the P-1 power and take the result modulo P, the result is 1.

For example, suppose P = 7. Then:

• 16 mod 7 = 1 mod 7 = 1
• 26 mod 7 = 64 mod 7 = 1
• 36 mod 7 = 729 mod 7 = 1
• 46 mod 7 = 4,096 mod 7 = 1
• 56 mod 7 = 15,625 mod 7 = 1
• 66 mod 7 = 46,656 mod 7 = 1

Now suppose you want to know if p is prime. Pick a random number n with 1 ≤ n < P and apply Fermat's little theorem. If p really is prime, then the result will always be 1 no matter what n you pick. It can be shown (but not by me) that if p is composite (non-prime), then there is at least a 50% chance that the n you pick will make np-1 ≠ 1 mod p. An n that makes Fermat's little theorem fail in this way is called a "Fermat witness" because it proves that p is not prime.

Of course there's also a chance that you'll be unlucky and the random n you pick satisfies Fermat's little theorem even if p is composite. Such a value is called a "Fermat liar" because implies that p is prime when it really isn't.

After you know these facts, the algorithm for primality testing is relatively simple. Pick a bunch of random values n and see if they satisfy Fermat's little theorem. If any of them are Fermat witnesses, you know that p is composite. (Although you still don't know p's factors.) If you pick N values for n and they all satisfy Fermat's little theorem, then there is only a 1/2N chance that p is composite and every value n you tried was a Fermat liar.

Now you can pick as many n as you like to get any desired degree of certainty. For example, if you use 10 values for n and they all claim p is prime, there is only a 1/210 ≈ 0.00098 chance that p is really composite. If those odds aren't good enough, try 20 values for n. If they all claim p is prime, there's only a 1/220 ≈ 0.00000095 chance that p is composite. If that's still not good enough, try 100 values for n and there will be only a 1/2100 ≈ 7.9 × 10-31 chance that p is actually composite. (In contrast the odds of you being struck by lightning is a whopping 9 × 10-7 per year, roughly a million million million million times greater.)

This is an example of a probabilistic algorithm. You can never be completely sure the result is correct, but you can use as many test values as you like to achieve whatever degree of certainty you want.

The following IsProbablyPrime method is the key to this example. (I've removed some progress reporting code to keep things simple.)

```// Perform tests to see if a number is (probably) prime.
private Random Rand = new Random();
private bool IsProbablyPrime(int p, int num_tests)
{
checked
{
// Perform the tests.
for (int i = 0; i < num_tests; i++)
{
// Pick a number n in the range (1, p).
long n = Rand.Next(2, p);

// Calculate n ^ (p - 1).
long result = n;
for (int power = 1; power < p - 1; power++)
{
result = (result * n) % p;
}

// If the final result is not 1, p is not prime.
if (result != 1) return false;
}
}

// If we survived all the tests, p is probably prime.
return true;
}```

The code performs the desired number of tests. For each test, the code picks a random n and then raises it to the p-1 power modulus p. If the result is 1 not for any value of n, the code returns false to indicate that p is definitely not prime.

If the method finishes all tests, it concludes that p is probably prime.

After it decides whether the number is prime, the program factors the number to make sure it was correct. To see how that code works, see the example Find a number's prime factors in C#.

There are a couple of C# issues here. First, the method uses a checked block. If a C# program encounters an integer error while performing an arithmetic operation, it normally does not signal an error. Instead it gets a nonsensical result and continues running as if nothing has gone wrong. In this example, if result * n is too big, the program causes an integer overflow, the new result is some sort of garbage (probably a large negative number), and the test is invalid.

The checked block makes the program throw an exception if it encounters an integer arithmetic error.

This program also uses long integers to perform its calculations so it can use larger values without causing an integer overflow. The example only handles values up to 100 million so it doesn't cause an overflow.

Another issue to consider is that these values are pretty small by cryptographic standards so the attacker Bill can easily factor them. (With a computer, not by hand.) In a real application you would need to use much larger numbers. The System.Numerics namespace defines a BigInteger structure that can do this in Framework version 4.0 and later. The version of Visual Studio I used for this example doesn't support that so it uses the long data type. You can rewrite the application if you like. This example is really just to show how it all works.

A final issue is that it can take a while to raise a test value n to the p-1 power if p is really large. Fortunately there's a clever algorithm called "fast exponentiation" that lets you do this more quickly. See my book for a description of that algorithm.

In my next post, I'll explain how you can use the algorithm described here to find large prime numbers. It's fairly simple so you may want to try to figure it out yourself before you read that post.

Again, my book contains more information about primality testing, prime number finding, and RSA. To see a description and table of contents, go here.

### Easily draw different styles of "illuminated" text in C#

The previous example showed how to draw "illuminated" text. It drew each paragraph's initial letter in a large font with a box around it.

Real illuminated manuscripts typically make the initial letter much more elaborate. You can modify that example to draw the initial letter in more interesting ways, but that requires you to modify the program's code in some non-trivial ways.

This example makes this process a lot easier. Its version of the DrawIlluminatedText method takes a final parameter that is a method that should be called to draw the background behind each paragraph's initial letter. The following code shows the method's new signature.

```// Draw an illuminated paragraph.
private void DrawIlluminatedText(Graphics gr,
float min_lead_width, float min_lead_height,
Font lead_font, Brush lead_brush, Pen lead_pen,
Font body_font, Brush body_brush,
ref RectangleF rect, int paragraph_spacing, string paragraph,
Action<Graphics, RectangleF> illuminator)
{
...
// Illuminate the lead character.
RectangleF lead_rect = new RectangleF(
rect.X, rect.Y, lead_size.Width, lead_size.Height);
illuminator(gr, lead_rect);

// Draw the lead character.
...
}```

Before the method draws the initial character, it calls this illuminator method to draw the background behind that letter.

The following code shows the simplest of this program's three illuminator methods.

```// Fill an illumination box with an image.
private void DrawIlluminationBox3(Graphics gr, RectangleF rectf)
{
// Fill the rectangle with an image.
using (Brush brush = new TextureBrush(Properties.Resources.Butterflies))
{
gr.FillRectangle(brush, rectf);
}

// Outline the rectangle.
using (Pen pen = new Pen(Color.Blue, 5))
{
gr.DrawRectangle(pen, Rectangle.Round(rectf));
}
}```

This method creates a TextureBrush to fill areas with the image stored in the Butterflies resource and uses that brush to fill the initial character's drawing area. It then outlines the area with a 5 pixel wide blue box.

The program's other illumination methods are a bit more complicated. Download the program to see how they work.

Now you can easily modify the illumination style by changing the illuminator method that is passed into the DrawIlluminatedText method by the main program.

### Draw "illuminated" text where each paragraph begins with an oversized letter in C#

(I'm taking a short break from three-dimensional WPF graphics. If you particularly want (or don't want) to see more about that topics, please let me know. I also have a new (very interesting) project starting so my posting frequency may drop a bit for a while.)

In some illuminated manuscripts, the first letter of a paragraph is drawn in a larger font than the remaining text. This example does something similar. (Although in a real illuminated manuscript that initial character would probably be decorated with ornate images and scroll work. This example is as much an exercise in formatting as a useful tool.)

The following code shows how the program controls the text drawing.

```// The text to draw.
private string[] paragraphs =
{
"Once there were three bats...",
"The first bat comes home one night ...",
...
};

// Draw the text.
private void picWriting_Paint(object sender, PaintEventArgs e)
{
// Get the available space.
const int paragraph_spacing = 10;
const int margin = 5;
RectangleF rect = new RectangleF(margin, margin,
picWriting.ClientSize.Width - 2 * margin,
picWriting.ClientSize.Height - 2 * margin);

// Make the fonts.
using (Font lead_font = new Font("Times New Roman", 30, FontStyle.Bold))
{
using (Font body_font = new Font("Times New Roman", 12))
{
// Draw the text.
foreach (string paragraph in paragraphs)
DrawIlluminatedText(e.Graphics, 50, 55,
lead_font, Brushes.Green, Pens.Green,
body_font, Brushes.Black,
ref rect, paragraph_spacing, paragraph);
}
}
}```

The program first defines an array of strings to hold the text's paragraphs. When the picWriting PictureBox receives a Paint event, its event handler creates a RectangleF representing the area in which the text should be drawn. It then creates fonts to use for each paragraph's lead character and the body of the paragraphs. The event handler then loops through the paragraphs and calls the following DrawIlluminatedText method for each.

```// Draw an illuminated paragraph.
private void DrawIlluminatedText(Graphics gr,
float min_lead_width, float min_lead_height,
Font lead_font, Brush lead_brush, Pen lead_pen,
Font body_font, Brush body_brush,
ref RectangleF rect, int paragraph_spacing, string paragraph)
{
// Get the lead character.
string ch = paragraph.Substring(0, 1);
paragraph = paragraph.Substring(1);

// Size the lead character.
SizeF lead_size = gr.MeasureString(ch, lead_font);
if (lead_size.Width < min_lead_width)
lead_size.Width = min_lead_width;
if (lead_size.Height < min_lead_height)
lead_size.Height = min_lead_height;

// Make a StringFormat to align and trim the text.
using (StringFormat string_format = new StringFormat())
{
// Stop drawing each line at a word boundary.
string_format.Trimming = StringTrimming.Word;

// See how much space is available for the side text.
SizeF side_size = new SizeF(
rect.Width - lead_size.Width,
lead_size.Height);

// See how much side text will fit
// allowing a partial line at the end.
int chars_fitted, lines_filled;
side_size = gr.MeasureString(paragraph, body_font,
side_size, string_format,
out chars_fitted, out lines_filled);

// Get the side text.
string side_text = paragraph.Substring(0, chars_fitted);
paragraph = paragraph.Substring(chars_fitted);

// Draw only complete lines.
string_format.FormatFlags = StringFormatFlags.LineLimit;

// See how much space the side text needs.
side_size.Height += 1000;
side_size = gr.MeasureString(side_text, body_font,
side_size, string_format,
out chars_fitted, out lines_filled);
if (side_size.Height < min_lead_height)
side_size.Height = min_lead_height;

// Use at least that much height for the lead character.
if (lead_size.Height < side_size.Height)
lead_size.Height = side_size.Height;

// Illuminate the lead character.
gr.DrawRectangle(lead_pen, rect.X, rect.Y,
lead_size.Width, lead_size.Height);

// Draw the lead character.
RectangleF lead_rect = new RectangleF(
rect.X, rect.Y, lead_size.Width, lead_size.Height);
string_format.Alignment = StringAlignment.Center;
string_format.LineAlignment = StringAlignment.Center;
gr.DrawString(ch, lead_font, lead_brush, lead_rect, string_format);
string_format.Alignment = StringAlignment.Near;
string_format.LineAlignment = StringAlignment.Near;

// Get the area available for the side text.
RectangleF side_rect = new RectangleF(
rect.X + lead_size.Width,
rect.Y,
side_size.Width,
side_size.Height);

// Draw the side text.
gr.DrawString(side_text, body_font, body_brush,
side_rect, string_format);

// Remove the space used by the side text.
rect.Y += lead_size.Height;
rect.Height -= lead_size.Height;

// Draw the rest of the paragraph.
gr.DrawString(paragraph, body_font, body_brush, rect, string_format);

// See how much space that used.
SizeF rect_size = new SizeF(rect.Width, rect.Height);
SizeF size = gr.MeasureString(paragraph, body_font, rect_size);

// Remove the used space.
rect.Y += size.Height + paragraph_spacing;
rect.Height -= size.Height + paragraph_spacing;
}
}```

This method takes as parameters:

• The Graphics object on which to draw
• The minimum width and height that should be used for the lead character
• The font and brush that should be used for the lead character
• The pen that should be used to draw a box around the lead character
• The font and brush that should be used for the remaining text
• A RectangleF where the paragraph should be drawn
• Spacing to add after the paragraph
• The paragraph's text

The code starts by pulling the initial character off of the paragraph. It uses the Graphics object's MeasureString method to measure the character and sets the lead_size structure to hold the larger of the character's size and the minimum allowed size.

Next the code creates a StringFormat object to use while drawing text. It sets the object's Trimming property to Word so any text is clipped to the nearest word. That prevents the Drawstring method from drawing part of a word at the end of a line of text.

The code then calculates the amount of space available to the right of the initial character. It uses MeasureString again to see how much of the paragraph's remaining text will fit in that area. The then method extracts the text that will fit in that area.

Next the code determines how much space the text to the right of the initial character actually needs. (For example, suppose that area is 100 x 50 pixels and can hold 120 characters. Those characters might actually only use 100 x 45 pixels. This step measures the space actually needed so we don't waste the 5 extra pixels.)

The results look better if the initial character's area has the same height as the text to its right. If that area's height is currently less than the height of the text to the right, the code increases it to match.

Finally the code starts producing output. First it draws a rectangle around the initial character's area. It then draws the initial character centered in that area.

The method then draws the text to the right of the initial character and removes the space used by that text from the RectangleF representing the area in which all of the text must fit.

Finally the method draws any remaining text and removes the area used by that text from the total space available. That RectangleF is passed into the method by reference so the change returns to the calling code and the next paragraph is drawn in whatever space remains.

You can modify the code if you like to omit the rectangle around the initial character, fill that rectangle, display an image in that rectangle, or produce other effects. My next post will show how to make those sorts of changes flexibly.

### Use static resources to customize a WPF/XAML program

The example Use static resources to draw three interlocked boxes using WPF, XAML, and C# uses static resources to avoid duplicating code. You can also use static resources to make it easier to configure an application.

This example adds the following three resources to the Window's Resources section.

```<!-- Define the cubes' colors. -->
<SolidColorBrush x:Key="Brush1" Color="Yellow" />
<SolidColorBrush x:Key="Brush2" Color="Fuchsia" />
<SolidColorBrush x:Key="Brush3" Color="Cyan" />```

Later the boxes use those resources to define the boxes' materials. The following code shows how the example creates its first box. The code that refers to the brush resource is shown in blue.

```<!-- Box 1 -->
<GeometryModel3D Geometry="{StaticResource CubeGeometry}">
<GeometryModel3D.Transform>
<ScaleTransform3D ScaleX="1" ScaleY="2" ScaleZ="3" />
</GeometryModel3D.Transform>

<GeometryModel3D.Material>
<DiffuseMaterial Brush="{StaticResource Brush1}" />
</GeometryModel3D.Material>
</GeometryModel3D>```

This technique doesn't save you any code. In fact, it makes the code slightly more complicated because you need to define and refer to the brushes instead of simply typing "Red" for a material's brush. However, it makes it easier for you to configure the program. If you decide to change the boxes' colors, you can do it in the Resources section at the top of the program instead of needing to dig through the code to find the places where you need to make the changes. (This is sort of like creating a const at the beginning of a C# program and then using it to customize the program.)

### Use static resources to draw three interlocked boxes using WPF and XAML

The example Draw three interlocked boxes using WPF and XAML draws three cubes and then scales them to make them interlock. One problem with this technique is that it requires you to include three identical copies of the data that defines the cubes. That means you need to write, debug, and maintain the code in three places.

The following code shows how the program defines its red box. The code in blue is duplicated for all three boxes.

```<GeometryModel3D>
<GeometryModel3D.Geometry>
<!-- Cube -->
<MeshGeometry3D
Positions="
-1,-1,-1   1,-1,-1   1,-1, 1  -1,-1, 1
-1,-1, 1   1,-1, 1   1, 1, 1  -1, 1, 1
1,-1, 1   1,-1,-1   1, 1,-1   1, 1, 1
1, 1, 1   1, 1,-1  -1, 1,-1  -1, 1, 1
-1,-1, 1  -1, 1, 1  -1, 1,-1  -1,-1,-1
-1,-1,-1  -1, 1,-1   1, 1,-1   1,-1,-1
"
TriangleIndices="
0  1  2     2  3  0
4  5  6     6  7  4
8  9 10    10 11  8
12 13 14    14 15 12
16 17 18    18 19 16
20 21 22    22 23 20
" />
</GeometryModel3D.Geometry>

<GeometryModel3D.Transform>
<ScaleTransform3D ScaleX="1" ScaleY="2" ScaleZ="3" />
</GeometryModel3D.Transform>

<GeometryModel3D.Material>
<DiffuseMaterial Brush="Red" />
</GeometryModel3D.Material>
</GeometryModel3D>```

You can reduce the amount of duplicated code if you define static resources to hold the common data. To do that, you can add a Resources section to just about any object in the XAML code. This example adds it to the main Window object so the resources are available everywhere in the file. The following code shows how the program defines the cube point and triangle data. The Resources section is shown in blue.

```<Window x:Class="howto_interlocked_boxes2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="howto_interlocked_boxes"
Height="300" Width="300">
<Window.Resources>
<!-- Define points representing a cube. -->
<Point3DCollection x:Key="CubePoints">
-1,-1,-1   1,-1,-1   1,-1, 1  -1,-1, 1
-1,-1, 1   1,-1, 1   1, 1, 1  -1, 1, 1
1,-1, 1   1,-1,-1   1, 1,-1   1, 1, 1
1, 1, 1   1, 1,-1  -1, 1,-1  -1, 1, 1
-1,-1, 1  -1, 1, 1  -1, 1,-1  -1,-1,-1
-1,-1,-1  -1, 1,-1   1, 1,-1   1,-1,-1
</Point3DCollection>

<!-- Define triangles representing a cube. -->
<Int32Collection x:Key="CubeTriangles">
0  1  2     2  3  0
4  5  6     6  7  4
8  9 10    10 11  8
12 13 14    14 15 12
16 17 18    18 19 16
20 21 22    22 23 20
</Int32Collection>
</Window.Resources>
<Grid>
...
</Grid>
</Window>```

The first resources is a Point3DCollection (a collection of Point3D objects) named CubePoints. It defines the vertices for the cube.

The second resources is an Int32Collection (a collection of 32-bit integers) named CubeTriangles. It contains the indices of the points that make up the cube's triangles.

Now the code that defines the boxes can refer to these resources instead of including all of the vertex and triangle data. The following code shows how the program defines the red box. The blue code refers to the resources.

```<!-- Red box -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<!-- Cube -->
<MeshGeometry3D
Positions="{StaticResource CubePoints}"
TriangleIndices="{StaticResource CubeTriangles}"
/>
</GeometryModel3D.Geometry>

<GeometryModel3D.Transform>
<ScaleTransform3D ScaleX="1" ScaleY="2" ScaleZ="3" />
</GeometryModel3D.Transform>

<GeometryModel3D.Material>
<DiffuseMaterial Brush="Red" />
</GeometryModel3D.Material>
</GeometryModel3D>```

This code uses the StaticResource statement to refer to the common resources. The rest of this code is different for the three boxes.

You can actually go a little farther with static resources. All three boxes define a MeshGeometry3D object that uses the cube vertices and positions. This example actually includes the following resource code inside the Window's Resources section to define a MeshGeometry3D object named CubeGeometry.

```<!-- Define the vertices and triangles for a cube. -->
<MeshGeometry3D x:Key="CubeGeometry"
Positions="{StaticResource CubePoints}"
TriangleIndices="{StaticResource CubeTriangles}"
/>```

This code uses the previously defined CubePoints and CubeTriangles resources to create a MeshGeometry3D object that defines a cube. The following code shows how the example uses that object to create the blue box. The code that defines the cube is shown in blue.

```<!-- Blue box -->
<GeometryModel3D Geometry="{StaticResource CubeGeometry}">
<GeometryModel3D.Transform>
<ScaleTransform3D ScaleX="2" ScaleY="3" ScaleZ="1" />
</GeometryModel3D.Transform>

<GeometryModel3D.Material>
<DiffuseMaterial Brush=" Blue" />
</GeometryModel3D.Material>
</GeometryModel3D>```

This lets the three boxes share just about as many resources as they can. Now if you decide to modify the basic cube, for example by moving the vertices or changing their coordinates from all 1s to all 2s, you only need to do it in one place.

### Draw three interlocked boxes using WPF and XAML

The example Draw two interlocked tetrahedrons using WPF, XAML, and C# draws two small tetrahedrons and uses transformations to make them overlap. This example uses a similar technique. It draws three cubes and scales them so they overlap.

The following code shows how the program defines a single cube centered at the origin and with vertex coordinates (±1, ±1, ±1).

```<MeshGeometry3D
Positions="
-1,-1,-1   1,-1,-1   1,-1, 1  -1,-1, 1
-1,-1, 1   1,-1, 1   1, 1, 1  -1, 1, 1
1,-1, 1   1,-1,-1   1, 1,-1   1, 1, 1
1, 1, 1   1, 1,-1  -1, 1,-1  -1, 1, 1
-1,-1, 1  -1, 1, 1  -1, 1,-1  -1,-1,-1
-1,-1,-1  -1, 1,-1   1, 1,-1   1,-1,-1
"
TriangleIndices="
0  1  2     2  3  0
4  5  6     6  7  4
8  9 10    10 11  8
12 13 14    14 15 12
16 17 18    18 19 16
20 21 22    22 23 20
" />```

The example repeats this code three times and then uses transformations to stretch the cube into the boxes that it needs. The following code shows how the program scales the red box.

```<GeometryModel3D.Transform>
<ScaleTransform3D ScaleX="1" ScaleY="2" ScaleZ="3" />
</GeometryModel3D.Transform>```

The other cubes are scaled similarly but with different scale factors in the X, Y, and Z directions.

### Draw two interlocked tetrahedrons using WPF, XAML, and C#

The example Make a tetrahedron with crisp edges and rotate it using WPF, XAML, and C# shows how to draw a three-dimensional tetrahedron. This example uses the same code to define the points and triangles that make up a tetrahedron. It repeats that code twice to define two different tetrahedrons with only three small changes.

First, it changes the colors of the tetrahedrons.

Second, it adds the following Transform to the first (blue) tetrahedron.

```<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX="2"
ScaleY="2" ScaleZ="2" />
<TranslateTransform3D OffsetY="-0.77"/>
</Transform3DGroup>
</GeometryModel3D.Transform>```

A Transform can contain a single transformation. Because this example needs to apply multiple transformations to the tetrahedron, this Transform contains a Transform3D group. That group can then contain as many transformations as you need.

In this example, the group contains a scale transformation that enlarges the tetrahedron by a factor of 2 in the X, Y, and Z directions, basically doubling its size. (The previous example also included that transformation.)

The group also contains a translation transformation that moves the tetrahedron 0.77 units in the negative Y direction.

The third way this example differs from the previous example is it applies the following Transform to the second (yellow) tetrahedron.

```<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX="2" ScaleY="-2" ScaleZ="2" />
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="0,1,0" Angle="45"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<TranslateTransform3D OffsetY="0.77"/>
</Transform3DGroup>
</GeometryModel3D.Transform>```

This Transform scales the tetrahedron by a factor of 2 in the X and Z directions, and by a factor of -2 in the Y direction to flip the tetrahedron upside down.

It the includes a RotateTransform. That transform's Rotation property contains an AxisAngleRotation3D object that defines the rotation. It rotates the shape around the Y axis <0, 1, 0> through an angle of 45 degrees.

The Transform finishes by translating the tetrahedron by 0.77 units in the positive Y direction.

This example demonstrates an important technique in building three-dimensional scenes. It starts with a basic object, in this case a small tetrahedron, and then uses transformations to scale, rotate, and translate the object as needed to produce new objects.

### Calendar

 March 2014
SuMoTuWeThFrSa
1
2345678
9101112131415
16171819202122
23242526272829
3031