Easily draw platonic solids in WPF and C#

[platonic solids]

This example basically combines and rearranges the techniques used by previous three-dimensional examples to make them easier to use. Its methods let you build platonic solids, geodesic spheres, and stellate spheres relatively easily.

One real change in this example is that it lets you pass a transformation into the solid creation methods so you can create the solids at locations other than the origin. That makes it easier to add multiple shapes at different locations to the same mesh.


The following AddTetrahedron method shows the general idea.

// Add a Tetrahedron to the mesh.
public static void AddTetrahedron(this MeshGeometry3D mesh,
    double side_length, Transform3D transform)
{
    Point3D[] points = TetrahedronVertices(side_length).ToArray();
    if (transform != null) transform.Transform(points);

    // Create the solid tetrahedron.
    MeshGeometry3D solid_mesh = new MeshGeometry3D();
    mesh.AddTriangle(points[0], points[1], points[2]);
    mesh.AddTriangle(points[0], points[2], points[3]);
    mesh.AddTriangle(points[0], points[3], points[1]);
    mesh.AddTriangle(points[3], points[2], points[1]);
}

The method first calls the TetrahedronVertices method to get an array containing the tetrahedron’s vertices. Then if the transform object isn’t null, the method applies it to the points. The method then uses the points to create the tetrahedron as usual.

For example, the example program uses the following code to create its cube.

MeshGeometry3D cube_mesh = new MeshGeometry3D();
cube_mesh.AddCube(1,
    new TranslateTransform3D(0, 0, 2));

This code creates a MeshGeometry3D. It then calls its AddCube extension method to add a cube to it. It passes the method a new TranslateTransform3D object that translates the cube’s points by distance 2 in the Z direction. If you look at the picture above, you’ll see that the cube is shifted to the left, which is the Z direction in this picture.

The following list shows the classes and methods used in this example that are intended for building shapes. They are grouped by the classes that contain them.

  • Edge – Represents 2 points that make up an edge. Implements IEquatable<Edge> so you can use it to make a list without duplicates.

  • Polygon – Represents an array of points that make up a polygon. Assumes the points are coplanar.

  • Triangle – Represents 3 points that make up a triangle.
    • Subdivide – Subdivides a triangle for use in making a geodesic sphere
    • Stellate – Adds a pyramid on top of a triangle for use in making a stellate geodesic sphere

  • TransformExtensions – Adds 1 extension method to the Transform3D class.
    • Transform – Make a Transform3D object transform a Triangle object.

  • VectorExtensions – Adds 1 extension method to the Vector3D class.
    • Scale – Adjusts a Vector3D to have a given length and returns the result.

  • MeshExtensions – Adds extension methods to the MeshGeometry3D class. Most of these methods either add shapes to a MeshGeometry3D object or return a new MeshGeometry3D object containing a shape.
    • AddPolygonNormal – Adds a segment showing the normal for a polygon.
    • AddPolygonNormal (with vertex names) – Adds a segment showing the normal for a polygon defined by vertex names and an array of points that include vertices that are not part of the polygon. (For the dodecahedron.)
    • ToTriangleNormals – Returns a new MeshGeometry3D object containing segments representing the triangles in this mesh.
    • FindTriangleNormal – Returns a Vector3D representing a triangle’s normal.
    • ToWireFrame – Returns a new MeshGeometry3D object containing segments representing the edges of the mesh’s triangles. (Uses a HashSet of Edge objects to add shared edges only once.)
    • AddPolygonWireframe – Adds a wireframe for a polygon to the mesh. (This is necessary for cubes and dodecahedrons because their faces are not triangles.)
    • AddPolygonWireframe (with vertex names) – Adds a wireframe for a polygon defined by an array of points and vertex names. (For use with the dodecahedron.)
    • ToVertexBoxes – Returns a new MeshGeometry3D object containing small boxes representing a mesh’s vertices.
    • AddTriangle – Adds a new triangle to the mesh. Assumes the points are outwardly oriented.
    • AddSegment – Adds a thin box to a mesh to represent a line segment. This method has several overloaded versions. For example, different versions take as parameters the segment’s end points as Point3D objects or end point coordinates. (Used to add normals, edges, axes, etc.)
    • AddBox – Adds a box defined by a corner and three vectors giving the box’s edge directions. If the vectors are parallel to the X, Y, and Z axes, you get a box parallel to the coordinate planes. If the axes are mutually perpendicular, you get a rectangular prism. If the axes are not perpendicular, you get a parallelepiped. (Which is a cool word that you don’t get to use very often.) (Assumes the vectors are outwardly oriented. In other words, the first vector crossed with the second should point in the same direction as the third.)
    • AddRectangle – Adds two triangles to the mesh to represent a rectangle. Defined by a corner point and two vectors. Assumes the points are outwardly oriented and actually do define a rectangle.
    • AddAxes – Adds segments representing the coordinate axes to the mesh. Parameters let you determine whether the axes extend on both sides of the origin and whether the axes include tick marks.
    • AddTriangles – Adds a list of Triangle objects to the mesh.
    • AddPolygon – Adds triangles representing a polygon to the mesh. Assumes the points are outwardly oriented and coplanar.
    • AddPolygon (with vertex names) – Adds triangles representing a polygon defined by an array of points and vertex names to the mesh. Assumes the points are outwardly oriented and coplanar.
    • MakeModel – Makes a solid colored model from a mesh.
    • MergeWith – Merges a mesh into this one.

  • PlatonicSolids
    • AddStellateSphere – Adds a stellate sphere to a mesh.
    • StellateSphereTriangles – Returns triangles representing a stellate sphere.
    • AddGeodesicSphere – Adds a geodesic sphere to a mesh.
    • GeodesicSphereTriangles – Returns triangles representing a geodesic sphere.
    • AddIcosahedron – Adds an icosahedron to a mesh.
    • IcosahedronTriangles – Returns triangles representing an icosahedron.
    • IcosahedronVertices – Returns an icosahedron’s vertices.
    • AddDodecahedron – Adds a dodecahedron to a mesh.
    • AddDodecahedronWireframe – Adds segments representing a dodecahedron’s wireframe to a mesh. (This is different from most other solids because its faces are not triangles. For most other solids you can use the MeshExtensions class’s ToWireframe method.)
    • AddDodecahedronNormals – Adds segments representing a dodecahedron’s normals to a mesh. (This is different from most other solids because its faces are not triangles. For most other solids you can use the MeshExtensions class’s ToTriangleNormals method.)
    • DodecahedronVertices – Returns a dodecahedron’s vertices.
    • AddOctahedron – Adds an octahedron to a mesh.
    • OctahedronVertices – Returns an octahedron’s vertices.
    • AddCube – Adds a cube to a mesh.
    • AddCubeWireframe – Adds segments representing a cube’s wireframe to a mesh. (This is different from most other solids because its faces are not triangles. For most other solids you can use the MeshExtensions class’s ToWireframe method.)
    • AddCubeNormals – Adds segments representing a cube’s normals to a mesh. (This is different from most other solids because its faces are not triangles. For most other solids you can use the MeshExtensions class’s ToTriangleNormals method.)
    • CubeVertices – Returns a cube’s vertices.
    • AddTetrahedron – Adds a tetrahedron to a mesh.
    • TetrahedronVertices – Returns a tetrahedron’s vertices.

