Draw smooth cylinders using WPF and C#

[draw smooth cylinders]

This example shows how to draw smooth cylinders in a WPF application. The example Draw cylinders using WPF and C# shows how to draw cylinders. See that example to learn the basic techniques.

To make WPF produce smooth cylinders, all you need to do is make the appropriate triangles share vertices. If two triangles share an edge, WPF interpolates the surface normal (the direction perpendicular to the surface) across the two triangles so they blend smoothly together instead of showing a sharp edge between them.

The program could use the previous code to draw the cylinder’s end caps. Because the triangles in an end cap lie in the same plane, they have the same normals whether they share points or not. However, this example makes them share points to save a little memory in the mesh’s Positions collection.

The following code shows this example’s DrawSmoothCylinder method.

// Add a cylinder with smooth sides.
private void AddSmoothCylinder(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.
    // Make the end point.
    int pt0 = mesh.Positions.Count; // Index of end_point.
    mesh.Positions.Add(end_point);

    // Make the top points.
    double theta = 0;
    double dtheta = 2 * Math.PI / num_sides;
    for (int i = 0; i < num_sides; i++)
    {
        mesh.Positions.Add(end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2);
        theta += dtheta;
    }

    // Make the top triangles.
    int pt1 = mesh.Positions.Count - 1; // Index of last point.
    int pt2 = pt0 + 1;                  // Index of first point.
    for (int i = 0; i < num_sides; i++)
    {
        mesh.TriangleIndices.Add(pt0);
        mesh.TriangleIndices.Add(pt1);
        mesh.TriangleIndices.Add(pt2);
        pt1 = pt2++;
    }

    // Make the bottom end cap.
    // Make the end point.
    pt0 = mesh.Positions.Count; // Index of end_point2.
    Point3D end_point2 = end_point + axis;
    mesh.Positions.Add(end_point2);

    // Make the bottom points.
    theta = 0;
    for (int i = 0; i < num_sides; i++)
    {
        mesh.Positions.Add(end_point2 +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2);
        theta += dtheta;
    }

    // Make the bottom triangles.
    theta = 0;
    pt1 = mesh.Positions.Count - 1; // Index of last point.
    pt2 = pt0 + 1;                  // Index of first point.
    for (int i = 0; i < num_sides; i++)
    {
        mesh.TriangleIndices.Add(num_sides + 1);    // end_point2
        mesh.TriangleIndices.Add(pt2);
        mesh.TriangleIndices.Add(pt1);
        pt1 = pt2++;
    }

    // Make the sides.
    // Add the points to the mesh.
    int first_side_point = mesh.Positions.Count;
    theta = 0;
    for (int i = 0; i < num_sides; i++)
    {
        Point3D p1 = end_point +
            Math.Cos(theta) * v1 +
            Math.Sin(theta) * v2;
        mesh.Positions.Add(p1);
        Point3D p2 = p1 + axis;
        mesh.Positions.Add(p2);
        theta += dtheta;
    }

    // Make the side triangles.
    pt1 = mesh.Positions.Count - 2;
    pt2 = pt1 + 1;
    int pt3 = first_side_point;
    int pt4 = pt3 + 1;
    for (int i = 0; i < num_sides; i++)
    {
        mesh.TriangleIndices.Add(pt1);
        mesh.TriangleIndices.Add(pt2);
        mesh.TriangleIndices.Add(pt4);

        mesh.TriangleIndices.Add(pt1);
        mesh.TriangleIndices.Add(pt4);
        mesh.TriangleIndices.Add(pt3);

        pt1 = pt3;
        pt3 += 2;
        pt2 = pt4;
        pt4 += 2;
    }
}

This code first creates perpendicular vectors as before. Next it adds the end point to the mesh’s Positions collection. The code then adds the points around the edge of the first end cap, and loops through those points, using them to create the first end cap’s triangles.

The program repeats those steps for the second end cap.

Next the code makes another loop somewhat similar to the one it used to create the top end cap’s points. This time it adds the top end cap point and the corresponding bottom end cap point to the mesh. (Note that the sides and ends of the cylinder must use separate versions of the end cap points. If they share those points, then WPF will smooth out the edge between the end caps and the sides, which looks really strange.)

After it creates all of the side points, the code loops num_sides times and connects the points to make two triangles for each strip along the cylinder’s side. These are the same triangles created by the previous example, just created in a different way so the triangles can share vertices.

When you draw smooth cylinders in this way, WPF makes the edges between the cylinder’s side triangles look smooth. The end caps are still polygons, however, so you can see their corners. For example, you can clearly see in the picture above that the red cylinder’s end cap has 8 sides.
When a cylinder intersects another objects, you can also see the edges

When a cylinder intersects with another object, you can also see effects caused by the fact that the cylinder’s sides are rectangles. For example, in the picture above you can see polygonal edges where the cylinders intersect.

[draw smooth cylinders]

You can reduce those effects by increasing the number of triangles used to make the cylinders. For example, in the cylinders in the picture on the right, num_sides = 50. This produces a much smoother result, although it makes each cylinder use 202 points and 600 triangles. You’ll have to decide where to set the tradeoff between memory use and quality. (num_sides = 20 produces a very reasonable result.)


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.

