File Coverage

blib/lib/Math/Polyhedra.pm
Criterion Covered Total %
statement 69 77 89.6
branch 22 34 64.7
condition 7 12 58.3
subroutine 9 10 90.0
pod 8 8 100.0
total 115 141 81.5


line stmt bran cond sub pod time code
1             # Math::Polyhedra - locate vertices, edges, and faces of common polyhedra
2              
3             package Math::Polyhedra;
4              
5             #----------------------------------------------------------------------------
6             #
7             # Copyright (C) 1998-2003 Ed Halley
8             # http://www.halley.cc/ed/
9             #
10             # Data Copyright (C) Robert W. Gray, Used with Permission.
11             # http://www.rwgrayprojects.com/Lynn/Coordinates/coord01.html
12             #
13             #----------------------------------------------------------------------------
14              
15             BEGIN
16             {
17 1     1   32183 use vars qw($VERSION @ISA);
  1         3  
  1         105  
18 1     1   2 $VERSION = 0.7;
19 1         18 @ISA = qw(Exporter);
20 1         5643 @EXPORT_OK = qw(phi coordinates polyhedron polyhedra
21             vertices edges faces tris);
22             }
23              
24             =head1 NAME
25              
26             Math::Polyhedra - locate vertices, edges, and faces of common polyhedra
27              
28             =head1 SYNOPSIS
29              
30             use Math::Polyhedra qw(polyhedron vertices edges faces tris);
31              
32             my $hedron = polyhedron('rhombic dodecahedron');
33             my $vertices = vertices($hedron);
34             my $edges = edges($hedron);
35             my $faces = faces($hedron);
36             my $tris = tris($hedron);
37              
38             =head1 ABSTRACT
39              
40             This module calculates and structures the coordinates of a library of
41             commonly useful regular polyhedra. These geometrical figures can be
42             selected by name, or by the number of sides.
43              
44             The heart of the data is a set of 62 coordinates, from which each of
45             these common polyhedra can be defined. Each of the vertices can be
46             defined as a set of points around the origin measured with various
47             multiples and powers of I, also known as the S, which
48             is approximately 1.618.
49              
50             This package was inspired by the nice reference page provided by Robert
51             W. Gray: L.
52             Other sites also explore the S and these polyhedra.
53              
54             =cut
55              
56             #----------------------------------------------------------------------------
57              
58             =head1 FUNCTIONS
59              
60             =head2 phi()
61              
62             The C is usually referred by the greek letter I. This
63             can be defined by the following expression:
64              
65             ____
66             1 + \/ 5
67             phi = ------------ = (1 + sqrt(5)) / 2
68             2
69              
70             This function simply returns that numeric value, which comes close to
71             1.61803398874989. This number, like I, has many interesting
72             properties and appears both in nature and invention.
73              
74             =cut
75              
76             my $F = undef;
77             sub phi
78             {
79 2 100   2 1 13 return $F if $F;
80 1         3 $F = (1 + sqrt(5)) / 2;
81 1         10 return $F;
82             }
83              
84             =head2 coordinates()
85              
86             Returns a list reference containing the master set of coordinate vectors.
87             All or most of these coordinates are calculated from the value of I.
88              
89             Without arguments, the points are only allocated and calculated on the
90             first call to this function. Subsequent calls without arguments will
91             return the same list reference each time. Each vector is a simple
92             unblessed list reference, like C<[ $x, $y, $z ]>.
93              
94             With any argument, this function will calculate a new list afresh. This
95             takes slightly more memory and time, but some algorithms may want to
96             change the given coordinates, which would otherwise spoil future calls
97             that would return the modified list.
98              
99             If the first argument given is a code reference, then that code is called
100             once per vector, with the vector as an argument. The result of that code
101             then B the original vector in the master set used by the other
102             functions. This can be used to bless or convert the vectors with other
103             vector math packages:
104              
105             use Math::Polyhedra;
106             use Math::VectorReal;
107             my $coords = coordinates( sub { vector(@{$_[0]}) } );
108              
109             After the above code, all vectors returned by any function in this module
110             are then referring to blessed C object instances. Just
111             about any vector library would work similarly. The choice of which
112             vector module to use is arbitrary, since it's the given code reference
113             which drives the vector conversion.
114              
115             =cut
116              
117             my $Coords = undef;
118             sub coordinates
119             {
120             # We pass through any blessed reference to our own package; no-op.
121             # This allows callers to pass specific coord sets through the other
122             # functions in this module; an undocumented feature which may change.
123             # Any passed-through set is not memo-ized.
124 32 50   32 1 147 return $_[0] if UNIVERSAL::isa($_[0], __PACKAGE__);
125              
126             # If any argument is given, it revokes previous memo-ized results and
127             # we need to make a fresh set of vectors instead. We'll check what
128             # the argument type is, if any, later. If no argument, we may use
129             # any already memo-ized prepared set.
130 32 50       81 $Coords = undef if @_;
131 32 100       86 return $Coords if $Coords;
132              
133             # The Golden Ratio, often written as phi or F.
134             # The value of F is approximately 1.61803398874989.
135             # All of the polyhedra coordinates in X, Y and Z are some relation to F.
136 1         4 my $F = phi();
137 1         138 $Coords =
138             # X Y Z
139             [ undef,
140             [ 0, 0, 2*$F**2 ],
141             [ $F**2, 0, $F**3 ],
142             [ $F, $F**2, $F**3 ],
143             [ 0, $F, $F**3 ],
144             [ -$F, $F**2, $F**3 ],
145             [ -$F**2, 0, $F**3 ],
146             [ -$F, -$F**2, $F**3 ],
147             [ 0, -$F, $F**3 ],
148             [ $F, -$F**2, $F**3 ],
149             [ $F**3, $F, $F**2 ],
150             [ $F**2, $F**2, $F**2 ],
151             [ 0, $F**3, $F**2 ],
152             [ -$F**2, $F**2, $F**2 ],
153             [ -$F**3, $F, $F**2 ],
154             [ -$F**3, -$F, $F**2 ],
155             [ -$F**2, -$F**2, $F**2 ],
156             [ 0, -$F**3, $F**2 ],
157             [ $F**2, -$F**2, $F**2 ],
158             [ $F**3, -$F, $F**2 ],
159             [ $F**3, 0, $F ],
160             [ $F**2, $F**3, $F ],
161             [ -$F**2, $F**3, $F ],
162             [ -$F**3, 0, $F ],
163             [ -$F**2, -$F**3, $F ],
164             [ $F**2, -$F**3, $F ],
165             [ 2*$F**2, 0, 0 ],
166             [ $F**3, $F**2, 0 ],
167             [ $F, $F**3, 0 ],
168             [ 0, 2*$F**2, 0 ],
169             [ -$F, $F**3, 0 ],
170             [ -$F**3, $F**2, 0 ],
171             [ -2*$F**2, 0, 0 ],
172             [ -$F**3, -$F**2, 0 ],
173             [ -$F, -$F**3, 0 ],
174             [ 0, -2*$F**2, 0 ],
175             [ $F, -$F**3, 0 ],
176             [ $F**3, -$F**2, 0 ],
177             [ $F**3, 0, -$F ],
178             [ $F**2, $F**3, -$F ],
179             [ -$F**2, $F**3, -$F ],
180             [ -$F**3, 0, -$F ],
181             [ -$F**2, -$F**3, -$F ],
182             [ $F**2, -$F**3, -$F ],
183             [ $F**3, $F, -$F**2 ],
184             [ $F**2, $F**2, -$F**2 ],
185             [ 0, $F**3, -$F**2 ],
186             [ -$F**2, $F**2, -$F**2 ],
187             [ -$F**3, $F, -$F**2 ],
188             [ -$F**3, -$F, -$F**2 ],
189             [ -$F**2, -$F**2, -$F**2 ],
190             [ 0, -$F**3, -$F**2 ],
191             [ $F**2, -$F**2, -$F**2 ],
192             [ $F**3, -$F, -$F**2 ],
193             [ $F**2, 0, -$F**3 ],
194             [ $F, $F**2, -$F**3 ],
195             [ 0, $F, -$F**3 ],
196             [ -$F, $F**2, -$F**3 ],
197             [ -$F**2, 0, -$F**3 ],
198             [ -$F, -$F**2, -$F**3 ],
199             [ 0, -$F, -$F**3 ],
200             [ $F, -$F**2, -$F**3 ],
201             [ 0, 0, -2*$F**2 ] ];
202              
203             # Convert the vectors if the application supplies a method.
204 1 50 33     9 if ($_[0] and "CODE" eq ref($_[0]))
205             {
206             eval
207 0         0 {
208 0         0 my $convert = shift;
209 0         0 foreach (@$Coords)
210             {
211 0 0       0 next if not $_;
212 0         0 $_ = &$convert($_);
213             }
214             };
215 0 0       0 print "died: $@" if $@;
216             }
217              
218             # This coordinate set is blessed so we can identify it in later calls.
219 1         4 bless($Coords, __PACKAGE__);
220 1         3 return $Coords;
221             }
222              
223             #----------------------------------------------------------------------------
224              
225             # Ten different regular four-sided tetrahedra can be found.
226             #
227             my $Tetrahedra =
228             [ { Verts => [4, 34, 38, 47],
229             Edges => [[4, 34], [4, 38], [4, 47],
230             [34, 38], [34, 47], [38, 47]],
231             Faces => [[4, 47, 34], [4, 34, 38],
232             [4, 38, 47], [34, 47, 38]] },
233             { Verts => [18, 23, 28, 60],
234             Edges => [[18, 23], [18, 28], [18, 60],
235             [23, 28], [23, 60], [28, 60]],
236             Faces => [[18, 28, 23], [18, 23, 60],
237             [18, 60, 28], [23, 28, 60]] },
238             { Verts => [4, 36, 41, 45],
239             Edges => [[4, 36], [4, 41], [4, 45],
240             [36, 41], [36, 45], [41, 45]],
241             Faces => [[4, 41, 36], [4, 36, 45],
242             [4, 45, 41], [36, 41, 45]] },
243             { Verts => [16, 20, 30, 60],
244             Edges => [[16, 20], [16, 30], [16, 60],
245             [20, 30], [20, 60], [30, 60]],
246             Faces => [[16, 20, 30], [16, 60, 20],
247             [16, 30, 60], [20, 60, 30]] },
248             { Verts => [8, 28, 41, 52],
249             Edges => [[8, 28], [8, 41], [8, 52],
250             [28, 41], [28, 52], [41, 52]],
251             Faces => [[8, 28, 41], [8, 52, 28],
252             [8, 41, 52], [28, 52, 41]] },
253             { Verts => [13, 20, 34, 56],
254             Edges => [[13, 20], [13, 34], [13, 56],
255             [20, 34], [20, 56], [34, 56]],
256             Faces => [[13, 34, 20], [13, 20, 56],
257             [13, 56, 34], [20, 34, 56]] },
258             { Verts => [8, 30, 38, 50],
259             Edges => [[8, 30], [8, 38], [8, 50],
260             [30, 38], [30, 50], [38, 50]],
261             Faces => [[8, 38, 30], [8, 30, 50],
262             [8, 50, 38], [30, 38, 50]] },
263             { Verts => [11, 23, 36, 56],
264             Edges => [[11, 23], [11, 36], [11, 56],
265             [23, 36], [23, 56], [36, 56]],
266             Faces => [[11, 23, 36], [11, 56, 23],
267             [11, 36, 56], [23, 56, 36]] },
268             { Verts => [11, 16, 47, 52],
269             Edges => [[11, 16], [11, 47], [11, 52],
270             [16, 47], [16, 52], [47, 52]],
271             Faces => [[47, 16, 11], [52, 47, 11],
272             [16, 52, 11], [47, 52, 16]] },
273             { Verts => [13, 18, 45, 50],
274             Edges => [[13, 18], [13, 45], [13, 50],
275             [18, 45], [18, 50], [45, 50]],
276             Faces => [[13, 18, 45], [18, 13, 50],
277             [13, 45, 50], [18, 50, 45]] } ];
278              
279             #----------------------------------------------------------------------------
280              
281             # Five different regular six-sided sexahedra (cubes) can be found.
282             # Each square face is a pair of triangles.
283             #
284             my $Sexahedra =
285             [ { Verts => [4, 18, 23, 28, 34, 38, 47, 60],
286             Edges => [ [4, 18], [18, 38], [38, 28],
287             [28, 4], [4, 23], [18, 34],
288             [28, 47], [38, 60], [23, 34],
289             [34, 60], [60, 47], [47, 23] ],
290             Faces => [ [[4, 18, 38], [38, 28, 4]],
291             [[4, 23, 18], [18, 23, 34]],
292             [[4, 28, 47], [4, 47, 23]],
293             [[28, 38, 60], [28, 60, 47]],
294             [[23, 47, 34], [47, 60, 34]],
295             [[38, 18, 60], [18, 34, 60]] ] },
296             { Verts => [4, 16, 20, 30, 36, 41, 45, 60],
297             Edges => [ [4, 16], [16, 36], [36, 20],
298             [20, 4], [4, 30], [16, 41],
299             [20, 45], [36, 60], [30, 41],
300             [41, 60], [60, 45], [45, 30] ],
301             Faces => [ [[4, 16, 20], [16, 36, 20]],
302             [[4, 20, 45], [4, 45, 30]],
303             [[4, 30, 41], [4, 41, 16]],
304             [[45, 41, 30], [45, 60, 41]],
305             [[20, 36, 60], [20, 60, 45]],
306             [[36, 16, 41], [36, 41, 60]] ] },
307             { Verts => [8, 13, 20, 28, 34, 41, 52, 56],
308             Edges => [ [8, 13], [13, 28], [28, 20],
309             [20, 8], [8, 34], [13, 41],
310             [28, 56], [20, 52], [34, 41],
311             [41, 56], [56, 52], [52, 34] ],
312             Faces => [ [[8, 20, 13], [13, 20, 28]],
313             [[8, 52, 20], [8, 34, 52]],
314             [[8, 41, 34], [8, 13, 41]],
315             [[20, 56, 28], [20, 52, 56]],
316             [[52, 41, 56], [52, 34, 41]],
317             [[28, 41, 13], [28, 56, 41]] ] },
318             { Verts => [8, 11, 23, 30, 36, 38, 50, 56],
319             Edges => [ [8, 11], [11, 30], [30, 23],
320             [23, 8], [8, 36], [11, 38],
321             [23, 50], [30, 56], [36, 38],
322             [38, 56], [56, 50], [50, 36] ],
323             Faces => [ [[8, 11, 23], [11, 30, 23]],
324             [[8, 23, 50], [8, 50, 36]],
325             [[8, 36, 38], [8, 38, 11]],
326             [[23, 30, 56], [23, 56, 50]],
327             [[50, 56, 38], [50, 38, 36]],
328             [[30, 11, 56], [11, 38, 56]] ] },
329             { Verts => [11, 13, 16, 18, 45, 47, 50, 52],
330             Edges => [ [11, 13], [13, 16], [16, 18],
331             [18, 11], [11, 45], [13, 47],
332             [16, 50], [18, 52], [45, 47],
333             [47, 50], [50, 52], [52, 45] ],
334             Faces => [ [[11, 13, 16], [11, 16, 18]],
335             [[11, 45, 47], [11, 47, 13]],
336             [[13, 47, 50], [13, 50, 16]],
337             [[16, 50, 52], [16, 52, 18]],
338             [[18, 52, 45], [18, 45, 11]],
339             [[45, 52, 50], [45, 50, 47]] ] } ];
340              
341             #----------------------------------------------------------------------------
342              
343             # Five different regular eight-sided octahedra can be found.
344             #
345             my $Octahedra =
346             [ { Verts => [7, 10, 22, 43, 49, 55],
347             Edges => [ [7, 10], [7, 22], [7, 43],
348             [7, 49], [10, 22], [10, 43],
349             [22, 49], [43, 49], [10, 55],
350             [22, 55], [43, 55], [49, 55] ],
351             Faces => [ [7, 43, 10], [7, 10, 22],
352             [7, 49, 43], [7, 22, 49],
353             [55, 10, 43], [55, 22, 10],
354             [55, 43, 49], [55, 49, 22] ] },
355             { Verts => [9, 14, 21, 42, 53, 57],
356             Edges => [ [9, 14], [9, 21], [9, 42],
357             [9, 53], [14, 21], [14, 42],
358             [21, 53], [42, 53], [14, 57],
359             [21, 57], [42, 57], [53, 57] ],
360             Faces => [ [9, 21, 14], [9, 53, 21],
361             [9, 14, 42], [9, 42, 53],
362             [57, 14, 21], [57, 21, 53],
363             [57, 42, 14], [57, 53, 42] ] },
364             { Verts => [3, 15, 25, 40, 44, 59],
365             Edges => [ [3, 15], [3, 25], [3, 40],
366             [3, 44], [15, 25], [15, 40],
367             [40, 44], [25, 44], [25, 59],
368             [15, 59], [40, 59], [44, 59] ],
369             Faces => [ [3, 15, 25], [3, 25, 44],
370             [3, 40, 15], [3, 44, 40],
371             [59, 25, 15], [59, 15, 40],
372             [59, 40, 44], [59, 44, 25] ] },
373             { Verts => [5, 19, 24, 39, 48, 61],
374             Edges => [ [5, 19], [5, 24], [5, 39],
375             [5, 48], [19, 24], [19, 39],
376             [24, 48], [39, 48], [19, 61],
377             [24, 61], [39, 61], [48, 61] ],
378             Faces => [ [5, 19, 39], [5, 24, 19],
379             [5, 39, 48], [5, 48, 24],
380             [61, 39, 19], [61, 19, 24],
381             [61, 48, 39], [61, 24, 48] ] },
382             { Verts => [1, 26, 29, 32, 35, 62],
383             Edges => [ [1, 26], [1, 29], [1, 32],
384             [1, 35], [26, 29], [29, 32],
385             [32, 35], [35, 26], [62, 26],
386             [62, 29], [62, 32], [62, 35] ],
387             Faces => [ [1, 26, 29], [1, 29, 32],
388             [1, 32, 35], [1, 35, 26],
389             [62, 29, 26], [62, 32, 29],
390             [62, 35, 32], [62, 26, 35] ] } ];
391              
392             #----------------------------------------------------------------------------
393              
394             # Five different rhombic twelve-sided dodecahedra can be found.
395             # Each rhomboid face is a pair of triangles.
396             #
397             my $RhombicDodecahedra =
398             [ { Verts => [4, 7, 10, 18, 22, 23, 28, 34, 38, 43, 47, 49, 55, 60],
399             Edges => [ [7, 4], [7, 18], [7, 23],
400             [7, 34], [10, 4], [10, 18],
401             [10, 28], [10, 38], [22, 4],
402             [22, 23], [22, 28], [22, 47],
403             [43, 18], [43, 34], [43, 38],
404             [43, 60], [49, 23], [49, 34],
405             [49, 47], [49, 60], [55, 28],
406             [55, 38], [55, 47], [55, 60] ],
407             Faces => [ [[4, 7, 18], [10, 4, 18]],
408             [[7, 34, 18], [43, 18, 34]],
409             [[7, 23, 34], [49, 34, 23]],
410             [[7, 4, 23], [4, 22, 23]],
411             [[22, 4, 28], [4, 10, 28]],
412             [[10, 18, 43], [38, 10, 43]],
413             [[34, 49, 43], [49, 60, 43]],
414             [[23, 22, 49], [47, 49, 22]],
415             [[55, 38, 60], [38, 43, 60]],
416             [[55, 60, 47], [49, 47, 60]],
417             [[55, 47, 28], [22, 28, 47]],
418             [[55, 28, 38], [28, 10, 38]] ] },
419             { Verts => [4, 9, 14, 16, 20, 21, 30, 36, 41, 42, 45, 53, 57, 60],
420             Edges => [ [9, 4], [9, 16], [9, 20],
421             [9, 36], [14, 4], [14, 16],
422             [14, 30], [14, 41], [21, 4],
423             [21, 20], [21, 30], [21, 45],
424             [42, 16], [42, 36], [42, 41],
425             [42, 60], [53, 20], [53, 36],
426             [53, 45], [53, 60], [57, 30],
427             [57, 41], [57, 45], [57, 60] ],
428             Faces => [ [[9, 4, 16], [4, 14, 16]],
429             [[9, 16, 36], [16, 42, 36]],
430             [[9, 36, 20], [53, 20, 36]],
431             [[9, 20, 4], [21, 4, 20]],
432             [[14, 4, 30], [21, 30, 4]],
433             [[16, 14, 42], [41, 42, 14]],
434             [[36, 42, 53], [60, 53, 42]],
435             [[20, 53, 21], [45, 21, 53]],
436             [[42, 41, 60], [57, 60, 41]],
437             [[53, 60, 45], [57, 45, 60]],
438             [[21, 45, 30], [57, 30, 45]],
439             [[14, 30, 41], [57, 41, 30]] ] },
440             { Verts => [3, 8, 13, 15, 20, 25, 28, 34, 40, 41, 44, 52, 56, 59],
441             Edges => [ [3, 8], [3, 13], [3, 20],
442             [3, 28], [15, 8], [15, 13],
443             [15, 34], [15, 41], [25, 8],
444             [25, 20], [25, 34], [25, 52],
445             [40, 13], [40, 28], [40, 41],
446             [40, 56], [44, 20], [44, 28],
447             [44, 52], [44, 56], [59, 34],
448             [59, 41], [59, 52], [59, 56] ],
449             Faces => [ [[3, 13, 8], [15, 8, 13]],
450             [[3, 28, 13], [40, 13, 28]],
451             [[3, 20, 28], [44, 28, 20]],
452             [[3, 8, 20], [25, 20, 8]],
453             [[8, 15, 25], [34, 25, 15]],
454             [[13, 40, 15], [41, 15, 40]],
455             [[28, 44, 40], [56, 40, 44]],
456             [[20, 25, 44], [52, 44, 25]],
457             [[15, 41, 34], [59, 34, 41]],
458             [[40, 56, 41], [59, 41, 56]],
459             [[44, 52, 56], [59, 56, 52]],
460             [[25, 34, 52], [59, 52, 34]] ] },
461             { Verts => [5, 8, 11, 19, 23, 24, 30, 36, 38, 39, 48, 50, 56, 61],
462             Edges => [ [5, 8], [5, 11], [5, 23],
463             [5, 30], [19, 8], [19, 11],
464             [19, 36], [19, 38], [24, 8],
465             [24, 23], [24, 36], [24, 50],
466             [39, 11], [39, 30], [39, 38],
467             [39, 56], [48, 23], [48, 30],
468             [48, 50], [48, 56], [61, 36],
469             [61, 38], [61, 50], [61, 56] ],
470             Faces => [ [[5, 8, 11], [8, 19, 11]],
471             [[5, 11, 30], [11, 39, 30]],
472             [[5, 30, 23], [30, 48, 23]],
473             [[5, 23, 8], [23, 24, 8]],
474             [[8, 24, 19], [36, 19, 24]],
475             [[11, 19, 39], [19, 38, 39]],
476             [[30, 39, 48], [39, 56, 48]],
477             [[23, 48, 24], [48, 50, 24]],
478             [[19, 36, 38], [61, 38, 36]],
479             [[39, 38, 56], [61, 56, 38]],
480             [[48, 56, 50], [61, 50, 56]],
481             [[24, 50, 36], [61, 36, 50]] ] },
482             { Verts => [1, 11, 13, 16, 18, 26, 29, 32, 35, 45, 47, 50, 52, 62],
483             Edges => [ [1, 11], [1, 13], [1, 16],
484             [1, 18], [26, 18], [26, 11],
485             [26, 45], [26, 52], [29, 11],
486             [29, 13], [29, 47], [29, 45],
487             [32, 13], [32, 16], [32, 50],
488             [32, 47], [35, 16], [35, 18],
489             [35, 52], [35, 50], [62, 45],
490             [62, 47], [62, 50], [62, 52] ],
491             Faces => [ [[1, 11, 29], [29, 13, 1]],
492             [[1, 13, 32], [32, 16, 1]],
493             [[1, 16, 35], [35, 18, 1]],
494             [[1, 18, 26], [26, 11, 1]],
495             [[26, 45, 29], [29, 11, 26]],
496             [[29, 47, 32], [32, 13, 29]],
497             [[32, 50, 35], [35, 16, 32]],
498             [[35, 52, 26], [26, 18, 35]],
499             [[62, 45, 26], [26, 52, 62]],
500             [[62, 47, 29], [29, 45, 62]],
501             [[62, 50, 32], [32, 47, 62]],
502             [[62, 52, 35], [35, 50, 62]] ] } ];
503              
504             #----------------------------------------------------------------------------
505              
506             # One regular twelve-sided dodecahedron can be found.
507             # Each regular pentagonal face is a triplet of triangles.
508             #
509             my $Dodecahedron =
510             [ { Verts => [ 4, 8, 11, 13, 16, 18, 20, 23, 28, 30,
511             34, 36, 38, 41, 45, 47, 50, 52, 56, 60 ],
512             Edges => [ [4, 8], [4, 11], [4, 13],
513             [8, 16], [8, 18], [11, 20],
514             [11, 28], [13, 30], [13, 23],
515             [16, 23], [16, 34], [18, 36],
516             [18, 20], [20, 38], [23, 41],
517             [28, 30], [28, 45], [30, 47],
518             [34, 50], [34, 36], [36, 52],
519             [38, 45], [38, 52], [41, 47],
520             [41, 50], [45, 56], [47, 56],
521             [50, 60], [52, 60], [56, 60] ],
522             Faces => [ [[4, 8, 11], [11, 8, 18], [11, 18, 20]],
523             [[4, 13, 23], [4, 23, 8], [8, 23, 16]],
524             [[4, 11, 28], [4, 28, 30], [4, 30, 13]],
525             [[8, 16, 34], [8, 34, 18], [18, 34, 36]],
526             [[11, 20, 28], [20, 45, 28], [20, 38, 45]],
527             [[13, 30, 23], [23, 30, 41], [41, 30, 47]],
528             [[16, 23, 34], [34, 23, 50], [50, 23, 41]],
529             [[18, 36, 52], [18, 52, 38], [18, 38, 20]],
530             [[28, 45, 56], [28, 56, 47], [28, 47, 30]],
531             [[34, 50, 60], [34, 60, 36], [36, 60, 52]],
532             [[38, 52, 60], [38, 60, 56], [38, 56, 45]],
533             [[41, 47, 56], [41, 56, 60], [41, 60, 50]] ] } ];
534              
535             #----------------------------------------------------------------------------
536              
537             # One regular twenty-sided icosahedron can be found.
538             #
539             my $Icosahedron =
540             [ { Verts => [2, 6, 12, 17, 27, 31, 33, 37, 46, 51, 54, 58],
541             Edges => [ [2, 6], [2, 12], [2, 17],
542             [2, 37], [2, 27], [6, 12],
543             [6, 17], [6, 31], [6, 33],
544             [12, 27], [12, 46], [12, 31],
545             [17, 33], [17, 51], [17, 37],
546             [27, 37], [27, 54], [27, 46],
547             [31, 46], [31, 58], [31, 33],
548             [33, 58], [33, 51], [37, 51],
549             [37, 54], [46, 54], [46, 58],
550             [51, 54], [51, 58], [54, 58] ],
551             Faces => [ [2, 6, 17], [2, 12, 6], [2, 17, 37],
552             [2, 37, 27], [2, 27, 12], [37, 54, 27],
553             [27, 54, 46], [27, 46, 12], [12, 46, 31],
554             [12, 31, 6], [6, 31, 33], [6, 33, 17],
555             [17, 33, 51], [17, 51, 37], [37, 51, 54],
556             [58, 54, 51], [58, 46, 54], [58, 31, 46],
557             [58, 33, 31], [58, 51, 33] ] } ];
558              
559             #----------------------------------------------------------------------------
560              
561             # One rhombic thirty-sided triacontahedron can be found.
562             # Each rhomboid face is a pair of triangles.
563             #
564             my $RhombicTriacontahedron =
565             [ { Verts => [ 2, 4, 6, 8, 11, 12, 13, 16,
566             17, 18, 20, 23, 27, 28, 30, 31,
567             33, 34, 36, 37, 38, 41, 45, 46,
568             47, 50, 51, 52, 54, 56, 58, 60 ],
569             Edges => [ [2, 4], [4, 6], [6, 8],
570             [8, 2], [2, 11], [11, 12],
571             [4, 12], [12, 13], [13, 6],
572             [6, 23], [6, 16], [16, 17],
573             [17, 8], [17, 18], [2, 18],
574             [2, 20], [20, 27], [27, 28],
575             [12, 28], [12, 30], [13, 31],
576             [23, 31], [23, 33], [33, 16],
577             [18, 37], [37, 20], [11, 27],
578             [54, 56], [56, 58], [58, 60],
579             [60, 54], [54, 45], [45, 46],
580             [46, 56], [58, 47], [58, 41],
581             [58, 50], [60, 51], [52, 54],
582             [54, 38], [38, 27], [27, 45],
583             [46, 47], [47, 31], [31, 41],
584             [41, 33], [33, 50], [50, 51],
585             [51, 52], [52, 37], [37, 38],
586             [28, 46], [30, 46], [30, 31],
587             [17, 36], [36, 51], [51, 34],
588             [34, 17], [36, 37], [33, 34] ],
589             Faces => [ [[2, 4, 6], [6, 8, 2]],
590             [[2, 11, 4], [4, 11, 12]],
591             [[4, 12, 13], [4, 13, 6]],
592             [[6, 16, 8], [8, 16, 17]],
593             [[8, 17, 18], [8, 18, 2]],
594             [[2, 18, 37], [2, 37, 20]],
595             [[2, 20, 27], [2, 27, 11]],
596             [[11, 27, 28], [11, 28, 12]],
597             [[6, 13, 31], [6, 31, 23]],
598             [[6, 23, 33], [6, 33, 16]],
599             [[54, 60, 58], [58, 56, 54]],
600             [[54, 56, 45], [45, 56, 46]],
601             [[56, 58, 47], [47, 46, 56]],
602             [[47, 58, 41], [41, 31, 47]],
603             [[58, 50, 33], [33, 41, 58]],
604             [[58, 60, 51], [51, 50, 58]],
605             [[60, 54, 52], [52, 51, 60]],
606             [[54, 38, 37], [37, 52, 54]],
607             [[45, 27, 38], [38, 54, 45]],
608             [[20, 37, 38], [38, 27, 20]],
609             [[23, 31, 41], [41, 33, 23]],
610             [[12, 28, 46], [46, 30, 12]],
611             [[12, 30, 31], [31, 13, 12]],
612             [[31, 30, 46], [46, 47, 31]],
613             [[28, 27, 45], [45, 46, 28]],
614             [[17, 34, 51], [51, 36, 17]],
615             [[18, 17, 36], [36, 37, 18]],
616             [[37, 36, 51], [51, 52, 37]],
617             [[17, 16, 33], [33, 34, 17]],
618             [[34, 33, 50], [50, 51, 34]] ] } ];
619              
620             # All of the points form a 120-sided figure.
621             # What would that be called? I'm guessing it's a hexicosahedron (6*20).
622             # Each rhomboid face is a pair of triangles.
623             #
624             my $Hexicosahedron =
625             [ { Verts => [ 1, 2, 3, 4, 5, 6, 7, 8, 9,
626             10, 11, 12, 13, 14, 15, 16, 17, 18,
627             19, 20, 21, 22, 23, 24, 25, 26, 27,
628             28, 29, 30, 31, 32, 33, 34, 35, 36,
629             37, 38, 39, 40, 41, 42, 43, 44, 45,
630             46, 47, 48, 49, 50, 51, 52, 53, 54,
631             55, 56, 57, 58, 59, 60, 61, 62 ],
632             Edges => [ [1, 2], [1, 4], [1, 6],
633             [1, 8], [2, 3], [2, 4],
634             [2, 8], [2, 9], [2, 10],
635             [2, 11], [2, 18], [2, 19],
636             [2, 20], [3, 4], [3, 11],
637             [3, 12], [4, 5], [4, 6],
638             [4, 12], [5, 6], [5, 12],
639             [5, 13], [6, 7], [6, 8],
640             [6, 13], [6, 14], [6, 15],
641             [6, 16], [6, 23], [7, 8],
642             [7, 16], [7, 17], [8, 9],
643             [8, 17], [9, 17], [9, 18],
644             [10, 11], [10, 20], [10, 27],
645             [11, 12], [11, 21], [11, 27],
646             [12, 13], [12, 21], [12, 28],
647             [12, 29], [12, 22], [12, 30],
648             [13, 14], [13, 22], [13, 31],
649             [14, 23], [14, 31], [15, 16],
650             [15, 23], [15, 33], [16, 17],
651             [16, 24], [16, 33], [17, 18],
652             [17, 24], [17, 25], [17, 34],
653             [17, 35], [17, 36], [18, 19],
654             [18, 25], [18, 37], [19, 20],
655             [19, 37], [20, 26], [20, 27],
656             [20, 37], [21, 27], [21, 28],
657             [22, 30], [22, 31], [23, 31],
658             [23, 32], [23, 33], [24, 33],
659             [24, 34], [25, 36], [25, 37],
660             [26, 27], [26, 37], [26, 38],
661             [27, 28], [27, 38], [27, 39],
662             [27, 44], [27, 45], [28, 29],
663             [28, 39], [28, 46], [29, 30],
664             [29, 46], [30, 31], [30, 40],
665             [30, 46], [31, 32], [31, 40],
666             [31, 41], [31, 47], [31, 48],
667             [32, 33], [32, 41], [33, 34],
668             [33, 41], [33, 42], [33, 49],
669             [33, 50], [34, 35], [34, 42],
670             [34, 51], [35, 36], [35, 51],
671             [36, 37], [36, 43], [36, 51],
672             [37, 38], [37, 43], [37, 52],
673             [37, 53], [38, 44], [38, 53],
674             [38, 54], [39, 45], [39, 46],
675             [40, 46], [40, 47], [41, 48],
676             [41, 49], [41, 58], [42, 50],
677             [42, 51], [43, 51], [43, 52],
678             [44, 45], [44, 54], [45, 55],
679             [45, 46], [46, 47], [46, 55],
680             [46, 56], [46, 57], [47, 48],
681             [47, 57], [47, 58], [48, 58],
682             [49, 50], [49, 58], [50, 51],
683             [50, 58], [50, 59], [51, 52],
684             [51, 59], [51, 60], [51, 61],
685             [52, 53], [52, 61], [52, 54],
686             [53, 54], [54, 55], [54, 56],
687             [54, 60], [54, 61], [54, 62],
688             [55, 56], [56, 57], [56, 58],
689             [56, 62], [57, 58], [58, 59],
690             [58, 60], [58, 62], [59, 60],
691             [60, 61], [60, 62] ],
692             Faces => [ [1, 2, 4], [2, 3, 4], [2, 20, 10],
693             [2, 10, 11], [2, 11, 3], [3, 11, 12],
694             [3, 12, 4], [20, 26, 27], [20, 27, 10],
695             [10, 27, 11], [11, 27, 21], [11, 21, 12],
696             [21, 27, 28], [12, 21, 28], [12, 28, 29],
697             [1, 4, 6], [4, 12, 5], [4, 5, 6],
698             [5, 12, 13], [5, 13, 6], [6, 13, 14],
699             [6, 14, 23], [12, 29, 30], [12, 30, 22],
700             [12, 22, 13], [13, 22, 31], [22, 30, 31],
701             [13, 31, 14], [14, 31, 23], [23, 31, 32],
702             [1, 6, 8], [6, 23, 15], [6, 15, 16],
703             [6, 16, 7], [6, 7, 8], [8, 7, 17],
704             [7, 16, 17], [23, 32, 33], [15, 23, 33],
705             [16, 15, 33], [24, 16, 33], [34, 24, 33],
706             [17, 16, 24], [17, 24, 34], [17, 34, 35],
707             [1, 8, 2], [8, 17, 9], [8, 9, 2],
708             [9, 17, 18], [9, 18, 2], [2, 18, 19],
709             [2, 19, 20], [17, 35, 36], [17, 36, 25],
710             [17, 25, 18], [18, 25, 37], [25, 36, 37],
711             [19, 18, 37], [20, 19, 37], [20, 37, 26],
712             [27, 26, 38], [27, 38, 44], [27, 44, 45],
713             [27, 45, 39], [27, 39, 28], [28, 39, 46],
714             [28, 46, 29], [39, 45, 46], [38, 54, 44],
715             [55, 45, 54], [45, 44, 54], [45, 55, 46],
716             [46, 55, 56], [55, 54, 56], [56, 54, 62],
717             [30, 29, 46], [30, 46, 40], [31, 30, 40],
718             [40, 46, 47], [31, 40, 47], [31, 47, 48],
719             [31, 48, 41], [31, 41, 32], [46, 56, 57],
720             [47, 46, 57], [47, 57, 58], [48, 47, 58],
721             [41, 48, 58], [57, 56, 58], [58, 56, 62],
722             [33, 32, 41], [33, 41, 49], [33, 49, 50],
723             [33, 50, 42], [33, 42, 34], [34, 42, 51],
724             [42, 50, 51], [35, 34, 51], [49, 41, 58],
725             [50, 49, 58], [50, 58, 59], [51, 50, 59],
726             [51, 59, 60], [59, 58, 60], [60, 58, 62],
727             [36, 35, 51], [36, 51, 43], [37, 36, 43],
728             [43, 51, 52], [37, 43, 52], [37, 52, 53],
729             [37, 53, 38], [37, 38, 26], [51, 60, 61],
730             [52, 51, 61], [52, 61, 54], [53, 52, 54],
731             [38, 53, 54], [54, 61, 60], [54, 60, 62] ] } ];
732              
733             #----------------------------------------------------------------------------
734              
735             # A map from sides or name to the right structure.
736             #
737             my $Tris =
738             {
739             120 => $Hexicosahedron, 'hexicosa' => $Hexicosahedron,
740             20 => $Icosahedron, 'icosa' => $Icosahedron,
741             12 => $Dodecahedron, 'dodeca' => $Dodecahedron,
742             8 => $Octahedra, 'octa' => $Octahedra,
743             6 => $Sexahedra, 'cube' => $Sexahedra,
744             'hexa' => $Sexahedra,
745             'sexa' => $Sexahedra,
746             4 => $Tetrahedra, 'tetra' => $Tetrahedra,
747             };
748              
749             my $Rhombics =
750             {
751             -30 => $RhombicTriacontahedron, 'triaconta' => $RhombicTriacontahedron,
752             -12 => $RhombicDodecahedra, 'dodeca' => $RhombicDodecahedra,
753             -6 => $Sexahedra, 'cube' => $Sexahedra,
754             'hexa' => $Sexahedra,
755             'sexa' => $Sexahedra,
756             };
757              
758             #----------------------------------------------------------------------------
759              
760             =head2 polyhedra(), polyhedron()
761              
762             Retrieves a reference to a polyhedron structure by its name or the number
763             of sides. This is a read-only structure which defines all of the vertex,
764             edge and face information for the given polyhedron figure.
765              
766             The argument should either be a number, or a name. For example,
767             C and C and C
768             hexahedron')> are all equivalent methods to retrieve the structure
769             representing regular six-sided cubical figures.
770              
771             my $cube = polyhedron(6); # regular six-sided figure
772             my $ico = polyhedron(20); # icosahedron has 20 triangular faces
773             my $rhombdod = polyhedron($_ = 'rhombic dodecahedron');
774             print 'Found ', scalar @$ico, ' variations of ', $_, $/;
775              
776             The values inside the reference should not be modified as they are not
777             recalculated on each subsequent call. The scalar reference is intended
778             to be given as an argument to the C, C, C,
779             or C functions in this module.
780              
781             The set of known names and their faces are as follows:
782              
783             =over 4
784              
785             =item *
786              
787             tetrahedron (4 triangular faces) [10 variations]
788              
789             =item *
790              
791             cube, hexahedron (6 square faces) (+6) (-6) [5 variations]
792              
793             =item *
794              
795             octahedron (8 triangular faces) [5 variations]
796              
797             =item *
798              
799             dodecahedron (12 pentagonic faces)
800              
801             =item *
802              
803             rhombic dodecahedron (12 diamond faces) (-12) [5 variations]
804              
805             =item *
806              
807             icosahedron (20 triangular faces)
808              
809             =item *
810              
811             rhombic triacontahedron (30 diamond faces) (-30)
812              
813             =item *
814              
815             hexicosahedron (120 irregular triangular faces)
816              
817             =back
818              
819             Other polyhedra such as the decahedron, a common rhombic ten-sided
820             figure, cannot be constructed solely using the value of I, and are
821             not currently supported by this module. The name 'hexicosahedron' may be
822             apocryphal, since it's not a regular shape with 120 regular sides, but is
823             instead comprised of various unions of the other volumes presented.
824              
825             For a given polyhedron structure, more than one variation of the
826             structure may be known. There are ten different C<'tetrahedra'> defined
827             by the 62 I point library, for example. The expression C<(scalar
828             @$hedron)> for a given structure will return how many variations are
829             defined, and C<< ($hedron->[3]) >> will select that specific variety for the
830             other functions. These variations are not different in shape, but merely
831             in differing orientations relative to the origin. Most applications
832             don't need this information, but some studies of the S may
833             select from these variations.
834              
835             The C function is just a convenient alias for the preferred
836             name, C.
837              
838             =cut
839              
840 0     0 1 0 sub polyhedra { polyhedron(@_) }
841              
842             sub polyhedron
843             {
844 8     8 1 8319 my $sides = shift;
845 8         28 $sides = lc($sides);
846 8         54 $sides =~ s/hedron|hedra//;
847 8 100 66     104 my $set = (($sides =~ s/rhomb//) || (0 > $sides))? $Rhombics : $Tris;
848 8 50       27 return undef if not $set;
849 8 100       57 return $set->{$sides} if $set->{$sides};
850 2 100       11 for (keys %$set) { return $set->{$_} if $sides =~ m/$_/ }
  9         101  
851 0         0 return undef;
852             }
853              
854             =head2 vertices()
855              
856             my $verts = vertices($cube);
857             while (@$verts)
858             { draw_dot(shift @$verts); }
859              
860             Returns a list reference, containing one vector for each vertex in the
861             polyhedron. Each vertex is itself a list reference of real coordinate
862             values, such as this C< [ $x, $y, $z ] > triple.
863              
864             =cut
865              
866             sub vertices
867             {
868 8     8 1 6599 my $hedron = shift;
869 8 50       37 $hedron = $hedron->[0] if "ARRAY" eq ref $hedron;
870 8         21 my $coords = coordinates(@_);
871 8         13 my @v = @{$hedron->{Verts}};
  8         55  
872 8         17 @v = map { $coords->[$_] } @v;
  158         254  
873 8         35 return \@v;
874             }
875              
876             =head2 edges()
877              
878             my $edges = edges($cube);
879             while (@$edges)
880             { move_to(shift @$edges);
881             draw_to(shift @$edges);
882             }
883              
884             Returns a list reference, which contains two vectors defining each edge
885             of the polyhedra. Each pair is an independent edge; the first edge is
886             the vectors indexed 0 and 1, then index 2 to index 3, and so on. (This
887             is not an optimized "line strip.") The vector references will be
888             repeated as required.
889              
890             =cut
891              
892             sub edges
893             {
894 8     8 1 9968 my $hedron = shift;
895 8 50       39 $hedron = $hedron->[0] if "ARRAY" eq ref $hedron;
896 8         20 my $coords = coordinates(@_);
897 8         13 my @e = @{$hedron->{Edges}};
  8         100  
898 8         20 @e = map { $coords->[$_->[0]], $coords->[$_->[1]] } @e;
  353         725  
899 8         63 return \@e;
900             }
901              
902             =head2 faces()
903              
904             Returns a list reference, which contains sublists of at least three
905             coplanar vectors defining each face. Each list is an independent
906             face. The vector references will be repeated as required.
907              
908             The vertices in each face are not sorted or ordered around the
909             circumference of the face. Typically, graphics programs should use the
910             C or C for ordering information.
911              
912             =cut
913              
914             sub faces
915             {
916 8     8 1 3406 my $hedron = shift;
917 8 50       39 $hedron = $hedron->[0] if "ARRAY" eq ref $hedron;
918 8         25 my $coords = coordinates(@_);
919              
920 8         19 my @e = @{$hedron->{Faces}};
  8         78  
921              
922             # multiple tris per face
923             # @e = ( [ [ v v v ] [ v v v ] ]
924             # [ [ v v v ] [ v v v ] ] ... )
925 8 100 66     69 if (ref($e[0]) and
926             ref($e[0][0]))
927             {
928 4         8 my @ee = ();
929 4         9 foreach my $f (@e)
930             {
931 60         78 my %u = ();
932 60         84 foreach my $t (@$f)
933 132         662 { @u{@$t} = (@$t); }
934 60         117 push(@ee, [ map { $coords->[$_] } values %u ]);
  252         520  
935             }
936 4         19 return \@ee;
937             }
938              
939             # single tris per face
940             # @e = ( [ v v v ]
941             # [ v v v ] ... )
942             #
943 4         10 @e = map { [ $coords->[$_->[0]],
  152         415  
944             $coords->[$_->[1]],
945             $coords->[$_->[2]] ] } @e;
946              
947 4         20 return \@e;
948             }
949              
950             =head2 tris()
951              
952             my $tris = tris($cube);
953             while (@$tris)
954             { first_vertex(shift @$tris);
955             second_vertex(shift @$tris),
956             third_vertex(shift @$tris);
957             }
958              
959             Returns a list reference, which contains three [ X, Y, Z ] coordinate
960             triples for each triangle in sublists. Each triple is an independent
961             triangle; the first triangle is between vectors 0, 1, 2, then another
962             triangle between vectors 3, 4, 5, and so on. (This is not an optimized
963             "triangle strip.") The vector references will be repeated as required.
964              
965             All of the triangles returned are defined in clockwise order, such that
966             the implied normals (B-A)x(C-A) are facing "outward," away from the
967             origin. Many graphics applications require consistent definition to
968             calculate proper outward-facing normals for lighting or culling.
969              
970             =cut
971              
972             sub tris
973             {
974 8     8 1 6311 my $hedron = shift;
975 8 50       41 $hedron = $hedron->[0] if "ARRAY" eq ref $hedron;
976 8         19 my $coords = coordinates(@_);
977 8         16 my @e = @{$hedron->{Faces}};
  8         59  
978              
979             # multiple tris per face
980             # @e = ( [ [ v v v ] [ v v v ] ]
981             # [ [ v v v ] [ v v v ] ] ... )
982 8 100 66     67 if (ref($e[0]) and
983             ref($e[0][0]))
984             {
985 4         9 @e = map { @$_ } @e;
  60         122  
986             }
987              
988             # single tris per face
989             # @e = ( [ v v v ]
990             # [ v v v ] ... )
991             #
992 8         64 @e = map { $coords->[$_->[0]],
  284         688  
993             $coords->[$_->[1]],
994             $coords->[$_->[2]] } @e;
995              
996 8         73 return \@e;
997             }
998              
999             1;
1000             __END__