The example uses those methods to create a collection of example objects. The following code shows how the program creates its octahedron.

// Octahedron.
MeshGeometry3D octahedron_mesh = new MeshGeometry3D();
octahedron_mesh.AddOctahedron(1,
    new TranslateTransform3D(-2, 0, 0));
SolidModels.Add(octahedron_mesh.MakeModel(Colors.Pink));

wireframe_mesh.MergeWith(
    octahedron_mesh.ToWireframe(line_thickness));
normal_mesh.MergeWith(
    octahedron_mesh.ToTriangleNormals(
        normal_length, line_thickness));
vertices_mesh.MergeWith(
    octahedron_mesh.ToVertexBoxes(vertex_width));

This code creates a new mesh for the octahedron and then calls its AddOctahedron extension method to add a new octahedron to the mesh. It passes in a TranslateTransform3D object to translate the new object by distance -2 in the X direction.

Next the code calls the MakeModel extension method to make a solid colored model for the mesh and it saves the model in the SolidModels list. (Each solid is saved as a separate model so they can each have different colors.)

Next the code calls ToWireframe to make a wireframe for the octahedron and merges the result into the wireframe_mesh mesh. (All of the objects’ wireframes are stored in the single mesh wireframe_mesh so they all have the same color.)

The code then calls ToTriangleNormals to get the octahedron’s normal segments and merges the result into the normal_mesh mesh. (All of the objects’ normals are stored in the single mesh normal_mesh so they all have the same color.)

Finally the code calls ToVertexBoxes to get boxes representing the octahedron’s vertices and merges the result into the vertices_mesh mesh. (All of the objects’ vertices are stored in the single mesh vertices_mesh so they all have the same color.)

The following DisplaySelectedModels method displays the models selected by the program’s check boxes.

// Display the selected models.
private void DisplaySelectedModels()
{
    MainModelGroup.Children.Clear();

    foreach (Light light in Lights)
        MainModelGroup.Children.Add(light);

    if (chkAxes.IsChecked.Value)
        MainModelGroup.Children.Add(AxesModel);
    if (chkFaces.IsChecked.Value)
        foreach (GeometryModel3D model in SolidModels)
            MainModelGroup.Children.Add(model);
    if (chkWireframe.IsChecked.Value)
        MainModelGroup.Children.Add(WireframesModel);
    if (chkNormals.IsChecked.Value)
        MainModelGroup.Children.Add(NormalsModel);
    if (chkVertices.IsChecked.Value)
        MainModelGroup.Children.Add(VerticesModel);
}

The code first empties the model group’s Children collection and then adds all of the program’s lights to it. It then examines the check boxes and adds the appropriate models to the Children collection.

The only unusual step is where the code loops through the solid models to add them all. That’s different from the way the other models work because each solid is stored in a separate model contained in the SolidModels list.

That’s how the program works at a medium-high level. The extension methods defined by the example’s classes make building these objects relatively easy. Download the example and look at the code to see additional details.


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, graphics, mathematics, wpf and tagged , , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

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