# Amateur Geometry

Editorial Note: This one is mostly for me. I want to record my solution to a puzzle so that I can throw out my notes.

A few days ago I began to play with the Platonic solids. (I was fooling around with OpenGL, and needed some aesthetically pleasing shapes to render.) I could have just looked up their geometry, but elected instead to derive it from a combination of inspection, guesswork, and simple trigonometry. This isn’t the “right” way to solve the problem, and it doesn’t yield an elegant solution, but, hey, an answer is an answer. Below, I talk a bit about how I found it.

### Preliminaries

It’s a lot easier to solve this problem if you’ve got some solids to stare at, and, due to some embarrassing personal history, I do. The dice seen to the right proved quite helpful when reasoning about how the points of the polyhedra relate to one another.

There are an infinite number of representations of the Platonic solids; they can be scaled to any size, moved to any position, and rotated to any orientation. For our purposes we’re only interested in solids with points that all lie on the unit sphere. There are still an infinite number of those, but we’ll settle for any that are found in convenient orientations.

### Trivial Cases

The points of the octahedron and cube are easy to figure out. The 4 cardinal directions, plus top and bottom, give us the 6 points of the octahedron. A cube’s 8 points are mirrored by all 3 coordinate planes, and are therefore easily derived.

In the interests of completeness, here’s a little Python to generate points and faces for these two polyhedra:

``````def make_hexahedron_points():
d = 1.0/math.sqrt(3)

return ((-d,-d, d),( d,-d, d),( d, d, d),(-d, d, d),
(-d,-d,-d),( d,-d,-d),( d, d,-d),(-d, d,-d))

octohedron_vertices = (( 0.0,  1.0,  0.0),
(-1.0,  0.0,  0.0),
( 0.0,  0.0,  1.0),
( 1.0,  0.0,  0.0),
( 0.0,  0.0, -1.0),
( 0.0, -1.0,  0.0))

octohedron_faces = ((5,2,1),(5,3,2),(5,4,3),(5,1,4),(0,1,2),(0,2,3),(0,3,4),(0,4,1))

hexahedron_vertices = make_hexahedron_points()

hexahedron_faces = ((0,1,2,3),(4,7,6,5),
(0,4,5,1),(1,5,6,2),(2,6,7,3),(3,7,4,0))``````

### Tangent

The remaining three solids require a bit more analysis, but, fortunately, 90% of that analysis is shared between them. Let’s begin by considering 4 abstract points near the top of the unit sphere:

``````A = (0,1,0)
B = (0,1-Y,Z)
C = (X,1-Y,Zc)
D = (0,1-Y,0)
|A|=|B|=|C|=1``````

Furthermore, let’s specify that `|AB|==|AC|`, label the angle between `AB` and `AC` as `theta`, and the angle between `DB` and `DC` as `phi`. (Yes, this would be easier if I knocked together some illustrations, but I’m lazy and the blog is free. Anyway.)

Consider the triangles `ADC` and `ADB`. Since these are both right triangles, we know that `|DC|=|DB|`:

``````|AD|*|AD|+|DC|*|DC|=|AC|*|AC|		(pythagorean t.)
|AC|*|AC|=|AB|*|AB|			(|AB|==|AC| given)
|DC|*|DC|=|DB|*|DB|
|DC|=|DB|``````

By taking the `DB*DC` dot product, we can eliminate `Zc` as an independent variable:

``````DB=(0,0,Z)				(vector subtraction)
DC=(X,0,Zc)				(vector subtraction)
|DB| = Z				(length definition)
|DC| = |DB| = Z				(earlier result)
DB*DC=Z*Zc				(dot product definition)
DB*DC=cos(phi)*|DB|*|DC|		(dot product geometric interpretation)
Z*Zc=cos(phi)*Z*Z
Zc=Z*cos(phi)``````

Finally, from the `AB*AC` dot product, we can calculate `T`, where `Y=Z*T`:

``````AB=(0,-Y,Z)					(vector subtraction)
AC=(X,-Y,Zc)					(vector subtraction)
|AB|=sqrt(Y*Y+Z*Z)				(length definition)
|AC|=|AB|					(given)
AB*AC=Y*Y+Z*Zc					(dot product definition)
AB*AC=cos(theta)*|AB|*|AC|			(dot product geometric interpretation)
Y*Y+Z*Zc=cos(theta)*|AB|*|AB|
Y*Y+Z*Zc=cos(theta)*(Y*Y+Z*Z)
Y*Y+Z*Zc=cos(theta)*Y*Y+cos(theta)*Z*Z
Y*Y*(1-cos(theta))=cos(theta)*Z*Z-Z*Zc
Y*Y*(1-cos(theta))=cos(theta)*Z*Z-Z*Z*cos(phi)	(earlier result)
Y*Y*(1-cos(theta))=Z*Z*(cos(theta)-cos(phi))
Y*Y/(Z*Z)=(cos(theta)-cos(phi))/(1-cos(theta))
Y/Z=sqrt((cos(theta)-cos(phi))/(1-cos(theta)))
T=sqrt((cos(theta)-cos(phi))/(1-cos(theta)))``````

