Title: Easily draw platonic solids in WPF and C#
This example basically combines and rearranges the techniques used by previous threedimensional 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 mediumhigh level. The extension methods defined by the example's classes make building these objects relatively easy.
Download the example to experiment with it and to see additional details.
