Autodesk Creative Platform Core Version 1.19.0
A broad and deep collection of 2D and 3D capabilities.

Platonic Solid Generator - Part 2

This is the second part of the tutorial that shows how to build the elementary Platonic Shapes using the Autodesk Creative Platform Shape Generators.

In Platonic Solid Generator - Part 1, we learned the essentials of creating meshes in and built the first two solids, the tetrahedron and the cube. This second part will cover the remaining three solids: the octahedron, the dodecahedron and the icosahedron. In the course of building these more complex shapes, we'll use the graphical debugging aids.

Table of contents

Octahedron

The octahedron is a simple form compared to the last two solids we're going to encounter next. It's essentially two square-based pyramids glued to each other at the base.

Octahedron

As you may remember from the first part of this tutorial, the tetrahedron is a pyramid with a triangular base. Hence we can easily modify the tetrahedron's code to produce pyramids with a square base instead. The height of each pyramid is simply the radius of the shape — because these are regular solids, each vertex must lie at the same distance from the center.

There's not much else to say about constructing the octahedron, so here's the code:

function process(params) {
    var r = params.radius;
    var e = Math.sqrt(2) * r;
    var angle = Math.PI / 2;

    var sides = [];
    for (var i = 0; i < 4; i++) {
        var x = r * Math.cos(i * angle);
        var y = r * Math.sin(i * angle);
        sides.push([x, y, 0]);
    }
    var top = [0, 0, r];
    var bot = [0, 0, -r];

    var mesh = new Mesh3D();
    mesh.triangle(sides[0], sides[3], top);
    mesh.triangle(sides[1], sides[0], top);
    mesh.triangle(sides[2], sides[1], top);
    mesh.triangle(sides[3], sides[2], top);

    mesh.triangle(sides[3], sides[0], bot);
    mesh.triangle(sides[0], sides[1], bot);
    mesh.triangle(sides[1], sides[2], bot);
    mesh.triangle(sides[2], sides[3], bot);

    return Solid.make(mesh);
}

Dodecahedron

Dodecahedron construction

The dodecahedron is a more complicated animal. It's built out of 12 pentagonal faces and 20 vertices, so it has the most vertices of all the Platonic solids. (The last solid, icosahedron, has 20 faces but only 12 vertices.)

The dodecahedron's vertex placement contains some rather interesting proportions. The famous golden ratio makes an appearance in the rectangles formed by many of the dodecahedron's vertices that lie on the same plane. The Wikipedia article has a useful picture showing these rectangles within the dodecahedron. You can find it reproduced on the right.

The illustration shows a cube and three rectangles on the x/y/z planes. I find this to be a reasonably straightforward way of visualizing the dodecahedron's vertex relationships, so I'll use it as the basis for constructing the shape.

The picture tells us the coordinates for the cube and rectangle vertices. But as you can see, the coordinates are given in relation to the dodecahedron's inner cube - the cube vertices lie at (±1, ±1, ±1). In order to create the dodecahedron in respect to the same radius parameter that we've used for the other Platonic shapes, we need one more number: the relation of the dodecahedron's inner cube to its outer radius. This number is given in the Wikipedia article, it's √3. Armed with this knowledge, let's build the vertices.

var r = params.radius;
var m = 1 / Math.sqrt(3) * r;
var phi = (1 + Math.sqrt(5)) / 2;  // The golden ratio, ~1.618

var r1 = 1 * m;
var phi1 = phi * m;
var iphi1 = (1.0 / phi) * m;

var cubeBot = [];
cubeBot.push([-r1, -r1, -r1]);
cubeBot.push([r1, -r1, -r1]);
cubeBot.push([r1, r1, -r1]);
cubeBot.push([-r1, r1, -r1]);

var cubeTop = [];
cubeTop.push([-r1, -r1, r1]);
cubeTop.push([r1, -r1, r1]);
cubeTop.push([r1, r1, r1]);
cubeTop.push([-r1, r1, r1]);

var xrect = [];
xrect.push([0, -iphi1, -phi1]);
xrect.push([0, -iphi1, phi1]);
xrect.push([0, iphi1, phi1]);
xrect.push([0, iphi1, -phi1]);

var zrect = [];
zrect.push([-iphi1, -phi1, 0]);
zrect.push([iphi1, -phi1, 0]);
zrect.push([iphi1, phi1, 0]);
zrect.push([-iphi1, phi1, 0]);

var yrect = [];
yrect.push([-phi1, 0, -iphi1]);
yrect.push([phi1, 0, -iphi1]);
yrect.push([phi1, 0, iphi1]);
yrect.push([-phi1, 0, iphi1]);

If you look back at the picture, the xrect vertices are marked in green, the zrect vertices are in blue and the yrect vertices are in pink.