Let’s now define `Z` in terms of `T` (and, indirectly, define the absolute location of all 4 points in terms of the angles `theta` and `phi`):

``````|B|=1					(given)
|B|=sqrt((1-Y)*(1-Y)+Z*Z)		(length definition)
(1-Y)*(1-Y)+Z*Z=1
1-2*Y+Y*Y+Z*Z=1
1-2*Z*T+Z*T*Z*T+Z*Z=1			(definition of T)
Z*Z*(T*T+1)=2*Z*T
Z=2*T/(T*T+1)``````

### Tetrahedron

A tetrahedron inscribed in the unit sphere can be oriented s.t. three of its vertices map onto the just-discussed points `A`, `B`, and `C` with an angle `theta` of 60 degrees, and an angle `phi` of 120 degrees. (In such an orientation the triangle `ABC` is an equilateral face of the tetrahedron and `phi` covers 1/3rd of a full rotation.) Grinding the math gives a `T` of `sqrt(2)`. Here’s some Python:

``````def make_tetrahedron_points():
z = 2.0*math.sqrt(2)/3.0
y = -1.0/3.0

zsin60 = z*math.sin(60*math.pi/180)
zcos60 = z*math.cos(60*math.pi/180)

return ((0.0,1.0,0.0),(0.0,y,z),(zsin60,y,-zcos60),(-zsin60,y,-zcos60))

tetrahedron_vertices = make_tetrahedron_points()

tetrahedron_faces = ((1,3,2),(1,0,3),(3,0,2),(2,0,1))``````

### Icosahedron

In a very similar way, an icosahedron inscribed in the unit sphere can be oriented s.t. three of its vertices map onto the just-discussed points `A`, `B`, and `C` with an angle `theta` of 60 degrees, and an angle `phi` of 72 degrees. (In such an orientation the triangle `ABC` is an equilateral face of the icosahedron and `phi` covers 1/5th of a full rotation, since the bases of the 5 faces which meet at `A` form a pentagon.) Grinding the math gives a `T` of `sqrt(1.0-2.0*cos72)`. Inspection of our model reveals that 2 points of the polyhedron are at the top and bottom of the unit sphere, while the other 10 are arranged in two pentagons offset by `Y` from the poles. Here’s some Python:

``````def make_icosahedron_points():
cos72 = math.cos(72*math.pi/180)

t = math.sqrt(1.0-2.0*cos72)
z = t/(1.0-cos72)
y = 1.0 - z*t

zsin72 = z*math.sin(72*math.pi/180)
zcos72 = z*math.cos(72*math.pi/180)
zsin36 = z*math.sin(36*math.pi/180)
zcos36 = z*math.cos(36*math.pi/180)

return ((0.0,1.0,0.0),(0.0,y,z),
(zsin72,y,zcos72),(zsin36,y,-zcos36),(-zsin36,y,-zcos36),(-zsin72,y,zcos72),
(0.0,-1.0,0.0),(0.0,-y,-z),
(zsin72,-y,-zcos72),(zsin36,-y,zcos36),(-zsin36,-y,zcos36),(-zsin72,-y,-zcos72))

icosahedron_vertices = make_icosahedron_points()

icosahedron_faces = ((0,1,2),(0,2,3),(0,3,4),(0,4,5),(0,5,1),
(1,9,2),(2,8,3),(3,7,4),(4,11,5),(5,10,1),
(11,10,5),(10,9,1),(9,8,2),(8,7,3),(7,11,4),
(7,8,6),(8,9,6),(9,10,6),(10,11,6),(11,7,6))``````

### Dodecahedron

Dodecahedrons are the hardest Platonic solids to reason about. They have the most points (20) and the most obvious arrangement of those points (4 parallel pentagons) doesn’t directly map onto the `A,B,C` model used above. The shortest argument seems to go through the edge length, `|AB|`, which I’ll call `E`. Something like this:

``````AB=(0,-Y,Z)					(vector subtraction)
|AB|=sqrt(Y*Y+Z*Z)				(length definition)
|AB|=sqrt(Z*T*Z*T+Z*Z)				(definition of T)
|AB|=sqrt(Z*Z*(T*T+1))
|AB|=sqrt(Z*2*T)				(earlier result)
|AB|=sqrt(4*T*T/(T*T+1))			(earlier result)

T=sqrt((cos(theta)-cos(phi))/(1-cos(theta)))	(earlier result)
T*T=(cos(theta)-cos(phi))/(1-cos(theta))
T*T+1=(1-cos(phi))/(1-cos(theta))
T*T/T*T+1=(cos(theta)-cos(phi))/(1-cos(phi))
|AB|=2*sqrt((cos(theta)-cos(phi))/(1-cos(phi)))
E=2*sqrt((cos(theta)-cos(phi))/(1-cos(phi)))	(definition of E)``````

