# NAME

Math::Polyhedra - locate vertices, edges, and faces of common polyhedra

# SYNOPSIS

``````    use Math::Polyhedra qw(polyhedron vertices edges faces tris);

my \$hedron = polyhedron('rhombic dodecahedron');
my \$vertices = vertices(\$hedron);
my \$edges = edges(\$hedron);
my \$faces = faces(\$hedron);
my \$tris = tris(\$hedron);``````

# ABSTRACT

This module calculates and structures the coordinates of a library of commonly useful regular polyhedra. These geometrical figures can be selected by name, or by the number of sides.

The heart of the data is a set of 62 coordinates, from which each of these common polyhedra can be defined. Each of the vertices can be defined as a set of points around the origin measured with various multiples and powers of phi, also known as the Golden Ratio, which is approximately 1.618.

This package was inspired by the nice reference page provided by Robert W. Gray: http://www.rwgrayprojects.com/Lynn/Coordinates/coord01.html. Other sites also explore the Golden Ratio and these polyhedra.

# FUNCTIONS

## phi()

The `Golden Ratio` is usually referred by the greek letter phi. This can be defined by the following expression:

``````                   ____
1 + \/ 5
phi  =  ------------  =  (1 + sqrt(5)) / 2
2``````

This function simply returns that numeric value, which comes close to 1.61803398874989. This number, like pi, has many interesting properties and appears both in nature and invention.

## coordinates()

Returns a list reference containing the master set of coordinate vectors. All or most of these coordinates are calculated from the value of phi.

Without arguments, the points are only allocated and calculated on the first call to this function. Subsequent calls without arguments will return the same list reference each time. Each vector is a simple unblessed list reference, like `[ \$x, \$y, \$z ]`.

With any argument, this function will calculate a new list afresh. This takes slightly more memory and time, but some algorithms may want to change the given coordinates, which would otherwise spoil future calls that would return the modified list.

If the first argument given is a code reference, then that code is called once per vector, with the vector as an argument. The result of that code then replaces the original vector in the master set used by the other functions. This can be used to bless or convert the vectors with other vector math packages:

``````    use Math::Polyhedra;
use Math::VectorReal;
my \$coords = coordinates( sub { vector(@{\$_}) } );``````

After the above code, all vectors returned by any function in this module are then referring to blessed `Math::VectorReal` object instances. Just about any vector library would work similarly. The choice of which vector module to use is arbitrary, since it's the given code reference which drives the vector conversion.

## polyhedra(), polyhedron()

Retrieves a reference to a polyhedron structure by its name or the number of sides. This is a read-only structure which defines all of the vertex, edge and face information for the given polyhedron figure.

The argument should either be a number, or a name. For example, `polyhedron(6)` and `polyhedron('cube')` and `polyhedron('rhombic hexahedron')` are all equivalent methods to retrieve the structure representing regular six-sided cubical figures.

``````    my \$cube = polyhedron(6); # regular six-sided figure
my \$ico = polyhedron(20); # icosahedron has 20 triangular faces
my \$rhombdod = polyhedron(\$_ = 'rhombic dodecahedron');
print 'Found ', scalar @\$ico, ' variations of ', \$_, \$/;``````

The values inside the reference should not be modified as they are not recalculated on each subsequent call. The scalar reference is intended to be given as an argument to the `vertices()`, `edges()`, `faces()`, or `tris()` functions in this module.

The set of known names and their faces are as follows:

• tetrahedron (4 triangular faces) [10 variations]

• cube, hexahedron (6 square faces) (+6) (-6) [5 variations]

• octahedron (8 triangular faces) [5 variations]

• dodecahedron (12 pentagonic faces)

• rhombic dodecahedron (12 diamond faces) (-12) [5 variations]

• icosahedron (20 triangular faces)

• rhombic triacontahedron (30 diamond faces) (-30)

• hexicosahedron (120 irregular triangular faces)