Now we've defined a cube and three rectangles, but we can't actually see them. There's a quick solution available: There's the Debug utilities which gives us the possibility to draw lines using Debug.line that are only visible while we're working on the script. This is a convenient way of visualizing 3D elements in space without having to define a valid solid.

Let's draw debug visualization lines for the cube and the three rectangles. These debug lines can also have different colors using the Debug.color function, so let's use the same colors (orange, green, blue and pink) as in the illustration.

Debug.color(1, 0.65, 0, 1);
Debug.line(cubeBot[0], cubeBot[1]);
Debug.line(cubeBot[1], cubeBot[2]);
Debug.line(cubeBot[2], cubeBot[3]);
Debug.line(cubeBot[3], cubeBot[0]);
Debug.line(cubeTop[0], cubeTop[1]);
Debug.line(cubeTop[1], cubeTop[2]);
Debug.line(cubeTop[2], cubeTop[3]);
Debug.line(cubeTop[3], cubeTop[0]);
Debug.line(cubeBot[0], cubeTop[0]);
Debug.line(cubeBot[1], cubeTop[1]);
Debug.line(cubeBot[2], cubeTop[2]);
Debug.line(cubeBot[3], cubeTop[3]);

Debug.color(0, 0.8, 0, 1);
Debug.line(xrect[0], xrect[1]);
Debug.line(xrect[1], xrect[2]);
Debug.line(xrect[2], xrect[3]);
Debug.line(xrect[3], xrect[0]);

Debug.color(0, 0.1, 1.0, 1);
Debug.line(zrect[0], zrect[1]);
Debug.line(zrect[1], zrect[2]);
Debug.line(zrect[2], zrect[3]);
Debug.line(zrect[3], zrect[0]);

Debug.color(1, 0.2, 0.8, 1);
Debug.line(yrect[0], yrect[1]);
Debug.line(yrect[1], yrect[2]);
Debug.line(yrect[2], yrect[3]);
Debug.line(yrect[3], yrect[0]);

You should now see this:

Dodecahedron debug lines

Ok, now that we've verified that the vertices are where we want them to be, it's time to build the surface.

There is no readymade function on the Mesh type for creating pentagons, as it's a fairly rare type of surface element compared to triangles and quads. This is not a problem since we can easily define our own helper function for creating pentagons out of triangles. The following is the complete code for the dodecahedron.

