Draw cylinders using WPF and C#

[draw cylinders]

This example shows how to draw cylinders in a WPF application. Other examples such as Draw a 3D surface from a set of data points with an altitude map using WPF and C# show how to draw 3D graphics.

This example merely adds the following AddCylinder method to create the necessary points and triangles to make a cylinder.

// Add a cylinder.
private void AddCylinder(MeshGeometry3D mesh,
    Point3D end_point, Vector3D axis, double radius, int num_sides)
{
    // Get two vectors perpendicular to the axis.
    Vector3D v1;
    if ((axis.Z < -0.01) || (axis.Z > 0.01))
        v1 = new Vector3D(axis.Z, axis.Z, -axis.X - axis.Y);
    else
        v1 = new Vector3D(-axis.Y - axis.Z, axis.X, axis.X);
    Vector3D v2 = Vector3D.CrossProduct(v1, axis);

    // Make the vectors have length radius.
    v1 *= (radius / v1.Length);
    v2 *= (radius / v2.Length);

    // Make the top end cap.
    double theta = 0;
    double dtheta = 2 * Math.PI / num_sides;
    for (int i = 0; i < num_sides; i++)
    {
        Point3D p1 = end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        theta += dtheta;
        Point3D p2 = end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        AddTriangle(mesh, end_point, p1, p2);
    }

    // Make the bottom end cap.
    Point3D end_point2 = end_point + axis;
    theta = 0;
    for (int i = 0; i < num_sides; i++)
    {
        Point3D p1 = end_point2 +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        theta += dtheta;
        Point3D p2 = end_point2 +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        AddTriangle(mesh, end_point2, p2, p1);
    }

    // Make the sides.
    theta = 0;
    for (int i = 0; i < num_sides; i++)
    {
        Point3D p1 = end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        theta += dtheta;
        Point3D p2 = end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;

        Point3D p3 = p1 + axis;
        Point3D p4 = p2 + axis;

        AddTriangle(mesh, p1, p3, p2);
        AddTriangle(mesh, p2, p3, p4);
    }
}

The parameters to the method are:

  • mesh — The MeshGeometry3D object that should contain the cylinder.
  • end_point — The center of one end of the cylinder.
  • axis — A vector that determines the direction and length of the cylinder. In other words, the vector connecting the cylinder’s two end points.
  • radius — The cylinder’s radius.
  • num_sides — The number of sides the end caps for the cylinder should have. The end caps will each be made up of num_sides triangles. The cylinder’s sides will be made up of 2 × num_sides triangles forming num_sides rectangles.

When it begins, the AddCylinder method finds two vectors that are perpendicular to the cylinder’s axis and to each other. Those vectors (and all other vectors that are perpendicular to the axis) lie in the plane containing the cylinder’s first end cap.

To find the two vectors, the program first creates vector v1. If the axis‘s Z coordinate is not close to 0, then the vector <axis.Z, axis.Z, -axis.X – axis.Y> is perpendicular to axis. If the axis‘s Z coordinate is close to 0, then the vector <-axis.Y – axis.Z, axis.X, axis.X> is perpendicular to axis. (To see why those are perpendicular vectors, calculate the dot product of the two vectors. For information about the dot product, see this Wikipedia article.)

Next the method takes the cross product of the first perpendicular vector and the axis. That gives a new vector perpendicular to both of the others (v1 and axis). (To learn why, see this Wikipedia article.)

Now the program has two perpendicular vectors in the plane of the cylinder’s end cap. To generate points along the edges of the end cap, the program will use those vectors multiplied by sines and cosines of angles much as you might generate the points on a circle in two dimensions. To give the end cap the right radius, the program divides v1 and v2 by their lengths and multiplies them by radius.

Now the program can start calculating points. To make the top, it makes variable theta range from 0 to 2π. For each value of theta, the program adds the end_point to v1 times sine(theta) plus v2 times cosine(theta). The triangles it uses to build the end cap connect end_point and two adjacent points along the edge of the circle.

The code then makes the bottom similarly. The only difference is that the code reverses the ordering of the three points so the bottom triangle is outwardly oriented. (So WPF can tell which side is on the inside of the cylinder.)

Finally the method makes the cylinder’s sides. To do that, the code loops through the values of theta one last time. For each value, it generates points p1 and p2 on the edge of the first end cap. It then adds the vector axis to those points to get the two corresponding points p3 and p4 on the other end cap. Finally it uses those points to make two rectangles between them.

Download the example and look at the code to see how the program uses the AddCylinder method to draw cylinders.

You can increase num_points to draw cylinders more smoothly, but don’t get too carried away. In my next post I’ll show how you can make the sides of the cylinder look smoother without making num_points huge.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawing, geometry, graphics, mathematics, wpf, XAML and tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to Draw cylinders using WPF and C#

  1. Pingback: Draw cones using WPF and C# - C# HelperC# Helper

  2. joe says:

    How can I modify this so I can see the Triangles that make up the cylinders?
    Kind of like the “Draw a 3D wireframe using WPF and C#” example.

  3. joe says:

    I have modified this program to call the ToWireframe method and now I have the triangles exposed. What is the best way to control the individual triangle sizes separately. I would like to have each individual triangles size and shape set differently depending on numbers in an array. Im looking to make a cylinder thats ruff and bumpy

    • RodStephens says:

      Ah. It depends a bit on exactly what effect you have in mind.

      One idea would be to break the cylinder into segments and then adjust the radii of the points at each segment. That would make different points bump in and out. You wold need to be sure to use one set of points, though, so the triangles in each segment line up.

      If you wanted something spikier, you could add tetrahedrons or pyramids poking out through the cylinder’s sides. You could vary the points’ heights.

      I think moving points out away from the axis is probably easiest. If you try to move them in other directions, too, you would have to be careful that they didn’t make their triangles overlap.

  4. Asma Khan says:

    Basically i have made classes like .. shape is the abstract class and than derived Point ,from Point derived circle and Cylinder class has been derived from Circle ..
    i wanted to draw all the shapes by calling methods of respective shape as shape behaviour

    • RodStephens says:

      You should be able to adapt the code from this example to do that.

      Note that you don’t really call an object’s methods to draw it. Instead the methods should create the 3D data structures that the system uses to draw. Basically you build the data structures that define the shapes’ points and triangles, and then the drawing system uses them to draw the shapes as needed.

Leave a Reply

Your email address will not be published. Required fields are marked *