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