params = [
    {
        “id”: “radius”,
        “displayName”: “Radius”,
        “type”: “float”,
        “rangeMin”: 1,
        “rangeMax”: 50,
        “default”: 20
    }
];
function process(params) {
    var r = params.radius;
    var m = 1 / Math.sqrt(3) * r;
    var phi = (1 + Math.sqrt(5)) / 2;  // The golden ratio, ~1.618

    var r1 = 1 * m;
    var phi1 = phi * m;
    var iphi1 = (1.0 / phi) * m;

    var cubeBot = [];
    cubeBot.push([-r1, -r1, -r1]);
    cubeBot.push([r1, -r1, -r1]);
    cubeBot.push([r1, r1, -r1]);
    cubeBot.push([-r1, r1, -r1]);

    var cubeTop = [];
    cubeTop.push([-r1, -r1, r1]);
    cubeTop.push([r1, -r1, r1]);
    cubeTop.push([r1, r1, r1]);
    cubeTop.push([-r1, r1, r1]);

    var xrect = [];
    xrect.push([0, -iphi1, -phi1]);
    xrect.push([0, -iphi1, phi1]);
    xrect.push([0, iphi1, phi1]);
    xrect.push([0, iphi1, -phi1]);

    var zrect = [];
    zrect.push([-iphi1, -phi1, 0]);
    zrect.push([iphi1, -phi1, 0]);
    zrect.push([iphi1, phi1, 0]);
    zrect.push([-iphi1, phi1, 0]);

    var yrect = [];
    yrect.push([-phi1, 0, -iphi1]);
    yrect.push([phi1, 0, -iphi1]);
    yrect.push([phi1, 0, iphi1]);
    yrect.push([-phi1, 0, iphi1]);

    if (false) {
        // Draw debug lines to show structures within the dodecahedron:
        // the vertices form a cube and three rectanges on each axis.
        Debug.color(1, 0.65, 0, 1);
        Debug.line(cubeBot[0], cubeBot[1]);
        Debug.line(cubeBot[1], cubeBot[2]);
        Debug.line(cubeBot[2], cubeBot[3]);
        Debug.line(cubeBot[3], cubeBot[0]);

        Debug.line(cubeTop[0], cubeTop[1]);
        Debug.line(cubeTop[1], cubeTop[2]);
        Debug.line(cubeTop[2], cubeTop[3]);
        Debug.line(cubeTop[3], cubeTop[0]);

        Debug.line(cubeBot[0], cubeTop[0]);
        Debug.line(cubeBot[1], cubeTop[1]);
        Debug.line(cubeBot[2], cubeTop[2]);
        Debug.line(cubeBot[3], cubeTop[3]);

        Debug.color(0, 0.8, 0, 1);
        Debug.line(xrect[0], xrect[1]);
        Debug.line(xrect[1], xrect[2]);
        Debug.line(xrect[2], xrect[3]);
        Debug.line(xrect[3], xrect[0]);

        Debug.color(0, 0.1, 1.0, 1);
        Debug.line(zrect[0], zrect[1]);
        Debug.line(zrect[1], zrect[2]);
        Debug.line(zrect[2], zrect[3]);
        Debug.line(zrect[3], zrect[0]);

        Debug.color(1, 0.2, 0.8, 1);
        Debug.line(yrect[0], yrect[1]);
        Debug.line(yrect[1], yrect[2]);
        Debug.line(yrect[2], yrect[3]);
        Debug.line(yrect[3], yrect[0]);
    }

    var mesh = new Mesh3D();

    function penta(a, b, c, d, e) {
        mesh.triangle(a, e, d);
        mesh.triangle(a, d, c);
        mesh.triangle(a, c, b);
    }

    penta(cubeTop[0], xrect[1], cubeTop[1], zrect[1], zrect[0]);
    penta(cubeTop[2], xrect[2], cubeTop[3], zrect[3], zrect[2]);

    penta(cubeBot[1], xrect[0], cubeBot[0], zrect[0], zrect[1]);
    penta(cubeBot[3], xrect[3], cubeBot[2], zrect[2], zrect[3]);

    penta(xrect[0], xrect[3], cubeBot[3], yrect[0], cubeBot[0]);
    penta(xrect[3], xrect[0], cubeBot[1], yrect[1], cubeBot[2]);

    penta(xrect[2], xrect[1], cubeTop[0], yrect[3], cubeTop[3]);
    penta(xrect[1], xrect[2], cubeTop[2], yrect[2], cubeTop[1]);

    penta(cubeTop[2], zrect[2], cubeBot[2], yrect[1], yrect[2]);
    penta(cubeBot[1], zrect[1], cubeTop[1], yrect[2], yrect[1]);

    penta(cubeBot[3], zrect[3], cubeTop[3], yrect[3], yrect[0]);
    penta(cubeTop[0], zrect[0], cubeBot[0], yrect[0], yrect[3]);

    return Solid.make(mesh);
}

Icosahedron

Finally we have the icosahedron. Again looking at the Wikipedia article, there are a number of ways to construct this solid:

  • In a similar way as the dodecahedron, using three rectangles for vertex placement. These have the nice property of being so-called golden rectangles, i.e. the aspect ratio of each rectangle is Φ : 1, where Φ (phi) is the golden ratio that we encountered earlier.
  • A regular octahedron can be subdivided along its edges so that the resulting vertices define an icosahedron. In this case the edges are subdivided using the golden ratio (once again).
  • It's also possible to use spherical coordinates to define an icosahedron. This is a mathematically somewhat more complex way to build the shape, but once the work is done, it can be expanded to other shapes that can be easily defined in spherical coordinates.

Let's take a shortcut and create an icosahedron using the last approach, but instead of doing all the work ourselves, we'll just modify the existing Sphere example script.

You can load the Sphere script either by creating a new shape or by choosing it from the list of example scripts:

Examples

Once loaded, modify the following line in the script:

var ndivs = Tess.circleDivisions(r);

So that it reads:

var ndivs = 1;

Press Save, and you'll see the sphere replaced with an icosahedron.

Try changing ndivs to a higher value, for example 7, and the shape becomes more rounded:

Sphere with lod=1

The sphere is generated using a level-of-detail (LOD) approach, so the number of faces will jump up in increments where basically each vertex of the icosahedron is subdivided into five more vertices. You can see this in the while() loop that calculates the LOD from the number of circle divisions: the lod variable is incremented for every multiple of six.

Ok, so we cheated a bit by making the icosahedron like this. There is another formulation available in the example scripts where the icosahedron is built as simply as possible from its basic math properties.

As an exercise you can try making the icosahedron in the same way as the dodecahedron: first create the three golden rectangles that define the icosahedron vertices, draw them using Debug.line to verify that the coordinates are what you expect, and then build the surface from triangles.

Thanks for reading this far! I hope you've learned something along the way. In closing, I give you this family portrait of Plato's brainchildren brought to the 21st century in the Autodesk Creative Platform. May your endeavors in the world of solid geometry be rewarding and fruitful.