Now, even though it’s not a convenient arrangement for most things, a dodecahedron inscribed in the unit sphere can be oriented s.t. three of its vertices map onto the above-discussed points `A`, `B`, and `C` with an angle `theta` of 108 degrees, and an angle `phi` of 120 degrees. (In such an orientation the triangle `ABC` is a partial face of the dodecahedron, and `theta` is an an interior angle of that pentagon. `Phi` covers 1/3rd of a full rotation, since the 3 edges which meet at `A` are symmetric. I admit that this construction isn’t watertight, but if you stare at a dodecahedron, I think you’ll find it convincing.) Grinding the math gives an `E` of `2*sqrt((cos108+0.5)/1.5)`.

Now we need to define the twenty vertices of a dodecahedron in terms of the single number `E` (and, admittedly, some arbitrary orientation terms). Inspection of our model reveals that its points are arranged in 4 pentagons, each parallel to the `XZ` plane. The edge length of the top and bottom pentagons is just the edge length of the dodecahedron, `E`. The edge length of the middle two pentagons — let’s call it `E2` — can be derived from `E`, since `E2` is the distance between two non-adjacent points of a face of the dodecahedron, which is just a pentagon with edge length `E`. From the pentagon edge lengths, we can compute the pentagon radiuses (distances from the pentagon centers to the pentagon vertices), and from the radiuses, the fact that the pentagons are centered on the `Y` axis, and the fact that the vertices lie on the unit sphere, we can compute the `Y`-offset of the pentagons themselves.

Yes, some pictures would really help. Also, the arguments above could be tightened up a lot. Instead, you get this:

``````E=2*sqrt((cos(theta)-cos(phi))/(1-cos(phi)))	(previous result)
sin(36)=0.5*E/R					(from face trigonometry)
R=0.5*E/sin(36)
Y1*Y1+R*R=1					(unit sphere)
Y1=sqrt(1-R*R)

sin(36)=0.5*E2/R2				(from pentagon trigonometry)
E2=2*E*sin(54)					(from face trigonometry)
R2=0.5*E2/sin(36)
R2=0.5*2*E*sin(54)/sin(36)
R2=2*R*sin(54)
Y2*Y2+R2*R2=1					(unit sphere)
Y2=sqrt(1-R2*R2)``````

And, at last, some Python:

``````def make_dodecahedron_points():
cos108 = math.cos(108*math.pi/180)
sin36 = math.sin(36*math.pi/180)
sin54 = math.sin(54*math.pi/180)

el = math.sqrt((4 + 8*cos108)/3)
z1 = 0.5*el/sin36
y1 = math.sqrt(1-z1**2)
z2 = 2*z1*sin54
y2 = math.sqrt(1-z2**2)

sin72 = math.sin(72*math.pi/180); z1sin72 = z1*sin72; z2sin72 = z2*sin72
cos72 = math.cos(72*math.pi/180); z1cos72 = z1*cos72; z2cos72 = z2*cos72
sin36 = math.sin(36*math.pi/180); z1sin36 = z1*sin36; z2sin36 = z2*sin36
cos36 = math.cos(36*math.pi/180); z1cos36 = z1*cos36; z2cos36 = z2*cos36

return ((0.0,y1,z1),
(z1sin72,y1,z1cos72),(z1sin36,y1,-z1cos36),(-z1sin36,y1,-z1cos36),(-z1sin72,y1,z1cos72),
(0.0,y2,z2),
(z2sin72,y2,z2cos72),(z2sin36,y2,-z2cos36),(-z2sin36,y2,-z2cos36),(-z2sin72,y2,z2cos72),
(0.0,-y1,-z1),
(z1sin72,-y1,-z1cos72),(z1sin36,-y1,z1cos36),(-z1sin36,-y1,z1cos36),(-z1sin72,-y1,-z1cos72),
(0.0,-y2,-z2),
(z2sin72,-y2,-z2cos72),(z2sin36,-y2,z2cos36),(-z2sin36,-y2,z2cos36),(-z2sin72,-y2,-z2cos72))

dodecahedron_vertices = make_dodecahedron_points()

dodecahedron_faces = ((0,1,2,3,4),
(0,5,17,6,1),(1,6,16,7,2),(2,7,15,8,3),(3,8,19,9,4),(4,9,18,5,0),
(10,11,12,13,14),
(10,15,7,16,11),(11,16,6,17,12),(12,17,5,18,13),(13,18,9,19,14),(14,19,8,15,10))``````

### In Conclusion

I’m left with a new appreciation for the ancient Greeks. It’s easy enough to construct these things with hand-wavy arguments and common trigonometry, but to work them out from first principles 2500 years ago is another matter entirely.

One can also get an inkling of why Plato associated the dodecahedron not with any of the 4 usual elements, but with the arrangement of “the constellations on the whole heaven”.

Share and Enjoy:
This entry was posted in Projects, Python. Bookmark the permalink.