8 Responses to Draw smooth cylinders using WPF and C#

  1. Phan Sang says:

    Hi Rods, I am making a program to draw a knot torus line And I need Your help.
    Here is the formula of the knot torus line:
    https://en.wikipedia.org/wiki/Torus_knot
    The problem is with the coordinate I have from this formula, I can draw the knot torus but it is just a thin line. I want to make real one which will look like the first image in the link above (A (3,−7)-3D torus knot). And I have to make some bended cylinders having the same radius, and the center of them, when connected, will be the thin line I have drawn. But I don’t know how to do that, I mean the bended cylinders, I hope you can help me. Thank you for reading all of these.

    By the way I do this in SharpGL and haven’t do anything in C#, but I think the solution is the same.

    Best Regards.

    • RodStephens says:

      This isn’t easy but here’s what you can do.

      Generate points along the knot. Let p1 and p2 be two adjacent points along the knot.

      If the knot is mostly horizontal (i.e. has no vertical sections), take the cross product of p1-p1 and <0,1,0> to get a vector v1 perpendicular to p1-p2. Then take the cross product of v1 and p1-p2 to get a second perpendicular vector v2.

      Set the lengths of v1 and v2 to the radius you want to give the knot.

      Now for theta = 0 to 2*pi find the points:
      p1 + Cos(theta) * v1 + Sin(theta) * v2
      and
      p2 + Cos(theta) * v1 + Sin(theta) * v2

      Those give points along the circumference of the knot’s cylinder. Use them make a cylinder “connecting” points p1 and p2.

      Do that all along the knot and you’ll have the knot as a series of cylinders. Make the triangles that represent the cylinders share vertices and you’ll get smoothing. Make the points p1 and p2 close together and the whole thing should look pretty smooth.

      Good luck! Put a comment here with a link to your result when you’re done so we can see it.

  2. Phan Sang says:

    Hi Rods, I made it! the knot torus, You can see it in “result” file (recently I have find out that SharpGL can not be install on VS 2015):

    https://drive.google.com/drive/my-drive

    I have written it in SharpGL haven’t made it in C# yet(SharpGL is a lot easier compare to C#). But there are some things that I am not satisfy:

    1. The method you told me to do is similar to Frenet-Frame formula, it is general way to draw any tube. Unfortunately it only work if I have an equation which show the relation between x, y and z.

    2.And more over the formula I used to draw the Knot-Torus:

    x = (float)(Math.Cos(u / q) * (r2 + r1 * Math.Cos(u / p) + r1 * Math.Cos(v -Math.PI)));
    y = (float)(Math.Sin(u / q) * (r2 + r1 * Math.Cos(u / p) + r1 * Math.Cos(v – Math.PI))); z = (float)(r1 * Math.Sin(u / p) + r1 * Math.Sin(v – Math.PI));

    it can not make a beautiful knot like the one on wikipedia and it doesn’t show when to recolor the tube so it will look more colorful. I still need to learn a lot of trigonometry tricks to do that.

    3. And I do not understand why they use angle (v-pi) instead of v. when I use v, the tube is transparent at some place, some is not. (v-pi) make the tube look normal again.

    It take me more than a week to improve the program but I don’t make any progress
    . So it’s time to put it on a side and someday I will study the torus knot more clearly.

    I have asked you many things so far but I haven’t introduce myself:
    My name is Phan Sang and I live in Vietnam (Sang is my first name because in Vietnam, last name is written before first name, it’s reverse to American name). I am 20 years old (I will be 21 at this May) And I am an university student, and I’m learning about computer vision. I found your Blog 2 years ago and it is fascinating. I hope one day we will meet real life. By a way, are you still working for Microsoft?

    • RodStephens says:

      Hi Phang,

      > 1. Unfortunately it only work if I have an equation which show
      > the relation between x, y and z.

      You should be able to step along the equations that draw the knot and add sections of cylinders to them. (My graphics book shows how to do that sort of thing, although it was written for VB 5/6 so it would need some big changes to make it work in .NET.)

      > 2.And more over the formula I used to draw the Knot-Torus:
      > it can not make a beautiful knot like the one on wikipedia and it
      > doesn’t show when to recolor the tube so it will look more
      > colorful. I still need to learn a lot of trigonometry tricks to do that.

      I don’t know for certain but I think they are adjusting the colors for different values of the parameter u. You could just try setting the colors to different values depending on what u is and see what happens.

      > 3. And I do not understand why they use angle (v-pi) instead of v.

      I don’t know about that. That shifts the Sin function by 180 degrees, but I don’t know how the equations work so I don’t know why that’s necessary.

      The transparency could be caused if some of the triangles are not outwardly oriented.

      > So it’s time to put it on a side and someday I will study the torus knot
      > more clearly.

      I know what you mean 😉 There are lots of things I wish I had time to look at more closely (including this).

      > By a way, are you still working for Microsoft?

      I’ve never worked for Microsoft. I am a Microsoft MVP but that’s an award they give to non-employees for helping the programming community by doing things like running the VB Helper and C# Helper web sites.

      Good luck with your computer vision studies! (Another thing I wish I had time to study but that’s a bit outside of what I can reasonably handle.)

      • Phan Sang says:

        Wow, that’s amazing, whenever my friends ask me for resources to learn C#. I tell them to visit your website and I told them:”The creator of this site is one the best C# programmer at Microsoft”. Now I know it’s only half true, ha ha :v .
        So do you work for specify company, organize or customers will ask you for doing, consulting some while you do it at your home?

        • RodStephens says:

          I have my own little company (just me) where I mostly I manage my web sites and write books, with some occasional consulting. (I would make a lot more money if it was the other way around, mostly consulting and only writing occasionally. 😉

Leave a Reply

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