From http://www.jwz.org/xscreensaver/xscreensaver-5.15.tar.gz
[xscreensaver] / hacks / glx / polyhedra.c
1 /*****************************************************************************
2  * #ident "Id: main.c,v 3.27 2002-01-06 16:23:01+02 rl Exp "
3  * kaleido
4  *
5  *      Kaleidoscopic construction of uniform polyhedra
6  *      Copyright (c) 1991-2002 Dr. Zvi Har'El <rl@math.technion.ac.il>
7  *
8  *      Redistribution and use in source and binary forms, with or without
9  *      modification, are permitted provided that the following conditions
10  *      are met:
11  *
12  *      1. Redistributions of source code must retain the above copyright
13  *         notice, this list of conditions and the following disclaimer.
14  *
15  *      2. Redistributions in binary form must reproduce the above copyright
16  *         notice, this list of conditions and the following disclaimer in
17  *         the documentation and/or other materials provided with the
18  *         distribution.
19  *
20  *      3. The end-user documentation included with the redistribution,
21  *         if any, must include the following acknowledgment:
22  *              "This product includes software developed by
23  *               Dr. Zvi Har'El (http://www.math.technion.ac.il/~rl/)."
24  *         Alternately, this acknowledgment may appear in the software itself,
25  *         if and wherever such third-party acknowledgments normally appear.
26  *
27  *      This software is provided 'as-is', without any express or implied
28  *      warranty.  In no event will the author be held liable for any
29  *      damages arising from the use of this software.
30  *
31  *      Author:
32  *              Dr. Zvi Har'El,
33  *              Deptartment of Mathematics,
34  *              Technion, Israel Institue of Technology,
35  *              Haifa 32000, Israel.
36  *              E-Mail: rl@math.technion.ac.il
37  *
38  *      ftp://ftp.math.technion.ac.il/kaleido/
39  *      http://www.mathconsult.ch/showroom/unipoly/
40  *
41  *      Adapted for xscreensaver by Jamie Zawinski <jwz@jwz.org> 25-Apr-2004
42  *
43  *****************************************************************************
44  */
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <math.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <string.h>
54 #include <stdlib.h>
55 #include <errno.h>
56
57 #include "polyhedra.h"
58
59 extern const char *progname;
60
61 #ifndef MAXLONG
62 #define MAXLONG 0x7FFFFFFF
63 #endif
64 #ifndef MAXDIGITS
65 #define MAXDIGITS 10 /* (int)log10((double)MAXLONG) + 1 */
66 #endif
67
68 #ifndef DBL_EPSILON
69 #define DBL_EPSILON 2.2204460492503131e-16
70 #endif
71 #define BIG_EPSILON 3e-2
72 #define AZ M_PI/7  /* axis azimuth */
73 #define EL M_PI/17 /* axis elevation */
74
75 #define Err(x) do {\
76         fprintf (stderr, "%s: %s\n", progname, (x)); \
77         exit (1); \
78     } while(0)
79
80 #define Free(lvalue) do {\
81         if (lvalue) {\
82             free((char*) lvalue);\
83             lvalue=0;\
84         }\
85     } while(0)
86
87 #define Matfree(lvalue,n) do {\
88         if (lvalue) \
89             matfree((char*) lvalue, n);\
90         lvalue=0;\
91     } while(0)
92
93 #define Malloc(lvalue,n,type) do {\
94         if (!(lvalue = (type*) calloc((n), sizeof(type)))) \
95             abort();\
96     } while(0)
97
98 #define Realloc(lvalue,n,type) do {\
99         if (!(lvalue = (type*) realloc(lvalue, (n) * sizeof(type)))) \
100             abort();\
101     } while(0)
102
103 #define Calloc(lvalue,n,type) do {\
104         if (!(lvalue = (type*) calloc(n, sizeof(type))))\
105             abort();\
106     } while(0)
107
108 #define Matalloc(lvalue,n,m,type) do {\
109         if (!(lvalue = (type**) matalloc(n, (m) * sizeof(type))))\
110             abort();\
111     } while(0)
112
113 #define Sprintfrac(lvalue,x) do {\
114         if (!(lvalue=sprintfrac(x)))\
115             return 0;\
116     } while(0)
117
118 #define numerator(x) (frac(x), frax.n)
119 #define denominator(x) (frac(x), frax.d)
120 #define compl(x) (frac(x), (double) frax.n / (frax.n-frax.d))
121
122 typedef struct {
123     double x, y, z;
124 } Vector;
125
126 typedef struct {
127   /* NOTE: some of the int's can be replaced by short's, char's,
128      or even bit fields, at the expense of readability!!!*/
129   int index; /* index to the standard list, the array uniform[] */
130   int N; /* number of faces types (atmost 5)*/
131   int M; /* vertex valency  (may be big for dihedral polyhedra) */
132   int V; /* vertex count */
133   int E; /* edge count */
134   int F; /* face count */
135   int D; /* density */
136   int chi; /* Euler characteristic */
137   int g; /* order of symmetry group */
138   int K; /* symmetry type: D=2, T=3, O=4, I=5 */
139   int hemi;/* flag hemi polyhedron */
140   int onesided;/* flag onesided polyhedron */
141   int even; /* removed face in pqr| */
142   int *Fi; /* face counts by type (array N)*/
143   int *rot; /* vertex configuration (array M of 0..N-1) */
144   int *snub; /* snub triangle configuration (array M of 0..1) */
145   int *firstrot; /* temporary for vertex generation (array V) */
146   int *anti; /* temporary for direction of ideal vertices (array E) */
147   int *ftype; /* face types (array F) */
148   int **e; /* edges (matrix 2 x E of 0..V-1)*/
149   int **dual_e; /* dual edges (matrix 2 x E of 0..F-1)*/
150   int **incid; /* vertex-face incidence (matrix M x V of 0..F-1)*/
151   int **adj; /* vertex-vertex adjacency (matrix M x V of 0..V-1)*/
152   double p[4]; /* p, q and r; |=0 */
153   double minr; /* smallest nonzero inradius */
154   double gon; /* basis type for dihedral polyhedra */
155   double *n; /* number of side of a face of each type (array N) */
156   double *m; /* number of faces at a vertex of each type (array N) */
157   double *gamma; /* fundamental angles in radians (array N) */
158   char *polyform; /* printable Wythoff symbol */
159   char *config; /* printable vertex configuration */
160   char *group; /* printable group name */
161   char *name; /* name, standard or manifuctured */
162   char *dual_name; /* dual name, standard or manifuctured */
163   char *class;
164   char *dual_class;
165   Vector *v; /* vertex coordinates (array V) */
166   Vector *f; /* face coordinates (array F)*/
167 } Polyhedron;
168
169 typedef struct {
170   long n,d;
171 } Fraction;
172
173 static Polyhedron *polyalloc(void);
174 static Vector rotate(Vector vertex, Vector axis, double angle);
175
176 static Vector sum3(Vector a, Vector b, Vector c);
177 static Vector scale(double k, Vector a);
178 static Vector sum(Vector a, Vector b);
179 static Vector diff(Vector a, Vector b);
180 static Vector pole (double r, Vector a, Vector b, Vector c);
181 static Vector cross(Vector a, Vector b);
182 static double dot(Vector a, Vector b);
183 static int same(Vector a, Vector b, double epsilon);
184
185 static char *sprintfrac(double x);
186
187 static void frac(double x);
188 static void matfree(void *mat, int rows);
189 static void *matalloc(int rows, int row_size);
190
191 static Fraction frax;
192
193
194 static const struct {
195   char *Wythoff, *name, *dual, *group, *class, *dual_class;
196   short Coxeter, Wenninger;
197 } uniform[] = {
198
199   /****************************************************************************
200    *            Dihedral Schwarz Triangles (D5 only)
201    ***************************************************************************/
202
203   /*    {"3|2 5/2",     "xyz",
204                                 "xyz",
205                                 "xyz",
206                                 "",
207                                 "",
208                                 0, 0},
209 */
210
211
212
213                                                            /* (2 2 5) (D1/5) */
214   /*  1 */      {"2 5|2",       "Pentagonal Prism",
215                                 "Pentagonal Dipyramid",
216                                 "Dihedral (D[1/5])",
217                                 "",
218                                 "",
219                                 0, 0},
220
221   /*  2 */      {"|2 2 5",      "Pentagonal Antiprism",
222                                 "Pentagonal Deltohedron",
223                                 "Dihedral (D[1/5])",
224                                 "",
225                                 "",
226                                 0, 0},
227                                                          /* (2 2 5/2) (D2/5) */
228   /*  3 */      {"2 5/2|2",     "Pentagrammic Prism",
229                                 "Pentagrammic Dipyramid",
230                                 "Dihedral (D[2/5])",
231                                 "",
232                                 "",
233                                 0, 0},
234
235   /*  4 */      {"|2 2 5/2",    "Pentagrammic Antiprism",
236                                 "Pentagrammic Deltohedron",
237                                 "Dihedral (D[2/5])",
238                                 "",
239                                 "",
240                                 0, 0},
241                                                          /* (5/3 2 2) (D3/5) */
242
243   /*  5 */      {"|2 2 5/3",    "Pentagrammic Crossed Antiprism",
244                                 "Pentagrammic Concave Deltohedron",
245                                 "Dihedral (D[3/5])",
246                                 "",
247                                 "",
248                                 0, 0},
249
250   /****************************************************************************
251    *            Tetrahedral
252    ***************************************************************************/
253
254                                                              /* (2 3 3) (T1) */
255   /*  6 */      {"3|2 3",       "Tetrahedron",
256                                 "Tetrahedron",
257                                 "Tetrahedral (T[1])",
258                                 "Platonic Solid",
259                                 "Platonic Solid",
260                                 15, 1},
261
262   /*  7 */      {"2 3|3",       "Truncated Tetrahedron",
263                                 "Triakistetrahedron",
264                                 "Tetrahedral (T[1])",
265                                 "Archimedian Solid",
266                                 "Catalan Solid",
267                                 16, 6},
268                                                            /* (3/2 3 3) (T2) */
269   /*  8 */      {"3/2 3|3",     "Octahemioctahedron",
270                                 "Octahemioctacron",
271                                 "Tetrahedral (T[2])",
272                                 "",
273                                 "",
274                                 37, 68},
275
276                                                            /* (3/2 2 3) (T3) */
277   /*  9 */      {"3/2 3|2",     "Tetrahemihexahedron",
278                                 "Tetrahemihexacron",
279                                 "Tetrahedral (T[3])",
280                                 "",
281                                 "",
282                                 36, 67},
283
284   /****************************************************************************
285    *            Octahedral
286    ***************************************************************************/
287
288                                                              /* (2 3 4) (O1) */
289   /* 10 */      {"4|2 3",       "Octahedron",
290                                 "Cube",
291                                 "Octahedral (O[1])",
292                                 "Platonic Solid",
293                                 "Platonic Solid",
294                                 17, 2},
295
296   /* 11 */      {"3|2 4",       "Cube",
297                                 "Octahedron",
298                                 "Octahedral (O[1])",
299                                 "Platonic Solid",
300                                 "Platonic Solid",
301                                 18, 3},
302
303   /* 12 */      {"2|3 4",       "Cuboctahedron",
304                                 "Rhombic Dodecahedron",
305                                 "Octahedral (O[1])",
306                                 "Archimedian Solid",
307                                 "Catalan Solid",
308                                 19, 11},
309
310   /* 13 */      {"2 4|3",       "Truncated Octahedron",
311                                 "Tetrakishexahedron",
312                                 "Octahedral (O[1])",
313                                 "Archimedian Solid",
314                                 "Catalan Solid",
315                                 20, 7},
316
317   /* 14 */      {"2 3|4",       "Truncated Cube",
318                                 "Triakisoctahedron",
319                                 "Octahedral (O[1])",
320                                 "Archimedian Solid",
321                                 "Catalan Solid",
322                                 21, 8},
323
324   /* 15 */      {"3 4|2",       "Rhombicuboctahedron",
325                                 "Deltoidal Icositetrahedron",
326                                 "Octahedral (O[1])",
327                                 "Archimedian Solid",
328                                 "Catalan Solid",
329                                 22, 13},
330
331   /* 16 */      {"2 3 4|",      "Truncated Cuboctahedron",
332                                 "Disdyakisdodecahedron",
333                                 "Octahedral (O[1])",
334                                 "Archimedian Solid",
335                                 "Catalan Solid",
336                                 23, 15},
337
338   /* 17 */      {"|2 3 4",      "Snub Cube",
339                                 "Pentagonal Icositetrahedron",
340                                 "Octahedral (O[1])",
341                                 "Archimedian Solid",
342                                 "Catalan Solid",
343                                 24, 17},
344                                                           /* (3/2 4 4) (O2b) */
345
346   /* 18 */      {"3/2 4|4",     "Small Cubicuboctahedron",
347                                 "Small Hexacronic Icositetrahedron",
348                                 "Octahedral (O[2b])",
349                                 "",
350                                 "",
351                                 38, 69},
352                                                            /* (4/3 3 4) (O4) */
353
354   /* 19 */      {"3 4|4/3",     "Great Cubicuboctahedron",
355                                 "Great Hexacronic Icositetrahedron",
356                                 "Octahedral (O[4])",
357                                 "",
358                                 "",
359                                 50, 77},
360
361   /* 20 */      {"4/3 4|3",     "Cubohemioctahedron",
362                                 "Hexahemioctacron",
363                                 "Octahedral (O[4])",
364                                 "",
365                                 "",
366                                 51, 78},
367
368   /* 21 */      {"4/3 3 4|",    "Cubitruncated Cuboctahedron",
369                                 "Tetradyakishexahedron",
370                                 "Octahedral (O[4])",
371                                 "",
372                                 "",
373                                 52, 79},
374                                                            /* (3/2 2 4) (O5) */
375
376   /* 22 */      {"3/2 4|2",     "Great Rhombicuboctahedron",
377                                 "Great Deltoidal Icositetrahedron",
378                                 "Octahedral (O[5])",
379                                 "",
380                                 "",
381                                 59, 85},
382
383   /* 23 */      {"3/2 2 4|",    "Small Rhombihexahedron",
384                                 "Small Rhombihexacron",
385                                 "Octahedral (O[5])",
386                                 "",
387                                 "",
388                                 60, 86},
389                                                            /* (4/3 2 3) (O7) */
390
391   /* 24 */      {"2 3|4/3",     "Stellated Truncated Hexahedron",
392                                 "Great Triakisoctahedron",
393                                 "Octahedral (O[7])",
394                                 "",
395                                 "",
396                                 66, 92},
397
398   /* 25 */      {"4/3 2 3|",    "Great Truncated Cuboctahedron",
399                                 "Great Disdyakisdodecahedron",
400                                 "Octahedral (O[7])",
401                                 "",
402                                 "",
403                                 67, 93},
404                                                         /* (4/3 3/2 2) (O11) */
405
406   /* 26 */      {"4/3 3/2 2|",  "Great Rhombihexahedron",
407                                 "Great Rhombihexacron",
408                                 "Octahedral (O[11])",
409                                 "",
410                                 "",
411                                 82, 103},
412
413   /****************************************************************************
414    *            Icosahedral
415    ***************************************************************************/
416
417                                                              /* (2 3 5) (I1) */
418   /* 27 */      {"5|2 3",       "Icosahedron",
419                                 "Dodecahedron",
420                                 "Icosahedral (I[1])",
421                                 "Platonic Solid",
422                                 "Platonic Solid",
423                                 25, 4},
424
425   /* 28 */      {"3|2 5",       "Dodecahedron",
426                                 "Icosahedron",
427                                 "Icosahedral (I[1])",
428                                 "Platonic Solid",
429                                 "Platonic Solid",
430                                 26, 5},
431
432   /* 29 */      {"2|3 5",       "Icosidodecahedron",
433                                 "Rhombic Triacontahedron",
434                                 "Icosahedral (I[1])",
435                                 "Archimedian Solid",
436                                 "Catalan Solid",
437                                 28, 12},
438
439   /* 30 */      {"2 5|3",       "Truncated Icosahedron",
440                                 "Pentakisdodecahedron",
441                                 "Icosahedral (I[1])",
442                                 "Archimedian Solid",
443                                 "Catalan Solid",
444                                 27, 9},
445
446   /* 31 */      {"2 3|5",       "Truncated Dodecahedron",
447                                 "Triakisicosahedron",
448                                 "Icosahedral (I[1])",
449                                 "Archimedian Solid",
450                                 "Catalan Solid",
451                                 29, 10},
452
453   /* 32 */      {"3 5|2",       "Rhombicosidodecahedron",
454                                 "Deltoidal Hexecontahedron",
455                                 "Icosahedral (I[1])",
456                                 "Archimedian Solid",
457                                 "Catalan Solid",
458                                 30, 14},
459
460   /* 33 */      {"2 3 5|",      "Truncated Icosidodecahedron",
461                                 "Disdyakistriacontahedron",
462                                 "Icosahedral (I[1])",
463                                 "Archimedian Solid",
464                                 "Catalan Solid",
465                                 31, 16},
466
467   /* 34 */      {"|2 3 5",      "Snub Dodecahedron",
468                                 "Pentagonal Hexecontahedron",
469                                 "Icosahedral (I[1])",
470                                 "Archimedian Solid",
471                                 "Catalan Solid",
472                                 32, 18},
473                                                           /* (5/2 3 3) (I2a) */
474
475   /* 35 */      {"3|5/2 3",     "Small Ditrigonal Icosidodecahedron",
476                                 "Small Triambic Icosahedron",
477                                 "Icosahedral (I[2a])",
478                                 "",
479                                 "",
480                                 39, 70},
481
482   /* 36 */      {"5/2 3|3",     "Small Icosicosidodecahedron",
483                                 "Small Icosacronic Hexecontahedron",
484                                 "Icosahedral (I[2a])",
485                                 "",
486                                 "",
487                                 40, 71},
488
489   /* 37 */      {"|5/2 3 3",    "Small Snub Icosicosidodecahedron",
490                                 "Small Hexagonal Hexecontahedron",
491                                 "Icosahedral (I[2a])",
492                                 "",
493                                 "",
494                                 41, 110},
495                                                           /* (3/2 5 5) (I2b) */
496
497   /* 38 */      {"3/2 5|5",     "Small Dodecicosidodecahedron",
498                                 "Small Dodecacronic Hexecontahedron",
499                                 "Icosahedral (I[2b])",
500                                 "",
501                                 "",
502                                 42, 72},
503                                                            /* (2 5/2 5) (I3) */
504
505   /* 39 */      {"5|2 5/2",     "Small Stellated Dodecahedron",
506                                 "Great Dodecahedron",
507                                 "Icosahedral (I[3])",
508                                 "Truncated Kepler-Poinsot Solid",
509                                 "",
510                                 43, 20},
511
512   /* 40 */      {"5/2|2 5",     "Great Dodecahedron",
513                                 "Small Stellated Dodecahedron",
514                                 "Icosahedral (I[3])",
515                                 "",
516                                 "",
517                                 44, 21},
518
519   /* 41 */      {"2|5/2 5",     "Great Dodecadodecahedron",
520                                 "Medial Rhombic Triacontahedron",
521                                 "Icosahedral (I[3])",
522                                 "",
523                                 "",
524                                 45, 73},
525
526   /* 42 */      {"2 5/2|5",     "Truncated Great Dodecahedron",
527                                 "Small Stellapentakisdodecahedron",
528                                 "Icosahedral (I[3])",
529                                 "Truncated Kepler-Poinsot Solid",
530                                 "",
531                                 47, 75},
532
533   /* 43 */      {"5/2 5|2",     "Rhombidodecadodecahedron",
534                                 "Medial Deltoidal Hexecontahedron",
535                                 "Icosahedral (I[3])",
536                                 "",
537                                 "",
538                                 48, 76},
539
540   /* 44 */      {"2 5/2 5|",    "Small Rhombidodecahedron",
541                                 "Small Rhombidodecacron",
542                                 "Icosahedral (I[3])",
543                                 "",
544                                 "",
545                                 46, 74},
546
547   /* 45 */      {"|2 5/2 5",    "Snub Dodecadodecahedron",
548                                 "Medial Pentagonal Hexecontahedron",
549                                 "Icosahedral (I[3])",
550                                 "",
551                                 "",
552                                 49, 111},
553                                                            /* (5/3 3 5) (I4) */
554
555   /* 46 */      {"3|5/3 5",     "Ditrigonal Dodecadodecahedron",
556                                 "Medial Triambic Icosahedron",
557                                 "Icosahedral (I[4])",
558                                 "",
559                                 "",
560                                 53, 80},
561
562   /* 47 */      {"3 5|5/3",     "Great Ditrigonal Dodecicosidodecahedron",
563                                "Great Ditrigonal Dodecacronic Hexecontahedron",
564                                 "Icosahedral (I[4])",
565                                 "",
566                                 "",
567                                 54, 81},
568
569   /* 48 */      {"5/3 3|5",     "Small Ditrigonal Dodecicosidodecahedron",
570                                "Small Ditrigonal Dodecacronic Hexecontahedron",
571                                 "Icosahedral (I[4])",
572                                 "",
573                                 "",
574                                 55, 82},
575
576   /* 49 */      {"5/3 5|3",     "Icosidodecadodecahedron",
577                                 "Medial Icosacronic Hexecontahedron",
578                                 "Icosahedral (I[4])",
579                                 "",
580                                 "",
581                                 56, 83},
582
583   /* 50 */      {"5/3 3 5|",    "Icositruncated Dodecadodecahedron",
584                                 "Tridyakisicosahedron",
585                                 "Icosahedral (I[4])",
586                                 "",
587                                 "",
588                                 57, 84},
589
590   /* 51 */      {"|5/3 3 5",    "Snub Icosidodecadodecahedron",
591                                 "Medial Hexagonal Hexecontahedron",
592                                 "Icosahedral (I[4])",
593                                 "",
594                                 "",
595                                 58, 112},
596                                                           /* (3/2 3 5) (I6b) */
597
598   /* 52 */      {"3/2|3 5",     "Great Ditrigonal Icosidodecahedron",
599                                 "Great Triambic Icosahedron",
600                                 "Icosahedral (I[6b])",
601                                 "",
602                                 "",
603                                 61, 87},
604
605   /* 53 */      {"3/2 5|3",     "Great Icosicosidodecahedron",
606                                 "Great Icosacronic Hexecontahedron",
607                                 "Icosahedral (I[6b])",
608                                 "",
609                                 "",
610                                 62, 88},
611
612   /* 54 */      {"3/2 3|5",     "Small Icosihemidodecahedron",
613                                 "Small Icosihemidodecacron",
614                                 "Icosahedral (I[6b])",
615                                 "",
616                                 "",
617                                 63, 89},
618
619   /* 55 */      {"3/2 3 5|",    "Small Dodecicosahedron",
620                                 "Small Dodecicosacron",
621                                 "Icosahedral (I[6b])",
622                                 "",
623                                 "",
624                                 64, 90},
625                                                           /* (5/4 5 5) (I6c) */
626
627   /* 56 */      {"5/4 5|5",     "Small Dodecahemidodecahedron",
628                                 "Small Dodecahemidodecacron",
629                                 "Icosahedral (I[6c])",
630                                 "",
631                                 "",
632                                 65, 91},
633                                                            /* (2 5/2 3) (I7) */
634
635   /* 57 */      {"3|2 5/2",     "Great Stellated Dodecahedron",
636                                 "Great Icosahedron",
637                                 "Icosahedral (I[7])",
638                                 "",
639                                 "",
640                                 68, 22},
641
642   /* 58 */      {"5/2|2 3",     "Great Icosahedron",
643                                 "Great Stellated Dodecahedron",
644                                 "Icosahedral (I[7])",
645                                 "",
646                                 "",
647                                 69, 41},
648
649   /* 59 */      {"2|5/2 3",     "Great Icosidodecahedron",
650                                 "Great Rhombic Triacontahedron",
651                                 "Icosahedral (I[7])",
652                                 "Truncated Kepler-Poinsot Solid",
653                                 "",
654                                 70, 94},
655
656   /* 60 */      {"2 5/2|3",     "Great Truncated Icosahedron",
657                                 "Great Stellapentakisdodecahedron",
658                                 "Icosahedral (I[7])",
659                                 "Truncated Kepler-Poinsot Solid",
660                                 "",
661                                 71, 95},
662
663   /* 61 */      {"2 5/2 3|",    "Rhombicosahedron",
664                                 "Rhombicosacron",
665                                 "Icosahedral (I[7])",
666                                 "",
667                                 "",
668                                 72, 96},
669
670   /* 62 */      {"|2 5/2 3",    "Great Snub Icosidodecahedron",
671                                 "Great Pentagonal Hexecontahedron",
672                                 "Icosahedral (I[7])",
673                                 "",
674                                 "",
675                                 73, 113},
676                                                            /* (5/3 2 5) (I9) */
677
678   /* 63 */      {"2 5|5/3",     "Small Stellated Truncated Dodecahedron",
679                                 "Great Pentakisdodekahedron",
680                                 "Icosahedral (I[9])",
681                                 "",
682                                 "",
683                                 74, 97},
684
685   /* 64 */      {"5/3 2 5|",    "Truncated Dodecadodecahedron",
686                                 "Medial Disdyakistriacontahedron",
687                                 "Icosahedral (I[9])",
688                                 "",
689                                 "",
690                                 75, 98},
691
692   /* 65 */      {"|5/3 2 5",    "Inverted Snub Dodecadodecahedron",
693                                 "Medial Inverted Pentagonal Hexecontahedron",
694                                 "Icosahedral (I[9])",
695                                 "",
696                                 "",
697                                 76, 114},
698                                                        /* (5/3 5/2 3) (I10a) */
699
700   /* 66 */      {"5/2 3|5/3",   "Great Dodecicosidodecahedron",
701                                 "Great Dodecacronic Hexecontahedron",
702                                 "Icosahedral (I[10a])",
703                                 "",
704                                 "",
705                                 77, 99},
706
707   /* 67 */      {"5/3 5/2|3",   "Small Dodecahemicosahedron",
708                                 "Small Dodecahemicosacron",
709                                 "Icosahedral (I[10a])",
710                                 "",
711                                 "",
712                                 78, 100},
713
714   /* 68 */      {"5/3 5/2 3|",  "Great Dodecicosahedron",
715                                 "Great Dodecicosacron",
716                                 "Icosahedral (I[10a])",
717                                 "",
718                                 "",
719                                 79, 101},
720
721   /* 69 */      {"|5/3 5/2 3",  "Great Snub Dodecicosidodecahedron",
722                                 "Great Hexagonal Hexecontahedron",
723                                 "Icosahedral (I[10a])",
724                                 "",
725                                 "",
726                                 80, 115},
727                                                          /* (5/4 3 5) (I10b) */
728
729   /* 70 */      {"5/4 5|3",     "Great Dodecahemicosahedron",
730                                 "Great Dodecahemicosacron",
731                                 "Icosahedral (I[10b])",
732                                 "",
733                                 "",
734                                 81, 102},
735                                                           /* (5/3 2 3) (I13) */
736
737   /* 71 */      {"2 3|5/3",     "Great Stellated Truncated Dodecahedron",
738                                 "Great Triakisicosahedron",
739                                 "Icosahedral (I[13])",
740                                 "",
741                                 "",
742                                 83, 104},
743
744   /* 72 */      {"5/3 3|2",     "Great Rhombicosidodecahedron",
745                                 "Great Deltoidal Hexecontahedron",
746                                 "Icosahedral (I[13])",
747                                 "",
748                                 "",
749                                 84, 105},
750
751   /* 73 */      {"5/3 2 3|",    "Great Truncated Icosidodecahedron",
752                                 "Great Disdyakistriacontahedron",
753                                 "Icosahedral (I[13])",
754                                 "",
755                                 "",
756                                 87, 108},
757
758   /* 74 */      {"|5/3 2 3",    "Great Inverted Snub Icosidodecahedron",
759                                 "Great Inverted Pentagonal Hexecontahedron",
760                                 "Icosahedral (I[13])",
761                                 "",
762                                 "",
763                                 88, 116},
764                                                      /* (5/3 5/3 5/2) (I18a) */
765
766   /* 75 */      {"5/3 5/2|5/3", "Great Dodecahemidodecahedron",
767                                 "Great Dodecahemidodecacron",
768                                 "Icosahedral (I[18a])",
769                                 "",
770                                 "",
771                                 86, 107},
772                                                        /* (3/2 5/3 3) (I18b) */
773
774   /* 76 */      {"3/2 3|5/3",   "Great Icosihemidodecahedron",
775                                 "Great Icosihemidodecacron",
776                                 "Icosahedral (I[18b])",
777                                 "",
778                                 "",
779                                 85, 106},
780                                                       /* (3/2 3/2 5/3) (I22) */
781
782   /* 77 */      {"|3/2 3/2 5/2","Small Retrosnub Icosicosidodecahedron",
783                                 "Small Hexagrammic Hexecontahedron",
784                                 "Icosahedral (I[22])",
785                                 "",
786                                 "",
787                                 91, 118},
788                                                         /* (3/2 5/3 2) (I23) */
789
790   /* 78 */      {"3/2 5/3 2|",  "Great Rhombidodecahedron",
791                                 "Great Rhombidodecacron",
792                                 "Icosahedral (I[23])",
793                                 "",
794                                 "",
795                                 89, 109},
796
797   /* 79 */      {"|3/2 5/3 2",  "Great Retrosnub Icosidodecahedron",
798                                 "Great Pentagrammic Hexecontahedron",
799                                 "Icosahedral (I[23])",
800                                 "",
801                                 "",
802                                 90, 117},
803
804   /****************************************************************************
805    *            Last But Not Least
806    ***************************************************************************/
807
808   /* 80 */    {"3/2 5/3 3 5/2", "Great Dirhombicosidodecahedron",
809                                 "Great Dirhombicosidodecacron",
810                                 "Non-Wythoffian",
811                                 "",
812                                 "",
813                                 92, 119}
814 };
815
816 static int last_uniform = sizeof (uniform) / sizeof (uniform[0]);
817
818
819
820 static int unpacksym(char *sym, Polyhedron *P);
821 static int moebius(Polyhedron *P);
822 static int decompose(Polyhedron *P);
823 static int guessname(Polyhedron *P);
824 static int newton(Polyhedron *P, int need_approx);
825 static int exceptions(Polyhedron *P);
826 static int count(Polyhedron *P);
827 static int configuration(Polyhedron *P);
828 static int vertices(Polyhedron *P);
829 static int faces(Polyhedron *P);
830 static int edgelist(Polyhedron *P);
831
832 static Polyhedron *
833 kaleido(char *sym, int need_coordinates, int need_edgelist, int need_approx,
834         int just_list)
835 {
836   Polyhedron *P;
837   /*
838    * Allocate a Polyhedron structure P.
839    */
840   if (!(P = polyalloc()))
841     return 0;
842   /*
843    * Unpack input symbol into P.
844    */
845   if (!unpacksym(sym, P))
846     return 0;
847   /*
848    * Find Mebius triangle, its density and Euler characteristic.
849    */
850   if (!moebius(P))
851     return 0;
852   /*
853    * Decompose Schwarz triangle.
854    */
855   if (!decompose(P))
856     return 0;
857   /*
858    * Find the names of the polyhedron and its dual.
859    */
860   if (!guessname(P))
861     return 0;
862   if (just_list)
863     return P;
864   /*
865    * Solve Fundamental triangles, optionally printing approximations.
866    */
867   if (!newton(P,need_approx))
868     return 0;
869   /*
870    * Deal with exceptional polyhedra.
871    */
872   if (!exceptions(P))
873     return 0;
874   /*
875    * Count edges and faces, update density and characteristic if needed.
876    */
877   if (!count(P))
878     return 0;
879   /*
880    * Generate printable vertex configuration.
881    */
882   if (!configuration(P))
883     return 0;
884   /*
885    * Compute coordinates.
886    */
887   if (!need_coordinates && !need_edgelist)
888     return P;
889   if (!vertices(P))
890     return 0;
891   if (!faces (P))
892     return 0;
893   /*
894    * Compute edgelist.
895    */
896   if (!need_edgelist)
897     return P;
898   if (!edgelist(P))
899     return 0;
900   return P;
901 }
902
903 /*
904  * Allocate a blank Polyhedron structure and initialize some of its nonblank
905  * fields.
906  *
907  * Array and matrix field are allocated when needed.
908  */
909 static Polyhedron *
910 polyalloc()
911 {
912   Polyhedron *P;
913   Calloc(P, 1, Polyhedron);
914   P->index = -1;
915   P->even = -1;
916   P->K = 2;
917   return P;
918 }
919
920 /*
921  * Free the struture allocated by polyalloc(), as well as all the array and
922  * matrix fields.
923  */
924 static void
925 polyfree(Polyhedron *P)
926 {
927   Free(P->Fi);
928   Free(P->n);
929   Free(P->m);
930   Free(P->gamma);
931   Free(P->rot);
932   Free(P->snub);
933   Free(P->firstrot);
934   Free(P->anti);
935   Free(P->ftype);
936   Free(P->polyform);
937   Free(P->config);
938   if (P->index < 0) {
939     Free(P->name);
940     Free(P->dual_name);
941   }
942   Free(P->v);
943   Free(P->f);
944   Matfree(P->e, 2);
945   Matfree(P->dual_e, 2);
946   Matfree(P->incid, P->M);
947   Matfree(P->adj, P->M);
948   free(P);
949 }
950
951 static void *
952 matalloc(int rows, int row_size)
953 {
954   void **mat;
955   int i = 0;
956   if (!(mat = malloc(rows * sizeof (void *))))
957     return 0;
958   while ((mat[i] = malloc(row_size)) && ++i < rows)
959     ;
960   if (i == rows)
961     return (void *)mat;
962   while (--i >= 0)
963     free(mat[i]);
964   free(mat);
965   return 0;
966 }
967
968 static void
969 matfree(void *mat, int rows)
970 {
971   while (--rows >= 0)
972     free(((void **)mat)[rows]);
973   free(mat);
974 }
975
976 /*
977  * compute the mathematical modulus function.
978  */
979 static int
980 mod (int i, int j)
981 {
982   return (i%=j)>=0?i:j<0?i-j:i+j;
983 }
984
985
986 /*
987  * Find the numerator and the denominator using the Euclidean algorithm.
988  */
989 static void
990 frac(double x)
991 {
992   static const Fraction zero = {0,1}, inf = {1,0};
993   Fraction r0, r;
994   long f;
995   double s = x;
996   r = zero;
997   frax = inf;
998   for (;;) {
999     if (fabs(s) > (double) MAXLONG)
1000       return;
1001     f = (long) floor (s);
1002     r0 = r;
1003     r = frax;
1004     frax.n = frax.n * f + r0.n;
1005     frax.d = frax.d * f + r0.d;
1006     if (x == (double)frax.n/(double)frax.d)
1007       return;
1008     s = 1 / (s - f);
1009   }
1010 }
1011
1012
1013 /*
1014  * Unpack input symbol: Wythoff symbol or an index to uniform[]. The symbol is
1015  * a # followed by a number, or a three fractions and a bar in some order. We
1016  * allow no bars only if it result from the input symbol #80.
1017  */
1018 static int
1019 unpacksym(char *sym, Polyhedron *P)
1020 {
1021   int i = 0, n, d, bars = 0;
1022   char c;
1023   while ((c = *sym++) && isspace(c))
1024     ;
1025   if (!c) Err("no data");
1026   if (c == '#') {
1027     while ((c = *sym++) && isspace(c))
1028       ;
1029     if (!c)
1030       Err("no digit after #");
1031     if (!isdigit(c))
1032       Err("not a digit");
1033     n = c - '0';
1034     while ((c = *sym++) && isdigit(c))
1035       n = n * 10 + c - '0';
1036     if (!n)
1037       Err("zero index");
1038     if (n > last_uniform)
1039       Err("index too big");
1040     sym--;
1041     while ((c = *sym++) && isspace(c))
1042       ;
1043     if (c)
1044       Err("data exceeded");
1045     sym = uniform[P->index = n - 1].Wythoff;
1046   } else
1047     sym--;
1048
1049   for (;;) {
1050     while ((c = *sym++) && isspace(c))
1051       ;
1052     if (!c) {
1053       if (i == 4 && (bars || P->index == last_uniform - 1))
1054         return 1;
1055       if (!bars)
1056         Err("no bars");
1057       Err("not enough fractions");
1058     }
1059     if (i == 4)
1060       Err("data exceeded");
1061     if (c == '|'){
1062       if (++bars > 1)
1063         Err("too many bars");
1064       P->p[i++] = 0;
1065       continue;
1066     }
1067     if (!isdigit(c))
1068       Err("not a digit");
1069     n = c - '0';
1070     while ((c = *sym++) && isdigit(c))
1071       n = n * 10 + c - '0';
1072     if (c && isspace (c))
1073       while ((c = *sym++) && isspace(c))
1074         ;
1075     if (c != '/') {
1076       sym--;
1077       if ((P->p[i++] = n) <= 1)
1078         Err("fraction<=1");
1079       continue;
1080     }
1081     while ((c = *sym++) && isspace(c))
1082       ;
1083     if (!c || !isdigit(c))
1084       return 0;
1085     d = c - '0';
1086     while ((c = *sym++) && isdigit(c))
1087       d = d * 10 + c - '0';
1088     if (!d)
1089       Err("zero denominator");
1090     sym--;
1091     if ((P->p[i++] = (double) n / d) <= 1)
1092       Err("fraction<=1");
1093   }
1094 }
1095
1096 /*
1097  * Using Wythoff symbol (p|qr, pq|r, pqr| or |pqr), find the Moebius triangle
1098  * (2 3 K) (or (2 2 n)) of the Schwarz triangle (pqr), the order g of its
1099  * symmetry group, its Euler characteristic chi, and its covering density D.
1100  * g is the number of copies of (2 3 K) covering the sphere, i.e.,
1101  *
1102  *              g * pi * (1/2 + 1/3 + 1/K - 1) = 4 * pi
1103  *
1104  * D is the number of times g copies of (pqr) cover the sphere, i.e.
1105  *
1106  *              D * 4 * pi = g * pi * (1/p + 1/q + 1/r - 1)
1107  *
1108  * chi is V - E + F, where F = g is the number of triangles, E = 3*g/2 is the
1109  * number of triangle edges, and V = Vp+ Vq+ Vr, with Vp = g/(2*np) being the
1110  * number of vertices with angle pi/p (np is the numerator of p).
1111  */
1112 static int
1113 moebius(Polyhedron *P)
1114 {
1115   int twos = 0, j, len = 1;
1116   /*
1117    * Arrange Wythoff symbol in a presentable form. In the same time check the
1118    * restrictions on the three fractions: They all have to be greater then one,
1119    * and the numerators 4 or 5 cannot occur together.  We count the ocurrences
1120    * of 2 in `two', and save the largest numerator in `P->K', since they
1121    * reflect on the symmetry group.
1122    */
1123   P->K = 2;
1124   if (P->index == last_uniform - 1) {
1125     Malloc(P->polyform, ++len, char);
1126     strcpy(P->polyform, "|");
1127   } else
1128     Calloc(P->polyform, len, char);
1129   for (j = 0; j < 4; j++) {
1130     if (P->p[j]) {
1131       char *s;
1132       Sprintfrac(s, P->p[j]);
1133       if (j && P->p[j-1]) {
1134         Realloc(P->polyform, len += strlen (s) + 1, char);
1135         strcat(P->polyform, " ");
1136       } else
1137         Realloc (P->polyform, len += strlen (s), char);
1138       strcat(P->polyform, s);
1139       free(s);
1140       if (P->p[j] != 2) {
1141         int k;
1142         if ((k = numerator (P->p[j])) > P->K) {
1143           if (P->K == 4)
1144             break;
1145           P->K = k;
1146         } else if (k < P->K && k == 4)
1147           break;
1148       } else
1149         twos++;
1150     } else  {
1151       Realloc(P->polyform, ++len, char);
1152       strcat(P->polyform, "|");
1153     }
1154   }
1155   /*
1156    * Find the symmetry group P->K (where 2, 3, 4, 5 represent the dihedral,
1157    * tetrahedral, octahedral and icosahedral groups, respectively), and its
1158    * order P->g.
1159    */
1160   if (twos >= 2) {/* dihedral */
1161     P->g = 4 * P->K;
1162     P->K = 2;
1163   } else {
1164     if (P->K > 5)
1165       Err("numerator too large");
1166     P->g = 24 * P->K / (6 - P->K);
1167   }
1168   /*
1169    * Compute the nominal density P->D and Euler characteristic P->chi.
1170    * In few exceptional cases, these values will be modified later.
1171    */
1172   if (P->index != last_uniform - 1) {
1173     int i;
1174     P->D = P->chi = - P->g;
1175     for (j = 0; j < 4; j++) if (P->p[j]) {
1176       P->chi += i = P->g / numerator(P->p[j]);
1177       P->D += i * denominator(P->p[j]);
1178     }
1179     P->chi /= 2;
1180     P->D /= 4;
1181     if (P->D <= 0)
1182       Err("nonpositive density");
1183   }
1184   return 1;
1185 }
1186
1187 /*
1188  * Decompose Schwarz triangle into N right triangles and compute the vertex
1189  * count V and the vertex valency M.  V is computed from the number g of
1190  * Schwarz triangles in the cover, divided by the number of triangles which
1191  * share a vertex. It is halved for one-sided polyhedra, because the
1192  * kaleidoscopic construction really produces a double orientable covering of
1193  * such polyhedra. All q' q|r are of the "hemi" type, i.e. have equatorial {2r}
1194  * faces, and therefore are (except 3/2 3|3 and the dihedra 2 2|r) one-sided. A
1195  * well known example is 3/2 3|4, the "one-sided heptahedron". Also, all p q r|
1196  * with one even denominator have a crossed parallelogram as a vertex figure,
1197  * and thus are one-sided as well.
1198  */
1199 static int
1200 decompose(Polyhedron *P)
1201 {
1202   int j, J, *s, *t;
1203   if (!P->p[1]) { /* p|q r */
1204     P->N = 2;
1205     P->M = 2 * numerator(P->p[0]);
1206     P->V = P->g / P->M;
1207     Malloc(P->n, P->N, double);
1208     Malloc(P->m, P->N, double);
1209     Malloc(P->rot, P->M, int);
1210     s = P->rot;
1211     for (j = 0; j < 2; j++) {
1212       P->n[j] = P->p[j+2];
1213       P->m[j] = P->p[0];
1214     }
1215     for (j = P->M / 2; j--;) {
1216       *s++ = 0;
1217       *s++ = 1;
1218     }
1219   } else if (!P->p[2]) { /* p q|r */
1220     P->N = 3;
1221     P->M = 4;
1222     P->V = P->g / 2;
1223     Malloc(P->n, P->N, double);
1224     Malloc(P->m, P->N, double);
1225     Malloc(P->rot, P->M, int);
1226     s = P->rot;
1227     P->n[0] = 2 * P->p[3];
1228     P->m[0] = 2;
1229     for (j = 1; j < 3; j++) {
1230       P->n[j] = P->p[j-1];
1231       P->m[j] = 1;
1232       *s++ = 0;
1233       *s++ = j;
1234     }
1235     if (fabs(P->p[0] - compl (P->p[1])) < DBL_EPSILON) {/* p = q' */
1236       /* P->p[0]==compl(P->p[1]) should work.  However, MSDOS
1237        * yeilds a 7e-17 difference! Reported by Jim Buddenhagen
1238        * <jb1556@daditz.sbc.com> */
1239       P->hemi = 1;
1240       P->D = 0;
1241       if (P->p[0] != 2 && !(P->p[3] == 3 && (P->p[0] == 3 ||
1242                                              P->p[1] == 3))) {
1243         P->onesided = 1;
1244         P->V /= 2;
1245         P->chi /= 2;
1246       }
1247     }
1248   } else if (!P->p[3]) { /* p q r| */
1249     P->M = P->N = 3;
1250     P->V = P->g;
1251     Malloc(P->n, P->N, double);
1252     Malloc(P->m, P->N, double);
1253     Malloc(P->rot, P->M, int);
1254     s = P->rot;
1255     for (j = 0; j < 3; j++) {
1256       if (!(denominator(P->p[j]) % 2)) {
1257         /* what happens if there is more then one even denominator? */
1258         if (P->p[(j+1)%3] != P->p[(j+2)%3]) { /* needs postprocessing */
1259           P->even = j;/* memorize the removed face */
1260           P->chi -= P->g / numerator(P->p[j]) / 2;
1261           P->onesided = 1;
1262           P->D = 0;
1263         } else {/* for p = q we get a double 2 2r|p */
1264                 /* noted by Roman Maeder <maeder@inf.ethz.ch> for 4 4 3/2| */
1265                 /* Euler characteristic is still wrong */
1266           P->D /= 2;
1267         }
1268         P->V /= 2;
1269       }
1270       P->n[j] = 2 * P->p[j];
1271       P->m[j] = 1;
1272       *s++ = j;
1273     }
1274   } else { /* |p q r - snub polyhedron */
1275     P->N = 4;
1276     P->M = 6;
1277     P->V = P->g / 2;/* Only "white" triangles carry a vertex */
1278     Malloc(P->n, P->N, double);
1279     Malloc(P->m, P->N, double);
1280     Malloc(P->rot, P->M, int);
1281     Malloc(P->snub, P->M, int);
1282     s = P->rot;
1283     t = P->snub;
1284     P->m[0] = P->n[0] = 3;
1285     for (j = 1; j < 4; j++) {
1286       P->n[j] = P->p[j];
1287       P->m[j] = 1;
1288       *s++ = 0;
1289       *s++ = j;
1290       *t++ = 1;
1291       *t++ = 0;
1292     }
1293   }
1294   /*
1295    * Sort the fundamental triangles (using bubble sort) according to decreasing
1296    * n[i], while pushing the trivial triangles (n[i] = 2) to the end.
1297    */
1298   J = P->N - 1;
1299   while (J) {
1300     int last;
1301     last = J;
1302     J = 0;
1303     for (j = 0; j < last; j++) {
1304       if ((P->n[j] < P->n[j+1] || P->n[j] == 2) && P->n[j+1] != 2) {
1305         int i;
1306         double temp;
1307         temp = P->n[j];
1308         P->n[j] = P->n[j+1];
1309         P->n[j+1] = temp;
1310         temp = P->m[j];
1311         P->m[j] = P->m[j+1];
1312         P->m[j+1] = temp;
1313         for (i = 0; i < P->M; i++) {
1314           if (P->rot[i] == j)
1315             P->rot[i] = j+1;
1316           else if (P->rot[i] == j+1)
1317             P->rot[i] = j;
1318         }
1319         if (P->even != -1) {
1320           if (P->even == j)
1321             P->even = j+1;
1322           else if (P->even == j+1)
1323             P->even = j;
1324         }
1325         J = j;
1326       }
1327     }
1328   }
1329   /*
1330    *  Get rid of repeated triangles.
1331    */
1332   for (J = 0; J < P->N && P->n[J] != 2;J++) {
1333     int k, i;
1334     for (j = J+1; j < P->N && P->n[j]==P->n[J]; j++)
1335       P->m[J] += P->m[j];
1336     k = j - J - 1;
1337     if (k) {
1338       for (i = j; i < P->N; i++) {
1339         P->n[i - k] = P->n[i];
1340         P->m[i - k] = P->m[i];
1341       }
1342       P->N -= k;
1343       for (i = 0; i < P->M; i++) {
1344         if (P->rot[i] >= j)
1345           P->rot[i] -= k;
1346         else if (P->rot[i] > J)
1347           P->rot[i] = J;
1348       }
1349       if (P->even >= j)
1350         P->even -= k;
1351     }
1352   }
1353   /*
1354    * Get rid of trivial triangles.
1355    */
1356   if (!J)
1357     J = 1; /* hosohedron */
1358   if (J < P->N) {
1359     int i;
1360     P->N = J;
1361     for (i = 0; i < P->M; i++) {
1362       if (P->rot[i] >= P->N) {
1363         for (j = i + 1; j < P->M; j++) {
1364           P->rot[j-1] = P->rot[j];
1365           if (P->snub)
1366             P->snub[j-1] = P->snub[j];
1367         }
1368         P->M--;
1369       }
1370     }
1371   }
1372   /*
1373    * Truncate arrays
1374    */
1375   Realloc(P->n, P->N, double);
1376   Realloc(P->m, P->N, double);
1377   Realloc(P->rot, P->M, int);
1378   if (P->snub)
1379     Realloc(P->snub, P->M, int);
1380   return 1;
1381 }
1382
1383
1384 static int dihedral(Polyhedron *P, char *name, char *dual_name);
1385
1386
1387 /*
1388  * Get the polyhedron name, using standard list or guesswork. Ideally, we
1389  * should try to locate the Wythoff symbol in the standard list (unless, of
1390  * course, it is dihedral), after doing few normalizations, such as sorting
1391  * angles and splitting isoceles triangles.
1392  */
1393 static int
1394 guessname(Polyhedron *P)
1395 {
1396   if (P->index != -1) {/* tabulated */
1397     P->name = uniform[P->index].name;
1398     P->dual_name = uniform[P->index].dual;
1399     P->group = uniform[P->index].group;
1400     P->class = uniform[P->index].class;
1401     P->dual_class = uniform[P->index].dual_class;
1402     return 1;
1403   } else if (P->K == 2) {/* dihedral nontabulated */
1404     if (!P->p[0]) {
1405       if (P->N == 1) {
1406         Malloc(P->name, sizeof ("Octahedron"), char);
1407         Malloc(P->dual_name, sizeof ("Cube"), char);
1408         strcpy(P->name, "Octahedron");
1409         strcpy(P->dual_name, "Cube");
1410         return 1;
1411       }
1412       P->gon = P->n[0] == 3 ? P->n[1] : P->n[0];
1413       if (P->gon >= 2)
1414         return dihedral(P, "Antiprism", "Deltohedron");
1415       else
1416         return dihedral(P, "Crossed Antiprism", "Concave Deltohedron");
1417     } else if (!P->p[3] ||
1418                (!P->p[2] &&
1419                 P->p[3] == 2)) {
1420       if (P->N == 1) {
1421         Malloc(P->name, sizeof("Cube"), char);
1422         Malloc(P->dual_name, sizeof("Octahedron"), char);
1423         strcpy(P->name, "Cube");
1424         strcpy(P->dual_name, "Octahedron");
1425         return 1;
1426       }
1427       P->gon = P->n[0] == 4 ? P->n[1] : P->n[0];
1428       return dihedral(P, "Prism", "Dipyramid");
1429     } else if (!P->p[1] && P->p[0] != 2) {
1430       P->gon = P->m[0];
1431       return dihedral(P, "Hosohedron", "Dihedron");
1432     } else {
1433       P->gon = P->n[0];
1434       return dihedral(P, "Dihedron", "Hosohedron");
1435     }
1436   } else {/* other nontabulated */
1437     static const char *pre[] = {"Tetr", "Oct", "Icos"};
1438     Malloc(P->name, 50, char);
1439     Malloc(P->dual_name, 50, char);
1440     sprintf(P->name, "%sahedral ", pre[P->K - 3]);
1441     if (P->onesided)
1442       strcat (P->name, "One-Sided ");
1443     else if (P->D == 1)
1444       strcat(P->name, "Convex ");
1445     else
1446       strcat(P->name, "Nonconvex ");
1447     strcpy(P->dual_name, P->name);
1448     strcat(P->name, "Isogonal Polyhedron");
1449     strcat(P->dual_name, "Isohedral Polyhedron");
1450     Realloc(P->name, strlen (P->name) + 1, char);
1451     Realloc(P->dual_name, strlen (P->dual_name) + 1, char);
1452     return 1;
1453   }
1454 }
1455
1456 static int
1457 dihedral(Polyhedron *P, char *name, char *dual_name)
1458 {
1459   char *s;
1460   int i;
1461   Sprintfrac(s, P->gon < 2 ? compl (P->gon) : P->gon);
1462   i = strlen(s) + sizeof ("-gonal ");
1463   Malloc(P->name, i + strlen (name), char);
1464   Malloc(P->dual_name, i + strlen (dual_name), char);
1465   sprintf(P->name, "%s-gonal %s", s, name);
1466   sprintf(P->dual_name, "%s-gonal %s", s, dual_name);
1467   free(s);
1468   return 1;
1469 }
1470
1471 /*
1472  * Solve the fundamental right spherical triangles.
1473  * If need_approx is set, print iterations on standard error.
1474  */
1475 static int
1476 newton(Polyhedron *P, int need_approx)
1477 {
1478   /*
1479    * First, we find initial approximations.
1480    */
1481   int j;
1482   double cosa;
1483   Malloc(P->gamma, P->N, double);
1484   if (P->N == 1) {
1485     P->gamma[0] = M_PI / P->m[0];
1486     return 1;
1487   }
1488   for (j = 0; j < P->N; j++)
1489     P->gamma[j] = M_PI / 2 - M_PI / P->n[j];
1490   errno = 0; /* may be non-zero from some reason */
1491   /*
1492    * Next, iteratively find closer approximations for gamma[0] and compute
1493    * other gamma[j]'s from Napier's equations.
1494    */
1495   if (need_approx)
1496     fprintf(stderr, "Solving %s\n", P->polyform);
1497   for (;;) {
1498     double delta = M_PI, sigma = 0;
1499     for (j = 0; j < P->N; j++) {
1500       if (need_approx)
1501         fprintf(stderr, "%-20.15f", P->gamma[j]);
1502       delta -= P->m[j] * P->gamma[j];
1503     }
1504     if (need_approx)
1505       printf("(%g)\n", delta);
1506     if (fabs(delta) < 11 * DBL_EPSILON)
1507       return 1;
1508     /* On a RS/6000, fabs(delta)/DBL_EPSILON may occilate between 8 and
1509      * 10. Reported by David W. Sanderson <dws@ssec.wisc.edu> */
1510     for (j = 0; j < P->N; j++)
1511       sigma += P->m[j] * tan(P->gamma[j]);
1512     P->gamma[0] += delta * tan(P->gamma[0]) / sigma;
1513     if (P->gamma[0] < 0 || P->gamma[0] > M_PI)
1514       Err("gamma out of bounds");
1515     cosa = cos(M_PI / P->n[0]) / sin(P->gamma[0]);
1516     for (j = 1; j < P->N; j++)
1517       P->gamma[j] = asin(cos(M_PI / P->n[j]) / cosa);
1518     if (errno)
1519       Err(strerror(errno));
1520   }
1521 }
1522
1523 /*
1524  * Postprocess pqr| where r has an even denominator (cf. Coxeter &al. Sec.9).
1525  * Remove the {2r} and add a retrograde {2p} and retrograde {2q}.
1526  */
1527 static int
1528 exceptions(Polyhedron *P)
1529 {
1530   int j;
1531   if (P->even != -1) {
1532     P->M = P->N = 4;
1533     Realloc(P->n, P->N, double);
1534     Realloc(P->m, P->N, double);
1535     Realloc(P->gamma, P->N, double);
1536     Realloc(P->rot, P->M, int);
1537     for (j = P->even + 1; j < 3; j++) {
1538       P->n[j-1] = P->n[j];
1539       P->gamma[j-1] = P->gamma[j];
1540     }
1541     P->n[2] = compl(P->n[1]);
1542     P->gamma[2] = - P->gamma[1];
1543     P->n[3] = compl(P->n[0]);
1544     P->m[3] = 1;
1545     P->gamma[3] = - P->gamma[0];
1546     P->rot[0] = 0;
1547     P->rot[1] = 1;
1548     P->rot[2] = 3;
1549     P->rot[3] = 2;
1550   }
1551
1552   /*
1553    * Postprocess the last polyhedron |3/2 5/3 3 5/2 by taking a |5/3 3 5/2,
1554    * replacing the three snub triangles by four equatorial squares and adding
1555    * the missing {3/2} (retrograde triangle, cf. Coxeter &al. Sec. 11).
1556    */
1557   if (P->index == last_uniform - 1) {
1558     P->N = 5;
1559     P->M = 8;
1560     Realloc(P->n, P->N, double);
1561     Realloc(P->m, P->N, double);
1562     Realloc(P->gamma, P->N, double);
1563     Realloc(P->rot, P->M, int);
1564     Realloc(P->snub, P->M, int);
1565     P->hemi = 1;
1566     P->D = 0;
1567     for (j = 3; j; j--) {
1568       P->m[j] = 1;
1569       P->n[j] = P->n[j-1];
1570       P->gamma[j] = P->gamma[j-1];
1571     }
1572     P->m[0] = P->n[0] = 4;
1573     P->gamma[0] = M_PI / 2;
1574     P->m[4] = 1;
1575     P->n[4] = compl(P->n[1]);
1576     P->gamma[4] = - P->gamma[1];
1577     for (j = 1; j < 6; j += 2) P->rot[j]++;
1578     P->rot[6] = 0;
1579     P->rot[7] = 4;
1580     P->snub[6] = 1;
1581     P->snub[7] = 0;
1582   }
1583   return 1;
1584 }
1585
1586 /*
1587  * Compute edge and face counts, and update D and chi.  Update D in the few
1588  * cases the density of the polyhedron is meaningful but different than the
1589  * density of the corresponding Schwarz triangle (cf. Coxeter &al., p. 418 and
1590  * p. 425).
1591  * In these cases, spherical faces of one type are concave (bigger than a
1592  * hemisphere), and the actual density is the number of these faces less the
1593  * computed density.  Note that if j != 0, the assignment gamma[j] = asin(...)
1594  * implies gamma[j] cannot be obtuse.  Also, compute chi for the only
1595  * non-Wythoffian polyhedron.
1596  */
1597 static int
1598 count(Polyhedron *P)
1599 {
1600   int j, temp;
1601   Malloc(P->Fi, P->N, int);
1602   for (j = 0; j < P->N; j++) {
1603     P->E += temp = P->V * numerator(P->m[j]);
1604     P->F += P->Fi[j] = temp / numerator(P->n[j]);
1605   }
1606   P->E /= 2;
1607   if (P->D && P->gamma[0] > M_PI / 2)
1608     P->D = P->Fi[0] - P->D;
1609   if (P->index == last_uniform - 1)
1610     P->chi = P->V - P->E + P->F;
1611   return 1;
1612 }
1613
1614 /*
1615  * Generate a printable vertex configuration symbol.
1616  */
1617 static int
1618 configuration(Polyhedron *P)
1619 {
1620   int j, len = 2;
1621   for (j = 0; j < P->M; j++) {
1622     char *s;
1623     Sprintfrac(s, P->n[P->rot[j]]);
1624     len += strlen (s) + 2;
1625     if (!j) {
1626       Malloc(P->config, len, char);
1627 /*      strcpy(P->config, "(");*/
1628       strcpy(P->config, "");
1629     } else {
1630       Realloc(P->config, len, char);
1631       strcat(P->config, ", ");
1632     }
1633     strcat(P->config, s);
1634     free(s);
1635   }
1636 /*  strcat (P->config, ")");*/
1637   if ((j = denominator (P->m[0])) != 1) {
1638     char s[MAXDIGITS + 2];
1639     sprintf(s, "/%d", j);
1640     Realloc(P->config, len + strlen (s), char);
1641     strcat(P->config, s);
1642   }
1643   return 1;
1644 }
1645
1646 /*
1647  * Compute polyhedron vertices and vertex adjecency lists.
1648  * The vertices adjacent to v[i] are v[adj[0][i], v[adj[1][i], ...
1649  * v[adj[M-1][i], ordered counterclockwise.  The algorith is a BFS on the
1650  * vertices, in such a way that the vetices adjacent to a givem vertex are
1651  * obtained from its BFS parent by a cyclic sequence of rotations. firstrot[i]
1652  * points to the first  rotaion in the sequence when applied to v[i]. Note that
1653  * for non-snub polyhedra, the rotations at a child are opposite in sense when
1654  * compared to the rotations at the parent. Thus, we fill adj[*][i] from the
1655  * end to signify clockwise rotations. The firstrot[] array is not needed for
1656  * display thus it is freed after being used for face computations below.
1657  */
1658 static int
1659 vertices(Polyhedron *P)
1660 {
1661   int i, newV = 2;
1662   double cosa;
1663   Malloc(P->v, P->V, Vector);
1664   Matalloc(P->adj, P->M, P->V, int);
1665   Malloc(P->firstrot, P->V, int); /* temporary , put in Polyhedron
1666                                      structure so that may be freed on
1667                                      error */
1668   cosa = cos(M_PI / P->n[0]) / sin(P->gamma[0]);
1669   P->v[0].x = 0;
1670   P->v[0].y = 0;
1671   P->v[0].z = 1;
1672   P->firstrot[0] = 0;
1673   P->adj[0][0] = 1;
1674   P->v[1].x = 2 * cosa * sqrt(1 - cosa * cosa);
1675   P->v[1].y = 0;
1676   P->v[1].z = 2 * cosa * cosa - 1;
1677   if (!P->snub) {
1678     P->firstrot[1] = 0;
1679     P->adj[0][1] = -1;/* start the other side */
1680     P->adj[P->M-1][1] = 0;
1681   } else {
1682     P->firstrot[1] = P->snub[P->M-1] ? 0 : P->M-1 ;
1683     P->adj[0][1] = 0;
1684   }
1685   for (i = 0; i < newV; i++) {
1686     int j, k;
1687     int last, one, start, limit;
1688     if (P->adj[0][i] == -1) {
1689       one = -1; start = P->M-2; limit = -1;
1690     } else {
1691       one = 1; start = 1; limit = P->M;
1692     }
1693     k = P->firstrot[i];
1694     for (j = start; j != limit; j += one) {
1695       Vector temp;
1696       int J;
1697       temp = rotate (P->v[P->adj[j-one][i]], P->v[i],
1698                      one * 2 * P->gamma[P->rot[k]]);
1699       for (J=0; J<newV && !same(P->v[J],temp,BIG_EPSILON); J++)
1700         ;/* noop */
1701       P->adj[j][i] = J;
1702       last = k;
1703       if (++k == P->M)
1704         k = 0;
1705       if (J == newV) { /* new vertex */
1706         if (newV == P->V) Err ("too many vertices");
1707         P->v[newV++] = temp;
1708         if (!P->snub) {
1709           P->firstrot[J] = k;
1710           if (one > 0) {
1711             P->adj[0][J] = -1;
1712             P->adj[P->M-1][J] = i;
1713           } else {
1714             P->adj[0][J] = i;
1715           }
1716         } else {
1717           P->firstrot[J] = !P->snub[last] ? last :
1718             !P->snub[k] ? (k+1)%P->M : k ;
1719           P->adj[0][J] = i;
1720         }
1721       }
1722     }
1723   }
1724   return 1;
1725 }
1726
1727 /*
1728  * Compute polyhedron faces (dual vertices) and incidence matrices.
1729  * For orientable polyhedra, we can distinguish between the two faces meeting
1730  * at a given directed edge and identify the face on the left and the face on
1731  * the right, as seen from the outside.  For one-sided polyhedra, the vertex
1732  * figure is a papillon (in Coxeter &al.  terminology, a crossed parallelogram)
1733  * and the two faces meeting at an edge can be identified as the side face
1734  * (n[1] or n[2]) and the diagonal face (n[0] or n[3]).
1735  */
1736 static int
1737 faces(Polyhedron *P)
1738 {
1739   int i, newF = 0;
1740   Malloc (P->f, P->F, Vector);
1741   Malloc (P->ftype, P->F, int);
1742   Matalloc (P->incid, P->M, P->V, int);
1743   P->minr = 1 / fabs (tan (M_PI / P->n[P->hemi]) * tan (P->gamma[P->hemi]));
1744   for (i = P->M; --i>=0;) {
1745     int j;
1746     for (j = P->V; --j>=0;)
1747       P->incid[i][j] = -1;
1748   }
1749   for (i = 0; i < P->V; i++) {
1750     int j;
1751     for (j = 0; j < P->M; j++) {
1752       int i0, J;
1753       int pap=0;/* papillon edge type */
1754       if (P->incid[j][i] != -1)
1755         continue;
1756       P->incid[j][i] = newF;
1757       if (newF == P->F)
1758         Err("too many faces");
1759       P->f[newF] = pole(P->minr, P->v[i], P->v[P->adj[j][i]],
1760                         P->v[P->adj[mod(j + 1, P->M)][i]]);
1761       P->ftype[newF] = P->rot[mod(P->firstrot[i] + ((P->adj[0][i] <
1762                                                      P->adj[P->M - 1][i])
1763                                                     ?  j
1764                                                     : -j - 2),
1765                                   P->M)];
1766       if (P->onesided)
1767         pap = (P->firstrot[i] + j) % 2;
1768       i0 = i;
1769       J = j;
1770       for (;;) {
1771         int k;
1772         k = i0;
1773         if ((i0 = P->adj[J][k]) == i) break;
1774         for (J = 0; J < P->M && P->adj[J][i0] != k; J++)
1775           ;/* noop */
1776         if (J == P->M)
1777           Err("too many faces");
1778         if (P->onesided && (J + P->firstrot[i0]) % 2 == pap) {
1779           P->incid [J][i0] = newF;
1780           if (++J >= P->M)
1781             J = 0;
1782         } else {
1783           if (--J < 0)
1784             J = P->M - 1;
1785           P->incid [J][i0] = newF;
1786         }
1787       }
1788       newF++;
1789     }
1790   }
1791   Free(P->firstrot);
1792   Free(P->rot);
1793   Free(P->snub);
1794   return 1;
1795 }
1796
1797 /*
1798  * Compute edge list and graph polyhedron and dual.
1799  * If the polyhedron is of the "hemi" type, each edge has one finite vertex and
1800  * one ideal vertex. We make sure the latter is always the out-vertex, so that
1801  * the edge becomes a ray (half-line).  Each ideal vertex is represented by a
1802  * unit Vector, and the direction of the ray is either parallel or
1803  * anti-parallel this Vector. We flag this in the array P->anti[E].
1804  */
1805 static int
1806 edgelist(Polyhedron *P)
1807 {
1808   int i, j, *s, *t, *u;
1809   Matalloc(P->e, 2, P->E, int);
1810   Matalloc(P->dual_e, 2, P->E, int);
1811   s = P->e[0];
1812   t = P->e[1];
1813   for (i = 0; i < P->V; i++)
1814     for (j = 0; j < P->M; j++)
1815       if (i < P->adj[j][i]) {
1816         *s++ = i;
1817         *t++ = P->adj[j][i];
1818       }
1819   s = P->dual_e[0];
1820   t = P->dual_e[1];
1821   if (!P->hemi)
1822     P->anti = 0;
1823   else
1824     Malloc(P->anti, P->E, int);
1825   u = P->anti;
1826   for (i = 0; i < P->V; i++)
1827     for (j = 0; j < P->M; j++)
1828       if (i < P->adj[j][i])
1829         {
1830           if (!u) {
1831             *s++ = P->incid[mod(j-1,P->M)][i];
1832             *t++ = P->incid[j][i];
1833           } else {
1834             if (P->ftype[P->incid[j][i]]) {
1835               *s = P->incid[j][i];
1836               *t = P->incid[mod(j-1,P->M)][i];
1837             } else {
1838               *s = P->incid[mod(j-1,P->M)][i];
1839               *t = P->incid[j][i];
1840             }
1841             *u++ = dot(P->f[*s++], P->f[*t++]) > 0;
1842           }
1843         }
1844   return 1;
1845 }
1846
1847
1848 static char *
1849 sprintfrac(double x)
1850 {
1851   char *s;
1852   frac (x);
1853   if (!frax.d) {
1854     Malloc(s, sizeof ("infinity"), char);
1855     strcpy(s, "infinity");
1856   } else if (frax.d == 1) {
1857     char n[MAXDIGITS + 1];
1858     sprintf(n, "%ld", frax.n);
1859     Malloc(s, strlen (n) +  1, char);
1860     strcpy(s, n);
1861   } else {
1862     char n[MAXDIGITS + 1], d[MAXDIGITS + 1];
1863     sprintf(n, "%ld", frax.n);
1864     sprintf(d, "%ld", frax.d);
1865     Malloc(s, strlen (n) + strlen (d) + 2, char);
1866     sprintf(s, "%s/%s", n, d);
1867   }
1868   return s;
1869 }
1870
1871 static double
1872 dot(Vector a, Vector b)
1873 {
1874   return a.x * b.x + a.y * b.y + a.z * b.z;
1875 }
1876
1877 static Vector
1878 scale(double k, Vector a)
1879 {
1880   a.x *= k;
1881   a.y *= k;
1882   a.z *= k;
1883   return a;
1884 }
1885
1886 static Vector
1887 diff(Vector a, Vector b)
1888 {
1889   a.x -= b.x;
1890   a.y -= b.y;
1891   a.z -= b.z;
1892   return a;
1893 }
1894
1895 static Vector
1896 cross(Vector a, Vector b)
1897 {
1898   Vector p;
1899   p.x = a.y * b.z - a.z * b.y;
1900   p.y = a.z * b.x - a.x * b.z;
1901   p.z = a.x * b.y - a.y * b.x;
1902   return p;
1903 }
1904
1905 static Vector
1906 sum(Vector a, Vector b)
1907 {
1908   a.x += b.x;
1909   a.y += b.y;
1910   a.z += b.z;
1911   return a;
1912 }
1913
1914 static Vector
1915 sum3(Vector a, Vector b, Vector c)
1916 {
1917   a.x += b.x + c.x;
1918   a.y += b.y + c.y;
1919   a.z += b.z + c.z;
1920   return a;
1921 }
1922
1923 static Vector
1924 rotate(Vector vertex, Vector axis, double angle)
1925 {
1926   Vector p;
1927   p = scale(dot (axis, vertex), axis);
1928   return sum3(p, scale(cos(angle), diff(vertex, p)),
1929               scale(sin(angle), cross(axis, vertex)));
1930 }
1931
1932 static Vector x, y, z;
1933
1934 /*
1935  * rotate the standard frame
1936  */
1937 static void
1938 rotframe(double azimuth, double elevation, double angle)
1939 {
1940   static const Vector X = {1,0,0}, Y = {0,1,0}, Z = {0,0,1};
1941   Vector axis;
1942
1943   axis = rotate(rotate (X, Y, elevation), Z, azimuth);
1944   x = rotate(X, axis, angle);
1945   y = rotate(Y, axis, angle);
1946   z = rotate(Z, axis, angle);
1947 }
1948
1949 /*
1950  * rotate an array of n Vectors
1951  */
1952 static void
1953 rotarray(Vector *new, Vector *old, int n)
1954 {
1955   while (n--) {
1956     *new++ = sum3(scale(old->x, x), scale(old->y, y), scale(old->z, z));
1957     old++;
1958   }
1959 }
1960
1961 static int
1962 same(Vector a, Vector b, double epsilon)
1963 {
1964   return fabs(a.x - b.x) < epsilon && fabs(a.y - b.y) < epsilon
1965     && fabs(a.z - b.z) < epsilon;
1966 }
1967
1968 /*
1969  * Compute the polar reciprocal of the plane containing a, b and c:
1970  *
1971  * If this plane does not contain the origin, return p such that
1972  *      dot(p,a) = dot(p,b) = dot(p,b) = r.
1973  *
1974  * Otherwise, return p such that
1975  *      dot(p,a) = dot(p,b) = dot(p,c) = 0
1976  * and
1977  *      dot(p,p) = 1.
1978  */
1979 static Vector
1980 pole(double r, Vector a, Vector b, Vector c)
1981 {
1982   Vector p;
1983   double k;
1984   p = cross(diff(b, a), diff(c, a));
1985   k = dot(p, a);
1986   if (fabs(k) < 1e-6)
1987     return scale(1 / sqrt(dot(p, p)), p);
1988   else
1989     return scale(r/ k , p);
1990 }
1991
1992 \f
1993 /* output */
1994
1995
1996
1997
1998 static void rotframe(double azimuth, double elevation, double angle);
1999 static void rotarray(Vector *new, Vector *old, int n);
2000 static int mod (int i, int j);
2001
2002
2003 static void
2004 push_point (polyhedron *p, Vector v)
2005 {
2006   p->points[p->npoints].x = v.x;
2007   p->points[p->npoints].y = v.y;
2008   p->points[p->npoints].z = v.z;
2009   p->npoints++;
2010 }
2011
2012 static void
2013 push_face3 (polyhedron *p, int x, int y, int z)
2014 {
2015   p->faces[p->nfaces].npoints = 3;
2016   Malloc (p->faces[p->nfaces].points, 3, int);
2017   p->faces[p->nfaces].points[0] = x;
2018   p->faces[p->nfaces].points[1] = y;
2019   p->faces[p->nfaces].points[2] = z;
2020   p->nfaces++;
2021 }
2022
2023 static void
2024 push_face4 (polyhedron *p, int x, int y, int z, int w)
2025 {
2026   p->faces[p->nfaces].npoints = 4;
2027   Malloc (p->faces[p->nfaces].points, 4, int);
2028   p->faces[p->nfaces].points[0] = x;
2029   p->faces[p->nfaces].points[1] = y;
2030   p->faces[p->nfaces].points[2] = z;
2031   p->faces[p->nfaces].points[3] = w;
2032   p->nfaces++;
2033 }
2034
2035
2036
2037
2038 static polyhedron *
2039 construct_polyhedron (Polyhedron *P, Vector *v, int V, Vector *f, int F,
2040                       char *name, char *dual, char *class, char *star,
2041                       double azimuth, double elevation, double freeze)
2042 {
2043   int i, j, k=0, l, ll, ii, *hit=0, facelets;
2044
2045   polyhedron *result;
2046   Vector *temp;
2047
2048   Malloc (result, 1, polyhedron);
2049   memset (result, 0, sizeof(*result));
2050
2051   /*
2052    * Rotate polyhedron
2053    */
2054   rotframe(azimuth, elevation, freeze);
2055   Malloc(temp, V, Vector);
2056   rotarray(temp, v, V);
2057   v = temp;
2058   Malloc(temp, F, Vector);
2059   rotarray(temp, f, F);
2060   f = temp;
2061
2062   result->number = P->index + 1;
2063   result->name = strdup (name);
2064   result->dual = strdup (dual);
2065   result->wythoff = strdup (P->polyform);
2066   result->config = strdup (P->config);
2067   result->group = strdup (P->group);
2068   result->class = strdup (class);
2069
2070   /*
2071    * Vertex list
2072    */
2073   Malloc (result->points, V + F * 13, point);
2074   result->npoints = 0;
2075
2076   result->nedges = P->E;
2077   result->logical_faces = F;
2078   result->logical_vertices = V;
2079   result->density = P->D;
2080   result->chi = P->chi;
2081
2082   for (i = 0; i < V; i++)
2083     push_point (result, v[i]);
2084
2085   /*
2086    * Auxiliary vertices (needed because current VRML browsers cannot handle
2087    * non-simple polygons, i.e., ploygons with self intersections): Each
2088    * non-simple face is assigned an auxiliary vertex. By connecting it to the
2089    * rest of the vertices the face is triangulated. The circum-center is used
2090    * for the regular star faces of uniform polyhedra. The in-center is used for
2091    * the pentagram (#79) and hexagram (#77) of the high-density snub duals, and
2092    * for the pentagrams (#40, #58) and hexagram (#52) of the stellated duals
2093    * with configuration (....)/2. Finally, the self-intersection of the crossed
2094    * parallelogram is used for duals with form p q r| with an even denominator.
2095    *
2096    * This method do not work for the hemi-duals, whose faces are not
2097    * star-shaped and have two self-intersections each.
2098    *
2099    * Thus, for each face we need six auxiliary vertices: The self intersections
2100    * and the terminal points of the truncations of the infinite edges. The
2101    * ideal vertices are listed, but are not used by the face-list.
2102    *
2103    * Note that the face of the last dual (#80) is octagonal, and constists of
2104    * two quadrilaterals of the infinite type.
2105    */
2106
2107   if (*star && P->even != -1)
2108     Malloc(hit, F, int);
2109   for (i = 0; i < F; i++)
2110     if ((!*star &&
2111          (frac(P->n[P->ftype[i]]), frax.d != 1 && frax.d != frax.n - 1)) ||
2112         (*star &&
2113          P->K == 5 &&
2114          (P->D > 30 ||
2115           denominator (P->m[0]) != 1))) {
2116       /* find the center of the face */
2117       double h;
2118       if (!*star && P->hemi && !P->ftype[i])
2119         h = 0;
2120       else
2121         h = P->minr / dot(f[i],f[i]);
2122       push_point(result, scale (h, f[i]));
2123
2124     } else if (*star && P->even != -1) {
2125       /* find the self-intersection of a crossed parallelogram.
2126        * hit is set if v0v1 intersects v2v3*/
2127       Vector v0, v1, v2, v3, c0, c1, p;
2128       double d0, d1;
2129       v0 = v[P->incid[0][i]];
2130       v1 = v[P->incid[1][i]];
2131       v2 = v[P->incid[2][i]];
2132       v3 = v[P->incid[3][i]];
2133       d0 = sqrt(dot(diff(v0, v2), diff(v0, v2)));
2134       d1 = sqrt(dot (diff(v1, v3), diff(v1, v3)));
2135       c0 = scale(d1, sum(v0, v2));
2136       c1 = scale(d0, sum(v1, v3));
2137       p = scale(0.5 / (d0 + d1), sum(c0, c1));
2138       push_point (result, p);
2139       p = cross(diff(p, v2), diff(p, v3));
2140       hit[i] = (dot(p, p) < 1e-6);
2141     } else if (*star && P->hemi && P->index != last_uniform - 1) {
2142       /* find the terminal points of the truncation and the
2143        * self-intersections.
2144        *  v23       v0       v21
2145        *  |  \     /  \     /  |
2146        *  |   v0123    v0321   |
2147        *  |  /     \  /     \  |
2148        *  v01       v2       v03
2149        */
2150       Vector v0, v1, v2, v3, v01, v03, v21, v23, v0123, v0321 ;
2151       Vector u;
2152       double t = 1.5;/* truncation adjustment factor */
2153       j = !P->ftype[P->incid[0][i]];
2154       v0 = v[P->incid[j][i]];/* real vertex */
2155       v1 = v[P->incid[j+1][i]];/* ideal vertex (unit vector) */
2156       v2 = v[P->incid[j+2][i]];/* real */
2157       v3 = v[P->incid[(j+3)%4][i]];/* ideal */
2158       /* compute intersections
2159        * this uses the following linear algebra:
2160        * v0123 = v0 + a v1 = v2 + b v3
2161        * v0 x v3 + a (v1 x v3) = v2 x v3
2162        * a (v1 x v3) = (v2 - v0) x v3
2163        * a (v1 x v3) . (v1 x v3) = (v2 - v0) x v3 . (v1 x v3)
2164        */
2165       u = cross(v1, v3);
2166       v0123 = sum(v0, scale(dot(cross(diff(v2, v0), v3), u) / dot(u,u),
2167                             v1));
2168       v0321 = sum(v0, scale(dot(cross(diff(v0, v2), v1), u) / dot(u,u),
2169                             v3));
2170       /* compute truncations */
2171       v01 = sum(v0 , scale(t, diff(v0123, v0)));
2172       v23 = sum(v2 , scale(t, diff(v0123, v2)));
2173       v03 = sum(v0 , scale(t, diff(v0321, v0)));
2174       v21 = sum(v2 , scale(t, diff(v0321, v2)));
2175
2176       push_point(result, v01);
2177       push_point(result, v23);
2178       push_point(result, v0123);
2179       push_point(result, v03);
2180       push_point(result, v21);
2181       push_point(result, v0321);
2182
2183     } else if (*star && P->index == last_uniform - 1) {
2184       /* find the terminal points of the truncation and the
2185        * self-intersections.
2186        *  v23       v0       v21
2187        *  |  \     /  \     /  |
2188        *  |   v0123    v0721   |
2189        *  |  /     \  /     \  |
2190        *  v01       v2       v07
2191        *
2192        *  v65       v4       v67
2193        *  |  \     /  \     /  |
2194        *  |   v4365    v4567   |
2195        *  |  /     \  /     \  |
2196        *  v43       v6       v45
2197        */
2198       Vector v0, v1, v2, v3, v4, v5, v6, v7, v01, v07, v21, v23;
2199       Vector v43, v45, v65, v67, v0123, v0721, v4365, v4567;
2200       double t = 1.5;/* truncation adjustment factor */
2201       Vector u;
2202       for (j = 0; j < 8; j++)
2203         if (P->ftype[P->incid[j][i]] == 3)
2204           break;
2205       v0 = v[P->incid[j][i]];/* real {5/3} */
2206       v1 = v[P->incid[(j+1)%8][i]];/* ideal */
2207       v2 = v[P->incid[(j+2)%8][i]];/* real {3} */
2208       v3 = v[P->incid[(j+3)%8][i]];/* ideal */
2209       v4 = v[P->incid[(j+4)%8][i]];/* real {5/2} */
2210       v5 = v[P->incid[(j+5)%8][i]];/* ideal */
2211       v6 = v[P->incid[(j+6)%8][i]];/* real {3/2} */
2212       v7 = v[P->incid[(j+7)%8][i]];/* ideal */
2213       /* compute intersections */
2214       u = cross(v1, v3);
2215       v0123 = sum(v0, scale(dot(cross(diff(v2, v0), v3), u) / dot(u,u),
2216                             v1));
2217       u = cross(v7, v1);
2218       v0721 = sum(v0, scale(dot(cross(diff(v2, v0), v1), u) / dot(u,u),
2219                             v7));
2220       u = cross(v5, v7);
2221       v4567 = sum(v4, scale(dot(cross(diff(v6, v4), v7), u) / dot(u,u),
2222                             v5));
2223       u = cross(v3, v5);
2224       v4365 = sum(v4, scale(dot(cross(diff(v6, v4), v5), u) / dot(u,u),
2225                             v3));
2226       /* compute truncations */
2227       v01 = sum(v0 , scale(t, diff(v0123, v0)));
2228       v23 = sum(v2 , scale(t, diff(v0123, v2)));
2229       v07 = sum(v0 , scale(t, diff(v0721, v0)));
2230       v21 = sum(v2 , scale(t, diff(v0721, v2)));
2231       v45 = sum(v4 , scale(t, diff(v4567, v4)));
2232       v67 = sum(v6 , scale(t, diff(v4567, v6)));
2233       v43 = sum(v4 , scale(t, diff(v4365, v4)));
2234       v65 = sum(v6 , scale(t, diff(v4365, v6)));
2235
2236       push_point(result, v01);
2237       push_point(result, v23);
2238       push_point(result, v0123);
2239       push_point(result, v07);
2240       push_point(result, v21);
2241       push_point(result, v0721);
2242       push_point(result, v45);
2243       push_point(result, v67);
2244       push_point(result, v4567);
2245       push_point(result, v43);
2246       push_point(result, v65);
2247       push_point(result, v4365);
2248     }
2249
2250   /*
2251    * Face list:
2252    * Each face is printed in a separate line, by listing the indices of its
2253    * vertices. In the non-simple case, the polygon is represented by the
2254    * triangulation, each triangle consists of two polyhedron vertices and one
2255    * auxiliary vertex.
2256    */
2257   Malloc (result->faces, F * 10, face);
2258   result->nfaces = 0;
2259
2260   ii = V;
2261   facelets = 0;
2262   for (i = 0; i < F; i++) {
2263     if (*star) {
2264       if (P->K == 5 &&
2265           (P->D > 30 ||
2266            denominator (P->m[0]) != 1)) {
2267         for (j = 0; j < P->M - 1; j++) {
2268           push_face3 (result, P->incid[j][i], P->incid[j+1][i],  ii);
2269           facelets++;
2270         }
2271
2272         push_face3 (result, P->incid[j][i], P->incid[0][i],  ii++);
2273         facelets++;
2274
2275       } else if (P->even != -1) {
2276         if (hit && hit[i]) {
2277           push_face3 (result, P->incid[3][i], P->incid[0][i],  ii);
2278           push_face3 (result, P->incid[1][i], P->incid[2][i],  ii);
2279         } else {
2280           push_face3 (result, P->incid[0][i], P->incid[1][i],  ii);
2281           push_face3 (result, P->incid[2][i], P->incid[3][i],  ii);
2282         }
2283         ii++;
2284         facelets += 2;
2285
2286       } else if (P->hemi && P->index != last_uniform - 1) {
2287         j = !P->ftype[P->incid[0][i]];
2288
2289         push_face3 (result, ii, ii + 1, ii + 2);
2290         push_face4 (result, P->incid[j][i], ii + 2, P->incid[j+2][i], ii + 5);
2291         push_face3 (result, ii + 3, ii + 4, ii + 5);
2292         ii += 6;
2293         facelets += 3;
2294       } else if (P->index == last_uniform - 1) {
2295         for (j = 0; j < 8; j++)
2296           if (P->ftype[P->incid[j][i]] == 3)
2297             break;
2298         push_face3 (result, ii, ii + 1, ii + 2);
2299         push_face4 (result,
2300                     P->incid[j][i], ii + 2, P->incid[(j+2)%8][i], ii + 5);
2301         push_face3 (result, ii + 3, ii + 4, ii + 5);
2302
2303         push_face3 (result, ii + 6, ii + 7, ii + 8);
2304         push_face4 (result,
2305                     P->incid[(j+4)%8][i], ii + 8, P->incid[(j+6)%8][i],
2306                     ii + 11);
2307         push_face3 (result, ii + 9, ii + 10, ii + 11);
2308         ii += 12;
2309         facelets += 6;
2310       } else {
2311
2312         result->faces[result->nfaces].npoints = P->M;
2313         Malloc (result->faces[result->nfaces].points, P->M, int);
2314         for (j = 0; j < P->M; j++)
2315           result->faces[result->nfaces].points[j] = P->incid[j][i];
2316         result->nfaces++;
2317         facelets++;
2318       }
2319     } else {
2320       int split = (frac(P->n[P->ftype[i]]),
2321                    frax.d != 1 && frax.d != frax.n - 1);
2322       for (j = 0; j < V; j++) {
2323         for (k = 0; k < P->M; k++)
2324           if (P->incid[k][j] == i)
2325             break;
2326         if (k != P->M)
2327           break;
2328       }
2329       if (split) {
2330         ll = j;
2331         for (l = P->adj[k][j]; l != j; l = P->adj[k][l]) {
2332           for (k = 0; k < P->M; k++)
2333             if (P->incid[k][l] == i)
2334               break;
2335           if (P->adj[k][l] == ll)
2336             k = mod(k + 1 , P->M);
2337           push_face3 (result, ll, l, ii);
2338           facelets++;
2339           ll = l;
2340         }
2341         push_face3 (result, ll, j, ii++);
2342         facelets++;
2343
2344       } else {
2345
2346         int *pp;
2347         int pi = 0;
2348         Malloc (pp, 100, int);
2349
2350         pp[pi++] = j;
2351         ll = j;
2352         for (l = P->adj[k][j]; l != j; l = P->adj[k][l]) {
2353           for (k = 0; k < P->M; k++)
2354             if (P->incid[k][l] == i)
2355               break;
2356           if (P->adj[k][l] == ll)
2357             k = mod(k + 1 , P->M);
2358           pp[pi++] = l;
2359           ll = l;
2360         }
2361         result->faces[result->nfaces].npoints = pi;
2362         result->faces[result->nfaces].points = pp;
2363         result->nfaces++;
2364         facelets++;
2365       }
2366     }
2367   }
2368
2369   /*
2370    * Face color indices - for polyhedra with multiple face types
2371    * For non-simple faces, the index is repeated as many times as needed by the
2372    * triangulation.
2373    */
2374   {
2375     int ff = 0;
2376     if (!*star && P->N != 1) {
2377       for (i = 0; i < F; i++)
2378         if (frac(P->n[P->ftype[i]]), frax.d == 1 || frax.d == frax.n - 1)
2379           result->faces[ff++].color = P->ftype[i];
2380         else
2381           for (j = 0; j < frax.n; j++)
2382             result->faces[ff++].color = P->ftype[i];
2383     } else {
2384       for (i = 0; i < facelets; i++)
2385         result->faces[ff++].color = 0;
2386     }
2387   }
2388
2389   if (*star && P->even != -1)
2390     free(hit);
2391   free(v);
2392   free(f);
2393
2394   return result;
2395 }
2396
2397
2398 \f
2399 /* External interface (jwz)
2400  */
2401
2402 void
2403 free_polyhedron (polyhedron *p)
2404 {
2405   if (!p) return;
2406   Free (p->wythoff);
2407   Free (p->name);
2408   Free (p->group);
2409   Free (p->class);
2410   if (p->faces)
2411     {
2412       int i;
2413       for (i = 0; i < p->nfaces; i++)
2414         Free (p->faces[i].points);
2415       Free (p->faces);
2416     }
2417   Free (p);
2418 }
2419
2420
2421 int
2422 construct_polyhedra (polyhedron ***polyhedra_ret)
2423 {
2424   double freeze = 0;
2425   double azimuth = AZ;
2426   double elevation = EL;
2427   int index = 0;
2428
2429   int count = 0;
2430   polyhedron **result;
2431   Malloc (result, last_uniform * 2 + 3, polyhedron*);
2432
2433   while (index < last_uniform) {
2434     char sym[4];
2435     Polyhedron *P;
2436
2437     sprintf(sym, "#%d", index + 1);
2438     if (!(P = kaleido(sym, 1, 0, 0, 0))) {
2439       Err (strerror(errno));
2440     }
2441
2442     result[count++] = construct_polyhedron (P, P->v, P->V, P->f, P->F,
2443                                             P->name, P->dual_name,
2444                                             P->class, "",
2445                                             azimuth, elevation, freeze);
2446
2447     result[count++] = construct_polyhedron (P, P->f, P->F, P->v, P->V,
2448                                             P->dual_name, P->name,
2449                                             P->dual_class, "*",
2450                                             azimuth, elevation, freeze);
2451     polyfree(P);
2452     index++;
2453   }
2454
2455   *polyhedra_ret = result;
2456   count++; /* leave room for teapot */
2457   return count;
2458 }