Other polyhedra such as the decahedron, a common rhombic ten-sided figure, cannot be constructed solely using the value of phi, and are not currently supported by this module. The name 'hexicosahedron' may be apocryphal, since it's not a regular shape with 120 regular sides, but is instead comprised of various unions of the other volumes presented.

For a given polyhedron structure, more than one variation of the structure may be known. There are ten different `'tetrahedra'` defined by the 62 phi point library, for example. The expression `(scalar @\$hedron)` for a given structure will return how many variations are defined, and `(\$hedron->)` will select that specific variety for the other functions. These variations are not different in shape, but merely in differing orientations relative to the origin. Most applications don't need this information, but some studies of the Golden Ratio may select from these variations.

The `polyhedra()` function is just a convenient alias for the preferred name, `polyhedron()`.

## vertices()

``````    my \$verts = vertices(\$cube);
while (@\$verts)
{ draw_dot(shift @\$verts); }``````

Returns a list reference, containing one vector for each vertex in the polyhedron. Each vertex is itself a list reference of real coordinate values, such as this ` [ \$x, \$y, \$z ] ` triple.

## edges()

``````    my \$edges = edges(\$cube);
while (@\$edges)
{    move_to(shift @\$edges);
draw_to(shift @\$edges);
}``````

Returns a list reference, which contains two vectors defining each edge of the polyhedra. Each pair is an independent edge; the first edge is the vectors indexed 0 and 1, then index 2 to index 3, and so on. (This is not an optimized "line strip.") The vector references will be repeated as required.

## faces()

Returns a list reference, which contains sublists of at least three coplanar vectors defining each face. Each list is an independent face. The vector references will be repeated as required.

The vertices in each face are not sorted or ordered around the circumference of the face. Typically, graphics programs should use the `edges()` or `tris()` for ordering information.

## tris()

``````    my \$tris = tris(\$cube);
while (@\$tris)
{    first_vertex(shift @\$tris);
second_vertex(shift @\$tris),
third_vertex(shift @\$tris);
}``````

Returns a list reference, which contains three [ X, Y, Z ] coordinate triples for each triangle in sublists. Each triple is an independent triangle; the first triangle is between vectors 0, 1, 2, then another triangle between vectors 3, 4, 5, and so on. (This is not an optimized "triangle strip.") The vector references will be repeated as required.

All of the triangles returned are defined in clockwise order, such that the implied normals (B-A)x(C-A) are facing "outward," away from the origin. Many graphics applications require consistent definition to calculate proper outward-facing normals for lighting or culling.

# EXAMPLE

``````    use Math::Polyhedra qw(polyhedron vertices edges faces);

# Get the geometry.
my \$hedron = polyhedron(\$_ = 'cube');
my \$verts = vertices(\$hedron);
my \$edges = edges(\$hedron);
my \$faces = faces(\$hedron);

# Validate Euler's Formula
\$verts = (scalar @\$verts);
\$edges = (scalar @\$edges) / 2; # each edge shared for two faces
\$faces = (scalar @\$faces);
print "\$_ has \$verts vertices, \$edges edges, and \$faces faces.\n";
my \$Euler = \$verts - \$edges + \$faces;
my \$is = \$Euler==2? "is" : "is not";
print "V - E + F = \$Euler so the \$_ \$is simple.\n";``````

# FUTURE WORK

The current module only has a small set of popular polyhedra, those which can be defined by the use of the Golden Ratio, or phi. Future extensions could add non-phi shapes such as decahedrons, various tesselated forms, and a set of useful prisms.

The `faces()` routine should probably derive the proper order of the vertices returned in each face. Some graphics libraries can (re)do the triangular subdivision with better consistency in the implied normals.

Another useful feature would be to return useful `normals()`, one vector for each face. This could augment or precompute the values needed by some graphics libraries which refuse to work out the implied normals for you.

# AUTHOR

Ed Halley, <ed@halley.cc>