36246176aac7a25f695db27a18559487da933790
[xscreensaver] / hacks / glx / dnalogo.c
1 /* DNA Logo, Copyright (c) 2001-2017 Jamie Zawinski <jwz@jwz.org>
2  *
3  *      DNA Lounge
4  *
5  *      Restaurant -- Bar -- Nightclub -- Cafe -- Est. 1985.
6  *
7  *      375 Eleventh Street
8  *      San Francisco, CA
9  *      94103
10  *
11  *      https://www.dnalounge.com/
12  *      http://www.dnapizza.com/
13  *
14  * Permission to use, copy, modify, distribute, and sell this software and its
15  * documentation for any purpose is hereby granted without fee, provided that
16  * the above copyright notice appear in all copies and that both that
17  * copyright notice and this permission notice appear in supporting
18  * documentation.  No representations are made about the suitability of this
19  * software for any purpose.  It is provided "as is" without express or 
20  * implied warranty.
21  */
22
23 #define DEFAULTS        __extension__ \
24                         "*delay:            25000   \n" \
25                         "*showFPS:          False   \n" \
26                         "*wireframe:        False   \n" \
27                         "*doGasket:         True    \n" \
28                         "*doHelix:          True    \n" \
29                         "*doLadder:         True    \n" \
30                         "*doFrame:          True    \n" \
31                         "*wallFacets:       360     \n" \
32                         "*barFacets:        90      \n" \
33                         "*clockwise:        False   \n" \
34                         "*turns:            0.69    \n" \
35                         "*turnSpacing:      2.2     \n" \
36                         "*barSpacing:       0.24    \n" \
37                         "*wallHeight:       0.45    \n" \
38                         "*wallThickness:    0.12    \n" \
39                         "*barThickness:     0.058   \n" \
40                         "*wallTaper:        0.95    \n" \
41                         "*gasketSize:       1.88    \n" \
42                         "*gasketDepth:      0.15    \n" \
43                         "*gasketThickness:  0.4     \n" \
44                         "*frameSize:        1.20    \n" \
45                         "*frameDepth:       0.01    \n" \
46                         "*frameThickness:   0.03    \n" \
47                         "*triangleSize:     0.045   \n" \
48                         "*cwFacets:         3       \n" \
49                         "*cwDiscFacets:     64      \n" \
50                         "*cwSpread:         0.5     \n" \
51                         "*cwLineWidth:      0.18    \n" \
52                         "*cwThickness:      0.15    \n" \
53                         "*cwCapSize:        0.4     \n" \
54                         "*text:             CODEWORD\n" \
55                         "*speed:            1.0     \n" \
56                         "*mode:             both"  "\n" \
57                         ".background:       #000000\n" \
58                         ".foreground:       #00AA00\n" \
59                         ".cwForeground:     #FCA816\n" \
60                         ".cwBackground:     #943225\n" \
61                         "*cwFont:           " CWFONT "\n" \
62                         "*geometry:         =640x640\n" \
63
64 # if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
65 #  define CWFONT "Yearling 28, OCR A Std 24"
66 # else
67 #  define CWFONT "-*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*"
68 # endif
69
70 # define free_logo 0
71 # define release_logo 0
72 #undef countof
73 #define countof(x) (sizeof((x))/sizeof((*x)))
74
75 #undef DXF_OUTPUT_HACK
76
77 #ifdef DXF_OUTPUT_HACK   /* When this is defined, instead of rendering
78                             to the screen, we write a DXF CAD file to stdout.
79                             This is a kludge of shocking magnitude...
80                             Maybe there's some other way to intercept all
81                             glVertex3f calls than with a #define? */
82 # define unit_tube dxf_unit_tube
83 # define unit_cone dxf_unit_cone
84 # define tube_1    dxf_tube_1
85 # define tube      dxf_tube
86 # define cone      dxf_cone
87 #endif /* DXF_OUTPUT_HACK */
88
89 #include "xlockmore.h"
90 #include "normals.h"
91 #include "tube.h"
92 #include "sphere.h"
93 #include "rotator.h"
94 #include "gltrackball.h"
95 #include "utf8wc.h"
96 #include "texfont.h"
97
98 #ifdef USE_GL /* whole file */
99
100 #ifdef HAVE_JWZGLES
101 # include "dnapizza.h"
102 #else
103 # define HAVE_TESS
104 #endif
105
106 typedef enum {
107   HELIX_IN, HELIX, HELIX_OUT,
108   PIZZA_IN, PIZZA, PIZZA_OUT,
109   HELIX_AND_PIZZA,
110 # ifdef CW
111   CODEWORD_IN, CODEWORD, CODEWORD_OUT, CODEWORD_BLANK
112 # endif
113 } glyph_mode;
114
115 typedef struct {
116   Bool spinning_p;
117   GLfloat position;             /* 0.0 - 1.0 */
118   GLfloat position_eased;       /* 0.0 - 1.0, eased in and out */
119   GLfloat easement;             /* portion of path that is eased. <= 0.5 */
120   GLfloat speed;                /* how far along the path (may be negative) */
121   GLfloat probability;          /* relative likelyhood to start spinning */
122 } spinner;
123
124 typedef struct {
125   GLXContext *glx_context;
126
127   GLuint helix_list,  helix_list_wire,  helix_list_facetted;
128   GLuint pizza_list,  pizza_list_wire,  pizza_list_facetted;
129   GLuint gasket_list, gasket_list_wire;
130   GLuint frame_list,  frame_list_wire;
131   int polys[7];
132
133   int wall_facets;
134   int bar_facets;
135   Bool clockwise;
136   GLfloat color[4];
137
138   GLfloat turns;
139   GLfloat turn_spacing;
140   GLfloat bar_spacing;
141   GLfloat wall_height;
142   GLfloat wall_thickness;
143   GLfloat bar_thickness;
144   GLfloat wall_taper;
145
146   GLfloat gasket_size;
147   GLfloat gasket_depth;
148   GLfloat gasket_thickness;
149
150   GLfloat frame_size;
151   GLfloat frame_depth;
152   GLfloat frame_thickness;
153   GLfloat triangle_size;
154
155 # ifdef CW
156   int codeword_facets, codeword_disc_facets;
157   GLfloat codeword_spread, codeword_line_width, codeword_thickness;
158   GLfloat codeword_cap_size;
159   const char *codeword_text;
160   char *codeword_text_out;
161   int *codeword_text_points;
162   XYZ *codeword_path;
163   int codeword_path_npoints;
164   int codeword_nguides;
165   XYZ *codeword_guides;
166   GLfloat codeword_color[4], codeword_bg[4];
167   texture_font_data *font;
168 # endif
169
170   GLfloat speed;
171   glyph_mode mode;
172   glyph_mode anim_state;
173   GLfloat anim_ratio;
174
175   spinner gasket_spinnerx, gasket_spinnery, gasket_spinnerz;
176   spinner scene_spinnerx,  scene_spinnery;      /* for DNA */
177 # ifdef CW
178   rotator *scene_rot;                           /* for Codeword */
179 # endif
180   spinner helix_spinnerz;
181   spinner pizza_spinnery, pizza_spinnerz;
182   spinner frame_spinner;
183
184   trackball_state *trackball;
185   Bool button_down_p;
186
187   int wire_overlay;  /* frame countdown */
188
189 } logo_configuration;
190
191 static logo_configuration *dcs = NULL;
192
193 static XrmOptionDescRec opts[] = {
194   { "-speed",    ".speed",  XrmoptionSepArg, 0          },
195   { "-mode",     ".mode",   XrmoptionSepArg, 0          },
196   { "-pizza",    ".mode",   XrmoptionNoArg,  "pizza"    },
197   { "-helix",    ".mode",   XrmoptionNoArg,  "helix"    },
198   { "-both",     ".mode",   XrmoptionNoArg,  "both"     },
199 # ifdef CW
200   { "-codeword", ".mode",   XrmoptionNoArg,  "codeword" },
201   { "-cw",       ".mode",   XrmoptionNoArg,  "codeword" },
202   { "-text",     ".text",   XrmoptionSepArg, 0          },
203 # endif
204 };
205
206 ENTRYPOINT ModeSpecOpt logo_opts = {countof(opts), opts, 0, NULL, NULL};
207
208 #define PROBABILITY_SCALE 600
209
210
211 #ifdef DXF_OUTPUT_HACK
212
213 # define glBegin         dxf_glBegin
214 # define glVertex3f      dxf_glVertex3f
215 # define glVertex3dv     dxf_glVertex3dv
216 # define glEnd           dxf_glEnd
217 # define glVertexPointer dxf_glVertexPointer
218 # define glDrawArrays    dxf_glDrawArrays
219
220 static int dxf_type, dxf_point, dxf_point_total, dxf_layer, dxf_color;
221 static GLfloat dxf_quads[4*4];
222
223 static void
224 dxf_glBegin (int type)
225 {
226   dxf_type = type; 
227   dxf_point = 0;
228   dxf_point_total = 0;
229 }
230
231 static void
232 dxf_glVertex3f (GLfloat ox, GLfloat oy, GLfloat oz)
233 {
234   int i = 0;
235   GLfloat m[4*4];
236   GLfloat x, y, z;
237
238   /* Transform the point into modelview space. */
239   glGetFloatv (GL_MODELVIEW_MATRIX, m);
240   x = ox * m[0] + oy * m[4] + oz * m[8]  + m[12];
241   y = ox * m[1] + oy * m[5] + oz * m[9]  + m[13];
242   z = ox * m[2] + oy * m[6] + oz * m[10] + m[14];
243
244   dxf_quads[dxf_point*3+0] = x;
245   dxf_quads[dxf_point*3+1] = y;
246   dxf_quads[dxf_point*3+2] = z;
247   dxf_point++;
248   dxf_point_total++;
249
250   switch (dxf_type) {
251   case GL_QUADS:
252     if (dxf_point < 4) return;
253
254     fprintf (stdout, "0\n3DFACE\n8\n%d\n62\n%d\n", dxf_layer, dxf_color);
255     fprintf (stdout, "10\n%.6f\n", dxf_quads[i++]);
256     fprintf (stdout, "20\n%.6f\n", dxf_quads[i++]);
257     fprintf (stdout, "30\n%.6f\n", dxf_quads[i++]);
258
259     fprintf (stdout, "11\n%.6f\n", dxf_quads[i++]);
260     fprintf (stdout, "21\n%.6f\n", dxf_quads[i++]);
261     fprintf (stdout, "31\n%.6f\n", dxf_quads[i++]);
262
263     fprintf (stdout, "12\n%.6f\n", dxf_quads[i++]);
264     fprintf (stdout, "22\n%.6f\n", dxf_quads[i++]);
265     fprintf (stdout, "32\n%.6f\n", dxf_quads[i++]);
266
267     fprintf (stdout, "13\n%.6f\n", dxf_quads[i++]);
268     fprintf (stdout, "23\n%.6f\n", dxf_quads[i++]);
269     fprintf (stdout, "33\n%.6f\n", dxf_quads[i++]);
270     dxf_point = 0;
271     break;
272
273   case GL_QUAD_STRIP:
274     if (dxf_point < 4) return;
275
276     fprintf (stdout, "0\n3DFACE\n8\n%d\n62\n%d\n", dxf_layer, dxf_color);
277     fprintf (stdout, "10\n%.6f\n", dxf_quads[i++]);
278     fprintf (stdout, "20\n%.6f\n", dxf_quads[i++]);
279     fprintf (stdout, "30\n%.6f\n", dxf_quads[i++]);
280
281     fprintf (stdout, "11\n%.6f\n", dxf_quads[i++]);
282     fprintf (stdout, "21\n%.6f\n", dxf_quads[i++]);
283     fprintf (stdout, "31\n%.6f\n", dxf_quads[i++]);
284
285     fprintf (stdout, "12\n%.6f\n", dxf_quads[i+3]);  /* funky quad strip */
286     fprintf (stdout, "22\n%.6f\n", dxf_quads[i+4]);  /* vert order: 1243. */
287     fprintf (stdout, "32\n%.6f\n", dxf_quads[i+5]);
288
289     fprintf (stdout, "13\n%.6f\n", dxf_quads[i]);
290     fprintf (stdout, "23\n%.6f\n", dxf_quads[i+1]);
291     fprintf (stdout, "33\n%.6f\n", dxf_quads[i+2]);
292     i += 6;
293
294     dxf_quads[0] = dxf_quads[6];        /* copy point 3 to pos 1 */
295     dxf_quads[1] = dxf_quads[7];
296     dxf_quads[2] = dxf_quads[8];
297     dxf_quads[3] = dxf_quads[9];        /* copy point 4 to pos 2 */
298     dxf_quads[4] = dxf_quads[10];
299     dxf_quads[5] = dxf_quads[11];
300     dxf_point = 2;                      /* leave those two points in queue */
301     break;
302
303   case GL_TRIANGLES:
304   case GL_TRIANGLE_FAN:
305     if (dxf_point < 3) return;
306
307     fprintf (stdout, "0\n3DFACE\n8\n%d\n62\n%d\n", dxf_layer, dxf_color);
308     fprintf (stdout, "10\n%.6f\n", dxf_quads[i++]);
309     fprintf (stdout, "20\n%.6f\n", dxf_quads[i++]);
310     fprintf (stdout, "30\n%.6f\n", dxf_quads[i++]);
311
312     fprintf (stdout, "11\n%.6f\n", dxf_quads[i++]);
313     fprintf (stdout, "21\n%.6f\n", dxf_quads[i++]);
314     fprintf (stdout, "31\n%.6f\n", dxf_quads[i++]);
315
316     fprintf (stdout, "12\n%.6f\n", dxf_quads[i++]);
317     fprintf (stdout, "22\n%.6f\n", dxf_quads[i++]);
318     fprintf (stdout, "32\n%.6f\n", dxf_quads[i++]);
319
320     i -= 3;
321     fprintf (stdout, "13\n%.6f\n", dxf_quads[i++]);  /* dup pt 4 as pt 3. */
322     fprintf (stdout, "23\n%.6f\n", dxf_quads[i++]);
323     fprintf (stdout, "33\n%.6f\n", dxf_quads[i++]);
324
325     dxf_point = 0;
326     if (dxf_type == GL_TRIANGLE_FAN)
327       {
328         dxf_quads[3] = dxf_quads[6];    /* copy point 3 to point 2 */
329         dxf_quads[4] = dxf_quads[7];
330         dxf_quads[5] = dxf_quads[8];
331         dxf_point = 2;                  /* leave two points in queue */
332       }
333     break;
334
335   case GL_TRIANGLE_STRIP:
336     if (dxf_point < 3) return;
337
338     fprintf (stdout, "0\n3DFACE\n8\n%d\n62\n%d\n", dxf_layer, dxf_color);
339
340     fprintf (stdout, "10\n%.6f\n", dxf_quads[i++]);
341     fprintf (stdout, "20\n%.6f\n", dxf_quads[i++]);
342     fprintf (stdout, "30\n%.6f\n", dxf_quads[i++]);
343
344     fprintf (stdout, "11\n%.6f\n", dxf_quads[i++]);
345     fprintf (stdout, "21\n%.6f\n", dxf_quads[i++]);
346     fprintf (stdout, "31\n%.6f\n", dxf_quads[i++]);
347
348     fprintf (stdout, "12\n%.6f\n", dxf_quads[i++]);
349     fprintf (stdout, "22\n%.6f\n", dxf_quads[i++]);
350     fprintf (stdout, "32\n%.6f\n", dxf_quads[i++]);
351
352     i -= 3;
353     fprintf (stdout, "13\n%.6f\n", dxf_quads[i++]);  /* dup pt 4 as pt 3. */
354     fprintf (stdout, "23\n%.6f\n", dxf_quads[i++]);
355     fprintf (stdout, "33\n%.6f\n", dxf_quads[i++]);
356
357     dxf_quads[0] = dxf_quads[3];        /* copy point 2 to pos 1 */
358     dxf_quads[1] = dxf_quads[4];
359     dxf_quads[2] = dxf_quads[5];
360     dxf_quads[3] = dxf_quads[6];        /* copy point 3 to pos 2 */
361     dxf_quads[4] = dxf_quads[7];
362     dxf_quads[5] = dxf_quads[8];
363     dxf_point = 2;                      /* leave those two points in queue */
364     break;
365
366   case GL_LINES:
367   case GL_LINE_STRIP:
368   case GL_LINE_LOOP:
369
370     if (dxf_point_total == 1)
371       {
372         dxf_quads[6] = ox;
373         dxf_quads[7] = oy;
374         dxf_quads[8] = oz;
375       }
376
377     if (dxf_point < 2) return;
378
379     fprintf (stdout, "0\nLINE\n8\n%d\n62\n%d\n", dxf_layer, dxf_color);
380
381     fprintf (stdout, "10\n%.6f\n", dxf_quads[i++]);
382     fprintf (stdout, "20\n%.6f\n", dxf_quads[i++]);
383     fprintf (stdout, "30\n%.6f\n", dxf_quads[i++]);
384
385     fprintf (stdout, "11\n%.6f\n", dxf_quads[i++]);
386     fprintf (stdout, "21\n%.6f\n", dxf_quads[i++]);
387     fprintf (stdout, "31\n%.6f\n", dxf_quads[i++]);
388
389     dxf_point = 0;
390     if (dxf_type != GL_LINES)
391       {
392         dxf_quads[0] = dxf_quads[3];
393         dxf_quads[1] = dxf_quads[4];
394         dxf_quads[2] = dxf_quads[5];
395         dxf_point = 1;
396       }
397     break;
398
399   default:
400     abort();
401     break;
402   }
403 }
404
405
406 static void
407 dxf_glVertex3dv (const GLdouble *v)
408 {
409   glVertex3f (v[0], v[1], v[2]);
410 }
411
412
413 static void
414 dxf_glEnd(void)
415 {
416   if (dxf_type == GL_LINE_LOOP)  /* close loop */
417     glVertex3f (dxf_quads[6], dxf_quads[7], dxf_quads[8]);
418   dxf_type = -1;
419   dxf_point = 0;
420   dxf_point_total = 0;
421 }
422
423
424 static void
425 dxf_start (void)
426 {
427   fprintf (stdout, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
428   fprintf (stdout, "0\nSECTION\n2\nENTITIES\n");
429 }
430
431 static void
432 dxf_end (void)
433 {
434   fprintf (stdout, "0\nENDSEC\n0\nEOF\n");
435   exit (0);
436 }
437
438
439 static const GLvoid *dxf_vp;
440 static GLsizei dxf_vp_size;
441 static GLsizei dxf_vp_stride;
442
443 static void
444 dxf_glVertexPointer (GLint size, GLenum type, GLsizei stride,
445                      const GLvoid *pointer)
446 {
447   if (type != GL_FLOAT) abort();
448   if (stride <= 0) abort();
449   dxf_vp = pointer;
450   dxf_vp_size = size;
451   dxf_vp_stride = stride;
452 }
453
454 static void
455 dxf_glDrawArrays (GLenum mode, GLint first, GLsizei count)
456 {
457   int i;
458   unsigned char *a = (unsigned char *) dxf_vp;
459   dxf_glBegin (mode);
460   for (i = first; i < first+count; i++)
461     {
462       GLfloat *fa = (GLfloat *) a;
463       dxf_glVertex3f (fa[0], fa[1], fa[2]);
464       a += dxf_vp_stride;
465     }
466   dxf_glEnd();
467 }
468
469
470 # define XYZ tube_XYZ   /* avoid conflict with normals.h */
471 # include "tube.c"      /* Yes, I really am including a C file. */
472 # undef XYZ
473 # define XYZ sphere_XYZ
474 # define unit_sphere unit_sphere_dxf
475 # define unit_dome unit_dome_dxf
476 # include "sphere.c"
477 # undef XYZ
478
479 #endif /* DXF_OUTPUT_HACK */
480
481
482 \f
483 /* Calculate the angle (in radians) between two vectors.
484  */
485 static GLfloat
486 vector_angle (double ax, double ay, double az,
487               double bx, double by, double bz)
488 {
489   double La = sqrt (ax*ax + ay*ay + az*az);
490   double Lb = sqrt (bx*bx + by*by + bz*bz);
491   double cc, angle;
492
493   if (La == 0 || Lb == 0) return 0;
494   if (ax == bx && ay == by && az == bz) return 0;
495
496   /* dot product of two vectors is defined as:
497        La * Lb * cos(angle between vectors)
498      and is also defined as:
499        ax*bx + ay*by + az*bz
500      so:
501       La * Lb * cos(angle) = ax*bx + ay*by + az*bz
502       cos(angle)  = (ax*bx + ay*by + az*bz) / (La * Lb)
503       angle = acos ((ax*bx + ay*by + az*bz) / (La * Lb));
504   */
505   cc = (ax*bx + ay*by + az*bz) / (La * Lb);
506   if (cc > 1) cc = 1;  /* avoid fp rounding error (1.000001 => sqrt error) */
507   angle = acos (cc);
508
509   return (angle);
510 }
511
512
513 # ifdef CW
514
515 static void
516 normalize (XYZ *p)
517 {
518   GLfloat d = sqrt (p->x*p->x + p->y*p->y + p->z*p->z);
519   if (d != 0)
520     {
521       p->x /= d;
522       p->y /= d;
523       p->z /= d;
524     }
525   else
526     {
527       p->x = p->y = p->z = 0;
528     }
529 }
530
531
532 static double
533 dot (const XYZ u, const XYZ v)
534 {
535   return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
536 }
537
538 #endif /* CW */
539
540 \f
541 /* Make the helix
542  */
543
544 static int
545 make_helix (logo_configuration *dc, int facetted, int wire)
546 {
547   int polys = 0;
548   int wall_facets = dc->wall_facets / (facetted ? 10 : 1);
549   GLfloat th;
550   GLfloat max_th = M_PI * 2 * dc->turns;
551   GLfloat th_inc = M_PI * 2 / wall_facets;
552
553   GLfloat x1=0,  y1=0,  x2=0,  y2=0;
554   GLfloat x1b=0, y1b=0, x2b=0, y2b=0;
555   GLfloat z1=0, z2=0;
556   GLfloat h1=0, h2=0;
557   GLfloat h1off=0, h2off=0;
558   GLfloat z_inc = dc->turn_spacing / wall_facets;
559
560   th  = 0;
561   x1  = 1;
562   y1  = 0;
563   x1b = 1 - dc->wall_thickness;
564   y1b = 0;
565
566   z1 = -(dc->turn_spacing * dc->turns / 2);
567
568   h1    = (dc->wall_taper > 0 ? 0 : dc->wall_height / 2);
569   h1off = (dc->wall_taper > 0 ?    -dc->wall_height / 2 : 0);
570
571   if (!dc->clockwise)
572     z1 = -z1, z_inc = -z_inc, h1off = -h1off;
573
574   /* Leading end-cap
575    */
576   if (!wire && h1 > 0)
577     {
578       GLfloat nx, ny;
579       glFrontFace(GL_CCW);
580       glBegin(GL_QUADS);
581       nx = cos (th + M_PI/2);
582       ny = sin (th + M_PI/2);
583       glNormal3f(nx, ny, 0);
584       glVertex3f( x1, y1,  z1 - h1 + h1off);
585       glVertex3f( x1, y1,  z1 + h1 + h1off);
586       glVertex3f(x1b, y1b, z1 + h1 + h1off);
587       glVertex3f(x1b, y1b, z1 - h1 + h1off);
588       polys++;
589       glEnd();
590     }
591
592   while (th + th_inc <= max_th)
593     {
594       th += th_inc;
595
596       x2 = cos (th);
597       y2 = sin (th);
598       z2 = z1 + z_inc;
599       x2b = x2 * (1 - dc->wall_thickness);
600       y2b = y2 * (1 - dc->wall_thickness);
601
602       h2 = h1;
603       h2off = h1off;
604
605       if (dc->wall_taper > 0)
606         {
607           h2off = 0;
608           if (th < dc->wall_taper)
609             {
610               h2 = dc->wall_height/2 * cos (M_PI / 2
611                                             * (1 - (th / dc->wall_taper)));
612               if (dc->clockwise)
613                 h2off = h2 - dc->wall_height/2;
614               else
615                 h2off = dc->wall_height/2 - h2;
616             }
617           else if (th >= max_th - dc->wall_taper)
618             {
619               if (th + th_inc > max_th) /* edge case: always come to a point */
620                 h2 = 0;
621               else
622                 h2 = dc->wall_height/2 * cos (M_PI / 2
623                                               * (1 - ((max_th - th)
624                                                       / dc->wall_taper)));
625               if (dc->clockwise)
626                 h2off = dc->wall_height/2 - h2;
627               else
628                 h2off = h2 - dc->wall_height/2;
629             }
630         }
631
632       /* outer face
633        */
634       glFrontFace(GL_CW);
635       glBegin(wire ? GL_LINES : GL_QUADS);
636       glNormal3f(x1, y1, 0);
637       glVertex3f(x1, y1, z1 - h1 + h1off);
638       glVertex3f(x1, y1, z1 + h1 + h1off);
639       glNormal3f(x2, y2, 0);
640       glVertex3f(x2, y2, z2 + h2 + h2off);
641       glVertex3f(x2, y2, z2 - h2 + h2off);
642       polys++;
643       glEnd();
644
645       /* inner face
646        */
647       glFrontFace(GL_CCW);
648       glBegin(wire ? GL_LINES : GL_QUADS);
649       glNormal3f(-x1b, -y1b, 0);
650       glVertex3f( x1b,  y1b, z1 - h1 + h1off);
651       glVertex3f( x1b,  y1b, z1 + h1 + h1off);
652       glNormal3f(-x2b, -y2b, 0);
653       glVertex3f( x2b,  y2b, z2 + h2 + h2off);
654       glVertex3f( x2b,  y2b, z2 - h2 + h2off);
655       polys++;
656       glEnd();
657
658       /* top face
659        */
660       glFrontFace(GL_CCW);
661       /* glNormal3f(0, 0, 1);*/
662       do_normal (x2,   y2,  z2 + h2 + h2off,
663                  x2b,  y2b, z2 + h2 + h2off,
664                  x1b,  y1b, z1 + h1 + h1off);
665       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
666       glVertex3f( x2,   y2,  z2 + h2 + h2off);
667       glVertex3f( x2b,  y2b, z2 + h2 + h2off);
668       glVertex3f( x1b,  y1b, z1 + h1 + h1off);
669       glVertex3f( x1,   y1,  z1 + h1 + h1off);
670       polys++;
671       glEnd();
672
673       /* bottom face
674        */
675       glFrontFace(GL_CCW);
676       do_normal ( x1,   y1,  z1 - h1 + h1off,
677                   x1b,  y1b, z1 - h1 + h1off,
678                   x2b,  y2b, z2 - h2 + h2off);
679       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
680       glNormal3f(0, 0, -1);
681       glVertex3f( x1,   y1,  z1 - h1 + h1off);
682       glVertex3f( x1b,  y1b, z1 - h1 + h1off);
683       glVertex3f( x2b,  y2b, z2 - h2 + h2off);
684       glVertex3f( x2,   y2,  z2 - h2 + h2off);
685       polys++;
686       glEnd();
687
688       x1 = x2;
689       y1 = y2;
690       x1b = x2b;
691       y1b = y2b;
692       z1 += z_inc;
693       h1 = h2;
694       h1off = h2off;
695     }
696
697   /* Trailing end-cap
698    */
699   if (!wire && h2 > 0)
700     {
701       GLfloat nx, ny;
702       glFrontFace(GL_CW);
703       glBegin(GL_QUADS);
704       nx = cos (th + M_PI/2);
705       ny = sin (th + M_PI/2);
706       glNormal3f(nx, ny, 0);
707       glVertex3f(x2,  y2,  z1 - h2 + h2off);
708       glVertex3f(x2,  y2,  z1 + h2 + h2off);
709       glVertex3f(x2b, y2b, z1 + h2 + h2off);
710       glVertex3f(x2b, y2b, z1 - h2 + h2off);
711       polys++;
712       glEnd();
713     }
714   return polys;
715 }
716
717
718 static int
719 make_ladder (logo_configuration *dc, int facetted, int wire)
720 {
721   int polys = 0;
722   GLfloat th;
723   GLfloat max_th = dc->turns * M_PI * 2;
724   GLfloat max_z  = dc->turns * dc->turn_spacing;
725   GLfloat z_inc  = dc->bar_spacing;
726   GLfloat th_inc = M_PI * 2 * (dc->bar_spacing / dc->turn_spacing);
727   GLfloat x, y, z;
728
729   /* skip forward to center the bars in the helix... */
730   int i;
731   GLfloat usable_th = max_th - dc->wall_taper;
732   GLfloat usable_z  = max_z / (max_th / usable_th);
733   int nbars = usable_z / dc->bar_spacing;
734   GLfloat used_z, pad_z, pad_ratio;
735
736   if (! (nbars & 1)) nbars--;  /* always an odd number of bars */
737
738   used_z = (nbars - 1) * dc->bar_spacing;
739   pad_z = max_z - used_z;
740   pad_ratio = pad_z / max_z;
741
742   th = (max_th * pad_ratio/2);
743   z  = -(max_z / 2) + (max_z * pad_ratio/2);
744
745   if (!dc->clockwise)
746     z = -z, z_inc = -z_inc;
747
748   glFrontFace(GL_CCW);
749   for (i = 0; i < nbars; i++)
750     {
751       int facets = dc->bar_facets / (facetted ? 14 : 1);
752       if (facets <= 3) facets = 3;
753       x = cos (th) * (1 - dc->wall_thickness);
754       y = sin (th) * (1 - dc->wall_thickness);
755       polys += tube ( x,  y, z,
756                      -x, -y, z,
757                       dc->bar_thickness, 0, facets,
758                       True, True, wire);
759       z  += z_inc;
760       th += th_inc;
761     }
762   return polys;
763 }
764
765
766 \f
767 /* Make the gasket
768  */
769
770
771 static int
772 make_gasket (logo_configuration *dc, int wire)
773 {
774   int polys = 0;
775   int i;
776   int points_size;
777   int npoints = 0;
778   int nctrls = 0;
779   int res = 360/8;
780   GLfloat d2r = M_PI / 180;
781
782   GLfloat thick2 = (dc->gasket_thickness / dc->gasket_size) / 2;
783
784   GLfloat *pointsx0, *pointsy0, *pointsx1, *pointsy1, *normals;
785
786   GLfloat r0  = 0.750;  /* 395 */
787   GLfloat r1a = 0.825;                /* bottom of wall below upper left hole */
788   GLfloat r1b = 0.867;                /* center of upper left hole */
789   GLfloat r1c = 0.909;                /* top of wall above hole */
790   GLfloat r1  = 0.916;  /* 471 */
791   GLfloat r2  = 0.963;  /* 490 */
792   GLfloat r3  = 0.960;  /* 499 */
793   GLfloat r4  = 1.000;  /* 507 */
794   GLfloat r5  = 1.080;  /* 553 */
795
796   GLfloat ctrl_r[100], ctrl_th[100];
797
798   glPushMatrix();
799
800 # ifdef DXF_OUTPUT_HACK
801   if (! wire) res *= 8;
802 # endif
803
804 # define POINT(r,th) \
805     ctrl_r [nctrls] = r, \
806     ctrl_th[nctrls] = (th * d2r), \
807     nctrls++
808
809   POINT (0.829, 0);      /* top indentation, right half */
810   POINT (0.831, 0.85);
811   POINT (0.835, 1.81);
812   POINT (0.841, 2.65);
813   POINT (0.851, 3.30);
814   POINT (0.862, 3.81);
815   POINT (0.872, 3.95);
816
817   POINT (r4,    4.0);   /* moving clockwise... */
818   POINT (r4,   47.0);
819   POINT (r1,   47.0);
820   POINT (r1,   53.0);
821   POINT (r2,   55.5);
822   POINT (r2,   72.3);
823   POINT (r1,   74.0);
824   POINT (r1,  100.0);
825   POINT (r3,  102.5);
826   POINT (r3,  132.0);
827   POINT (r1,  133.0);
828
829   POINT (r1,  180.7);
830   POINT (r2,  183.6);
831   POINT (r2,  210.0);
832   POINT (r1,  212.0);
833   POINT (r1,  223.2);
834   POINT (r5,  223.2);
835   POINT (r5,  225.0);
836   POINT (r4,  225.0);
837
838   POINT (r4,    316.8);      /* upper left indentation */
839   POINT (0.990, 316.87);
840   POINT (0.880, 317.21);
841   POINT (0.872, 317.45);
842   POINT (0.869, 317.80);
843   POINT (0.867, 318.10);
844
845   POINT (0.867, 318.85);
846   POINT (0.869, 319.15);
847   POINT (0.872, 319.50);
848   POINT (0.880, 319.74);
849   POINT (0.990, 320.08);
850
851   POINT (r4,  338.0);
852   if (! wire)
853     {
854       POINT (r1a, 338.0);      /* cut-out disc */
855       POINT (r1a, 344.0);
856     }
857   POINT (r4,  344.0);
858   POINT (r4,  356.0);
859
860   POINT (0.872, 356.05);   /* top indentation, left half */
861   POINT (0.862, 356.19);
862   POINT (0.851, 356.70);
863   POINT (0.841, 357.35);
864   POINT (0.835, 358.19);
865   POINT (0.831, 359.15);
866   POINT (0.829, 360);
867 # undef POINT
868
869   points_size = res + (nctrls * 2);
870   pointsx0 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
871   pointsy0 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
872   pointsx1 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
873   pointsy1 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
874   normals  = (GLfloat *) malloc (points_size * sizeof(GLfloat) * 2);
875
876   npoints = 0;
877   for (i = 1; i < nctrls; i++)
878     {
879       GLfloat from_r  = ctrl_r [i-1];
880       GLfloat from_th = ctrl_th[i-1];
881       GLfloat to_r    = ctrl_r [i];
882       GLfloat to_th   = ctrl_th[i];
883
884       GLfloat step = 2*M_PI / res;
885       int nsteps = 1 + ((to_th - from_th) / step);
886       int j;
887
888       for (j = 0; j < nsteps + (i == nctrls-1); j++)
889         {
890           GLfloat r  = from_r  + (j * (to_r  - from_r)  / nsteps);
891           GLfloat th = from_th + (j * (to_th - from_th) / nsteps);
892
893           GLfloat cth = cos(th) * dc->gasket_size;
894           GLfloat sth = sin(th) * dc->gasket_size;
895
896           pointsx0[npoints] = r0 * cth;  /* inner ring */
897           pointsy0[npoints] = r0 * sth;
898           pointsx1[npoints] = r  * cth;  /* outer ring */
899           pointsy1[npoints] = r  * sth;
900           npoints++;
901
902           if (npoints >= points_size) abort();
903         }
904     }
905
906   /* normals for the outer ring */
907   for (i = 1; i < npoints; i++)
908     {
909       XYZ a, b, c, n;
910       a.x = pointsx1[i-1];
911       a.y = pointsy1[i-1];
912       a.z = 0;
913       b.x = pointsx1[i];
914       b.y = pointsy1[i];
915       b.z = 0;
916       c = b;
917       c.z = 1;
918       n = calc_normal (a, b, c);
919       normals[(i-1)*2  ] = n.x;
920       normals[(i-1)*2+1] = n.y;
921     }
922
923   glRotatef(-90, 0, 1, 0);
924   glRotatef(180, 0, 0, 1);
925
926   if (wire)
927     {
928       GLfloat z;
929       for (z = -thick2; z <= thick2; z += thick2*2)
930         {
931 # if 1
932           /* inside edge */
933           glBegin (GL_LINE_LOOP);
934           for (i = 0; i < npoints; i++)
935             glVertex3f (pointsx0[i], pointsy0[i], z);
936           polys += npoints;
937           glEnd();
938
939           /* outside edge */
940           glBegin (GL_LINE_LOOP);
941           for (i = 0; i < npoints; i++)
942             glVertex3f (pointsx1[i], pointsy1[i], z);
943           polys += npoints;
944           glEnd();
945 # else
946           for (i = 1; i < npoints; i++)
947             {
948               glBegin (GL_LINE_STRIP);
949               glVertex3f (pointsx0[i-1], pointsy0[i-1], z);
950               glVertex3f (pointsx0[i  ], pointsy0[i  ], z);
951               glVertex3f (pointsx1[i  ], pointsy1[i  ], z);
952               glVertex3f (pointsx1[i-1], pointsy1[i-1], z);
953               glEnd();
954             }
955           polys += npoints;
956 # endif
957         }
958 #if 1
959       glBegin (GL_LINES);
960       for (i = 0; i < npoints; i++)
961         {
962           /* inside rim */
963           glVertex3f (pointsx0[i], pointsy0[i], -thick2);
964           glVertex3f (pointsx0[i], pointsy0[i],  thick2);
965           /* outside rim */
966           glVertex3f (pointsx1[i], pointsy1[i], -thick2);
967           glVertex3f (pointsx1[i], pointsy1[i],  thick2);
968         }
969       polys += npoints;
970       glEnd();
971 #endif
972     }
973   else
974     {
975       /* top */
976       glFrontFace(GL_CW);
977       glNormal3f(0, 0, -1);
978       glBegin (GL_QUAD_STRIP);
979       for (i = 0; i < npoints; i++)
980         {
981           glVertex3f (pointsx0[i], pointsy0[i], -thick2);
982           glVertex3f (pointsx1[i], pointsy1[i], -thick2);
983         }
984       polys += npoints;
985       glEnd();
986
987       /* bottom */
988       glFrontFace(GL_CCW);
989       glNormal3f(0, 0, 1);
990       glBegin (GL_QUAD_STRIP);
991       for (i = 0; i < npoints; i++)
992         {
993           glVertex3f (pointsx0[i], pointsy0[i], thick2);
994           glVertex3f (pointsx1[i], pointsy1[i], thick2);
995         }
996       polys += npoints;
997       glEnd();
998
999       /* inside edge */
1000       glFrontFace(GL_CW);
1001       glBegin (GL_QUAD_STRIP);
1002       for (i = 0; i < npoints; i++)
1003         {
1004           glNormal3f (-pointsx0[i], -pointsy0[i],  0);
1005           glVertex3f ( pointsx0[i],  pointsy0[i],  thick2);
1006           glVertex3f ( pointsx0[i],  pointsy0[i], -thick2);
1007         }
1008       polys += npoints;
1009       glEnd();
1010
1011       /* outside edge */
1012       glFrontFace(GL_CCW);
1013       glBegin (GL_QUADS);
1014       {
1015         for (i = 0; i < npoints-1; i++)
1016           {
1017             int ia = (i == 0 ? npoints-2 : i-1);
1018             int iz = (i == npoints-2 ? 0 : i+1);
1019             GLfloat  x = pointsx1[i];
1020             GLfloat  y = pointsy1[i];
1021             GLfloat xz = pointsx1[iz];
1022             GLfloat yz = pointsy1[iz];
1023
1024             GLfloat nxa = normals[ia*2];   /* normal of [i-1 - i] face */
1025             GLfloat nya = normals[ia*2+1];
1026             GLfloat nx  = normals[i*2];    /* normal of [i - i+1] face */
1027             GLfloat ny  = normals[i*2+1];
1028             GLfloat nxz = normals[iz*2];    /* normal of [i+1 - i+2] face */
1029             GLfloat nyz = normals[iz*2+1];
1030
1031             GLfloat anglea = vector_angle (nx, ny, 0, nxa, nya, 0);
1032             GLfloat anglez = vector_angle (nx, ny, 0, nxz, nyz, 0);
1033             GLfloat pointy = 0.6;
1034
1035             if (anglea > pointy)
1036               {
1037                 glNormal3f (nx, ny, 0);
1038                 glVertex3f (x,  y,   thick2);
1039                 glVertex3f (x,  y,  -thick2);
1040               }
1041             else
1042               {
1043                 glNormal3f ((nxa + nx) / 2, (nya + ny) / 2, 0);
1044                 glVertex3f (x,  y,   thick2);
1045                 glVertex3f (x,  y,  -thick2);
1046               }
1047
1048             if (anglez > pointy)
1049               {
1050                 glNormal3f (nx, ny, 0);
1051                 glVertex3f (xz, yz, -thick2);
1052                 glVertex3f (xz, yz,  thick2);
1053               }
1054             else
1055               {
1056                 glNormal3f ((nx + nxz) / 2, (ny + nyz) / 2, 0);
1057                 glVertex3f (xz, yz, -thick2);
1058                 glVertex3f (xz, yz,  thick2);
1059               }
1060           }
1061         polys += npoints;
1062       }
1063       glEnd();
1064     }
1065
1066   /* Fill in the upper left hole...
1067    */
1068   {
1069     GLfloat th;
1070     npoints = 0;
1071
1072     th = 338.0 * d2r;
1073     pointsx0[npoints] = r1c * cos(th) * dc->gasket_size;
1074     pointsy0[npoints] = r1c * sin(th) * dc->gasket_size;
1075     npoints++;
1076     pointsx0[npoints] = r4 * cos(th) * dc->gasket_size;
1077     pointsy0[npoints] = r4 * sin(th) * dc->gasket_size;
1078     npoints++;
1079
1080     th = 344.0 * d2r;
1081     pointsx0[npoints] = r1c * cos(th) * dc->gasket_size;
1082     pointsy0[npoints] = r1c * sin(th) * dc->gasket_size;
1083     npoints++;
1084     pointsx0[npoints] = r4 * cos(th) * dc->gasket_size;
1085     pointsy0[npoints] = r4 * sin(th) * dc->gasket_size;
1086
1087     if (! wire)
1088       {
1089         /* front wall */
1090         glNormal3f (0, 0, -1);
1091         glFrontFace(GL_CW);
1092         glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
1093         glVertex3f (pointsx0[0], pointsy0[0], -thick2);
1094         glVertex3f (pointsx0[1], pointsy0[1], -thick2);
1095         glVertex3f (pointsx0[3], pointsy0[3], -thick2);
1096         glVertex3f (pointsx0[2], pointsy0[2], -thick2);
1097         glEnd();
1098         polys++;
1099
1100         /* back wall */
1101         glNormal3f (0, 0, 1);
1102         glFrontFace(GL_CCW);
1103         glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
1104         glVertex3f (pointsx0[0], pointsy0[0],  thick2);
1105         glVertex3f (pointsx0[1], pointsy0[1],  thick2);
1106         glVertex3f (pointsx0[3], pointsy0[3],  thick2);
1107         glVertex3f (pointsx0[2], pointsy0[2],  thick2);
1108         glEnd();
1109         polys++;
1110       }
1111
1112     /* top wall */
1113     glFrontFace(GL_CW);
1114     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
1115     glNormal3f (pointsx0[1], pointsy0[1], 0);
1116     glVertex3f (pointsx0[1], pointsy0[1],  thick2);
1117     glNormal3f (pointsx0[3], pointsy0[3], 0);
1118     glVertex3f (pointsx0[3], pointsy0[3],  thick2);
1119     glVertex3f (pointsx0[3], pointsy0[3], -thick2);
1120     glNormal3f (pointsx0[1], pointsy0[1], 0);
1121     glVertex3f (pointsx0[1], pointsy0[1], -thick2);
1122     glEnd();
1123     polys++;
1124
1125
1126     /* Now make a donut.
1127      */
1128     {
1129       int nsteps = (wire ? 12 : 64);
1130       GLfloat r0 = 0.04;
1131       GLfloat r1 = 0.070;
1132       GLfloat th, cth, sth;
1133
1134       glPushMatrix ();
1135
1136       th = ((339.0 + 343.0) / 2) * d2r;
1137       
1138       glTranslatef (r1b * cos(th) * dc->gasket_size,
1139                     r1b * sin(th) * dc->gasket_size,
1140                     0);
1141
1142       npoints = 0;
1143       for (i = 0; i < nsteps; i++)
1144         {
1145           th = 2 * M_PI * i / nsteps;
1146           cth = cos (th) * dc->gasket_size;
1147           sth = sin (th) * dc->gasket_size;
1148           pointsx0[npoints] = r0 * cth;
1149           pointsy0[npoints] = r0 * sth;
1150           pointsx1[npoints] = r1 * cth;
1151           pointsy1[npoints] = r1 * sth;
1152           npoints++;
1153           polys++;
1154         }
1155
1156       pointsx0[npoints] = pointsx0[0];
1157       pointsy0[npoints] = pointsy0[0];
1158       pointsx1[npoints] = pointsx1[0];
1159       pointsy1[npoints] = pointsy1[0];
1160       npoints++;
1161
1162       if (wire)
1163         {
1164           glBegin (GL_LINE_LOOP);
1165           for (i = 0; i < npoints; i++)
1166             glVertex3f (pointsx0[i], pointsy0[i], -thick2);
1167           polys += npoints;
1168           glEnd();
1169           glBegin (GL_LINE_LOOP);
1170           for (i = 0; i < npoints; i++)
1171             glVertex3f (pointsx0[i], pointsy0[i],  thick2);
1172           polys += npoints;
1173           glEnd();
1174 # if 0
1175           glBegin (GL_LINE_LOOP);
1176           for (i = 0; i < npoints; i++)
1177             glVertex3f (pointsx1[i], pointsy1[i], -thick2);
1178           polys += npoints;
1179           glEnd();
1180           glBegin (GL_LINE_LOOP);
1181           for (i = 0; i < npoints; i++)
1182             glVertex3f (pointsx1[i], pointsy1[i],  thick2);
1183           polys += npoints;
1184           glEnd();
1185 # endif
1186         }
1187       else
1188         {
1189           /* top */
1190           glFrontFace(GL_CW);
1191           glNormal3f(0, 0, -1);
1192           glBegin (GL_QUAD_STRIP);
1193           for (i = 0; i < npoints; i++)
1194             {
1195               glVertex3f (pointsx0[i], pointsy0[i], -thick2);
1196               glVertex3f (pointsx1[i], pointsy1[i], -thick2);
1197             }
1198           polys += npoints;
1199           glEnd();
1200
1201           /* bottom */
1202           glFrontFace(GL_CCW);
1203           glNormal3f(0, 0, 1);
1204           glBegin (GL_QUAD_STRIP);
1205           for (i = 0; i < npoints; i++)
1206             {
1207               glVertex3f (pointsx0[i], pointsy0[i],  thick2);
1208               glVertex3f (pointsx1[i], pointsy1[i],  thick2);
1209             }
1210           polys += npoints;
1211           glEnd();
1212         }
1213
1214       /* inside edge */
1215       glFrontFace(GL_CW);
1216       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1217       for (i = 0; i < npoints; i++)
1218         {
1219           glNormal3f (-pointsx0[i], -pointsy0[i],  0);
1220           glVertex3f ( pointsx0[i],  pointsy0[i],  thick2);
1221           glVertex3f ( pointsx0[i],  pointsy0[i], -thick2);
1222         }
1223       polys += npoints;
1224       glEnd();
1225
1226       glPopMatrix();
1227     }
1228   }
1229
1230
1231   /* Attach the bottom-right dingus...
1232    */
1233   {
1234     GLfloat w = 0.05;
1235     GLfloat h = 0.19;
1236     GLfloat th;
1237
1238     glRotatef (49.5, 0, 0, 1);
1239     glScalef (dc->gasket_size, dc->gasket_size, 1);
1240     glTranslatef (0, (r0+r1)/2, 0);
1241
1242     /* buried box */
1243     if (! wire)
1244       {
1245         glFrontFace(GL_CCW);
1246         glBegin (wire ? GL_LINE_STRIP : GL_QUADS);
1247         glNormal3f (0, 0, -1);
1248         glVertex3f (-w/2, -h/2, -thick2); glVertex3f (-w/2,  h/2, -thick2);
1249         glVertex3f ( w/2,  h/2, -thick2); glVertex3f ( w/2, -h/2, -thick2);
1250         glNormal3f (1, 0, 0);
1251         glVertex3f ( w/2, -h/2, -thick2); glVertex3f ( w/2,  h/2, -thick2);
1252         glVertex3f ( w/2,  h/2,  thick2); glVertex3f ( w/2, -h/2,  thick2);
1253         glNormal3f (0, 0, 1);
1254         glVertex3f ( w/2, -h/2,  thick2); glVertex3f ( w/2,  h/2,  thick2);
1255         glVertex3f (-w/2,  h/2,  thick2); glVertex3f (-w/2, -h/2,  thick2);
1256         glNormal3f (-1, 0, 0);
1257         glVertex3f (-w/2, -h/2,  thick2); glVertex3f (-w/2,  h/2,  thick2);
1258         glVertex3f (-w/2,  h/2, -thick2); glVertex3f (-w/2, -h/2, -thick2);
1259         polys++;
1260         glEnd();
1261       }
1262
1263     npoints = 0;
1264     for (th = (wire ? 0 : -0.1);
1265          th <= M_PI + 0.1;
1266          th += (M_PI / (wire ? 5 : 32)))
1267       {
1268         pointsx0[npoints] = w/2 * cos(th);
1269         pointsy0[npoints] = w/2 * sin(th);
1270         npoints++;
1271         polys++;
1272       }
1273
1274     /* front inside curve */
1275     glNormal3f (0, 0, -1);
1276     glFrontFace(GL_CW);
1277     glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
1278     if (! wire) glVertex3f (0, h/2, -thick2);
1279     for (i = 0; i < npoints; i++)
1280       glVertex3f (pointsx0[i], h/2 + pointsy0[i], -thick2);
1281     polys += npoints;
1282     glEnd();
1283
1284     /* front outside curve */
1285     glFrontFace(GL_CCW);
1286     glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
1287     if (! wire) glVertex3f (0, -h/2, -thick2);
1288     for (i = 0; i < npoints; i++)
1289       glVertex3f (pointsx0[i], -h/2 - pointsy0[i], -thick2);
1290     polys += npoints;
1291     glEnd();
1292
1293     /* back inside curve */
1294     glNormal3f (0, 0, 1);
1295     glFrontFace(GL_CCW);
1296     glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
1297     if (! wire) glVertex3f (0, h/2, thick2);
1298     for (i = 0; i < npoints; i++)
1299       glVertex3f (pointsx0[i], h/2 + pointsy0[i], thick2);
1300     polys += npoints;
1301     glEnd();
1302
1303     /* back outside curve */
1304     glFrontFace(GL_CW);
1305     glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
1306     if (! wire) glVertex3f (0, -h/2, thick2);
1307     for (i = 0; i < npoints; i++)
1308       glVertex3f (pointsx0[i], -h/2 - pointsy0[i], thick2);
1309     polys += npoints;
1310     glEnd();
1311
1312     /* inside curve */
1313     glFrontFace(GL_CCW);
1314     glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1315     for (i = 0; i < npoints; i++)
1316       {
1317         glNormal3f (pointsx0[i], pointsy0[i], 0);
1318         glVertex3f (pointsx0[i], h/2 + pointsy0[i],  thick2);
1319         glVertex3f (pointsx0[i], h/2 + pointsy0[i], -thick2);
1320       }
1321     polys += npoints;
1322     glEnd();
1323
1324     /* outside curve */
1325     glFrontFace(GL_CW);
1326     glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1327     for (i = 0; i < npoints; i++)
1328       {
1329         glNormal3f (pointsx0[i], -pointsy0[i], 0);
1330         glVertex3f (pointsx0[i], -h/2 - pointsy0[i],  thick2);
1331         glVertex3f (pointsx0[i], -h/2 - pointsy0[i], -thick2);
1332       }
1333     polys += npoints;
1334     glEnd();
1335   }
1336
1337   free (pointsx0);
1338   free (pointsy0);
1339   free (pointsx1);
1340   free (pointsy1);
1341   free (normals);
1342
1343   glPopMatrix();
1344   return polys;
1345 }
1346
1347 static int
1348 make_frame (logo_configuration *dc, int wire)
1349 {
1350   int polys = 0;
1351   int i, j;
1352   GLfloat x[20], y[20];
1353   GLfloat corner_cut = 0.5;
1354
1355   glPushMatrix();
1356   glRotatef (90, 0, 1, 0);
1357   glScalef (4 * dc->frame_size,
1358             4 * dc->frame_size,
1359             4 * dc->frame_size);
1360
1361   x[0] = -dc->frame_thickness;
1362   x[1] = -dc->frame_thickness * corner_cut;
1363   x[2] = 0;
1364   x[3] = 0.5 - dc->triangle_size;
1365   x[4] = 0.5;
1366   x[5] = 0.5 + dc->triangle_size;
1367   x[6] = 1;
1368   x[7] = 1 + dc->frame_thickness * corner_cut;
1369   x[8] = 1 + dc->frame_thickness;
1370
1371   y[0] = -dc->frame_thickness;
1372   y[1] = -dc->frame_thickness * corner_cut;
1373   y[2] = 0;
1374   y[3] = dc->triangle_size;
1375
1376   /* front and back
1377    */
1378   glTranslatef (-0.5, -0.5, dc->frame_depth / 4);
1379   if (! wire)
1380     for (j = 0; j <= 1; j++)
1381       {
1382         if (j) glTranslatef (0, 0, -dc->frame_depth / 2);
1383         glFrontFace (j ? GL_CCW : GL_CW);
1384         for (i = 0; i < 4; i++)
1385           {
1386             glNormal3f (0, 0, (j ? -1 : 1));
1387             glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1388             glVertex3f (x[0], y[1], 0); glVertex3f (x[0], y[2], 0);
1389             glVertex3f (x[1], y[0], 0); glVertex3f (x[1], y[2], 0);
1390             glVertex3f (x[3], y[0], 0); glVertex3f (x[3], y[2], 0);
1391             glVertex3f (x[4], y[0], 0); glVertex3f (x[4], y[3], 0);
1392             glVertex3f (x[5], y[0], 0); glVertex3f (x[5], y[2], 0); 
1393             glVertex3f (x[7], y[0], 0); glVertex3f (x[7], y[2], 0);
1394             glVertex3f (x[8], y[1], 0); glVertex3f (x[8], y[2], 0);
1395             polys += 6;
1396             glEnd ();
1397             glTranslatef (0.5, 0.5, 0);
1398             glRotatef (90, 0, 0, 1);
1399             glTranslatef (-0.5, -0.5, 0);
1400           }
1401       }
1402
1403   /* ledges
1404    */
1405   glFrontFace (GL_CCW);
1406   for (i = 0; i < 4; i++)
1407     {
1408       glNormal3f (0, 1, 0);
1409       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1410       glVertex3f (x[2], y[2], 0); glVertex3f (x[2], y[2], dc->frame_depth/2); 
1411       glVertex3f (x[3], y[2], 0); glVertex3f (x[3], y[2], dc->frame_depth/2); 
1412       glVertex3f (x[4], y[3], 0); glVertex3f (x[4], y[3], dc->frame_depth/2); 
1413       glVertex3f (x[5], y[2], 0); glVertex3f (x[5], y[2], dc->frame_depth/2); 
1414       glVertex3f (x[6], y[2], 0); glVertex3f (x[6], y[2], dc->frame_depth/2); 
1415       polys += 4;
1416       glEnd ();
1417
1418       glNormal3f (0, -1, 0);
1419       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
1420       glVertex3f (x[7], y[0], 0); 
1421       glVertex3f (x[7], y[0], dc->frame_depth/2); 
1422       glVertex3f (x[1], y[0], dc->frame_depth/2); 
1423       glVertex3f (x[1], y[0], 0); 
1424       polys++;
1425       glEnd ();
1426
1427       glNormal3f (1, -1, 0);
1428       glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
1429       glVertex3f (x[8], y[1], 0); 
1430       glVertex3f (x[8], y[1], dc->frame_depth/2); 
1431       glVertex3f (x[7], y[0], dc->frame_depth/2); 
1432       glVertex3f (x[7], y[0], 0); 
1433       polys++;
1434       glEnd ();
1435
1436       if (wire) 
1437         {
1438           glNormal3f (0, 1, 0);
1439           for (j = 0; j <= 1; j++)
1440             {
1441               glBegin (GL_LINE_STRIP);
1442               glVertex3f (x[2], y[2], j*dc->frame_depth/2);
1443               glVertex3f (x[3], y[2], j*dc->frame_depth/2);
1444               glVertex3f (x[4], y[3], j*dc->frame_depth/2);
1445               glVertex3f (x[5], y[2], j*dc->frame_depth/2);
1446               glVertex3f (x[6], y[2], j*dc->frame_depth/2);
1447               polys += 4;
1448               glEnd ();
1449             }
1450         }
1451
1452       glTranslatef (0.5, 0.5, 0);
1453       glRotatef (90, 0, 0, 1);
1454       glTranslatef (-0.5, -0.5, 0);
1455     }
1456
1457   glPopMatrix();
1458   return polys;
1459 }
1460
1461 \f
1462 /* Make some pizza.
1463  */
1464
1465 #ifdef HAVE_TESS
1466
1467 typedef struct {
1468   GLdouble *points;
1469   int i;
1470 } tess_out;
1471
1472
1473 static void
1474 tess_error_cb (GLenum errorCode)
1475 {
1476   fprintf (stderr, "%s: tesselation error: %s\n",
1477            progname, gluErrorString(errorCode));
1478   exit (0);
1479 }
1480
1481 static void
1482 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4], 
1483                  GLdouble **data_out)
1484 {
1485   GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
1486   new[0] = coords[0];
1487   new[1] = coords[1];
1488   new[2] = coords[2];
1489   *data_out = new;
1490 }
1491
1492
1493 #if 0
1494 static void
1495 tess_vertex_cb (void *vertex_data, void *closure)
1496 {
1497   tess_out *to = (tess_out *) closure;
1498   GLdouble *v = (GLdouble *) vertex_data;
1499   to->points[to->i++] = v[0];
1500   to->points[to->i++] = v[1];
1501   to->points[to->i++] = v[2];
1502 }
1503 #endif
1504
1505 static void
1506 tess_begin_cb (GLenum which)
1507 {
1508   glBegin(which);
1509 }
1510
1511 static void
1512 tess_end_cb (void)
1513 {
1514   glEnd();
1515 }
1516
1517 #endif /* HAVE_TESS */
1518
1519
1520 static int
1521 make_pizza (logo_configuration *dc, int facetted, int wire)
1522 {
1523   int polys = 0;
1524   int topfaces = (facetted ? 48 : 120);
1525   int discfaces = (facetted ? 12 : 120);
1526   int npoints = topfaces * 2 + 100;
1527   GLdouble *points = (GLdouble *) calloc (npoints * 3, sizeof(GLdouble));
1528   int nholes = 3;
1529   GLdouble *holes  = (GLdouble *) calloc (topfaces*nholes*3, sizeof(GLdouble));
1530
1531   GLfloat step = M_PI * 2 / 6 / topfaces;
1532   GLfloat thick2 = (dc->gasket_thickness / dc->gasket_size) / 4;
1533   GLfloat th, x, y, s;
1534   int i, j, k;
1535   int endpoints;
1536
1537 # ifdef HAVE_TESS
1538   tess_out TO, *to = &TO;
1539   GLUtesselator *tess = gluNewTess();
1540
1541   to->points = (GLdouble *) calloc (topfaces * 20, sizeof(GLdouble));
1542   to->i = 0;
1543
1544 #  ifndef  _GLUfuncptr
1545 #   define _GLUfuncptr void(*)(void)
1546 #  endif
1547
1548   gluTessCallback(tess,GLU_TESS_BEGIN,      (_GLUfuncptr)tess_begin_cb);
1549   gluTessCallback(tess,GLU_TESS_VERTEX,     (_GLUfuncptr)glVertex3dv);
1550   gluTessCallback(tess,GLU_TESS_END,        (_GLUfuncptr)tess_end_cb);
1551   gluTessCallback(tess,GLU_TESS_COMBINE,    (_GLUfuncptr)tess_combine_cb);
1552   gluTessCallback(tess,GLU_TESS_ERROR,      (_GLUfuncptr)tess_error_cb);
1553
1554   gluTessProperty (tess, GLU_TESS_BOUNDARY_ONLY, wire);
1555   gluTessProperty (tess,GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
1556
1557 # endif /* HAVE_TESS */
1558
1559   glPushMatrix();
1560
1561   s = 1.9;
1562   glRotatef (180, 0, 0, 1);
1563   glScalef (s, s, s);
1564   glRotatef (90, 0, 1, 0);
1565   glTranslatef (-0.53, 0, 0);
1566   glRotatef (-30, 0, 0, 1);
1567
1568   /* Compute the wedge */
1569   th = 0;
1570   j = 0;
1571
1572   /* Edge 1 */
1573   {
1574     GLfloat edge[] = {
1575       0.000, 0.000,
1576       0.000, 0.210,
1577       0.042, 0.230,
1578       0.042, 0.616,
1579       0.000, 0.641,
1580     };
1581     for (i = 0; i < countof(edge)/2; i++)
1582       {
1583         points[j++] = edge[i*2+1];
1584         points[j++] = edge[i*2];
1585         points[j++] = 0;
1586       }
1587   }
1588
1589   s = 0.798;  /* radius of end of slice, before crust gap */
1590   for (i = 0; i < topfaces; i++)
1591     {
1592       points[j++] = cos(th) * s;
1593       points[j++] = sin(th) * s;
1594       points[j++] = 0;
1595       th += step;
1596     }
1597
1598   /* Edge 2 */
1599   {
1600     GLfloat edge[] = {
1601       0.613, 0.353,
1602       0.572, 0.376,
1603       0.455, 0.309,
1604       0.452, 0.260,
1605       0.332, 0.192,
1606       0.293, 0.216,
1607       0.178, 0.149,
1608       0.178, 0.102,
1609     };
1610     for (i = 0; i < countof(edge)/2; i++)
1611       {
1612         points[j++] = edge[i*2+1];
1613         points[j++] = edge[i*2];
1614         points[j++] = 0;
1615       }
1616     endpoints = j/3;
1617   }
1618
1619
1620   /* Draw the rim of the slice */
1621   glBegin (wire ? GL_LINES : GL_QUADS);
1622   x = points[0];
1623   y = points[1];
1624   for (i = (wire ? 0 : 1); i < endpoints; i++)
1625     {
1626       GLdouble *p = points + (i*3);
1627
1628       do_normal (p[0], p[1],  -thick2,
1629                  p[0], p[1],   thick2,
1630                  x,    y,     thick2);
1631       if (!wire)
1632         {
1633           glVertex3f (x, y, -thick2);
1634           glVertex3f (x, y,  thick2);
1635         }
1636       glVertex3f (p[0], p[1],  thick2);
1637       glVertex3f (p[0], p[1], -thick2);
1638       x = p[0];
1639       y = p[1];
1640       polys++;
1641     }
1642
1643   do_normal (points[0], points[1],  -thick2,
1644              points[0], points[1],   thick2,
1645              x,         y,           thick2);
1646   glVertex3f (x, y, -thick2);
1647   glVertex3f (x, y,  thick2);
1648   glVertex3f (points[0], points[1],  thick2);
1649   glVertex3f (points[0], points[1], -thick2);
1650   polys++;
1651   glEnd();
1652
1653 # ifndef HAVE_TESS
1654   if (wire)
1655     {
1656       /* Outline of slice */
1657       glBegin (GL_LINE_LOOP);
1658       for (i = 0; i < endpoints; i++)
1659         glVertex3f (points[i*3], points[i*3+1], -thick2);
1660       glEnd();
1661       glBegin (GL_LINE_LOOP);
1662       for (i = 0; i < endpoints; i++)
1663         glVertex3f (points[i*3], points[i*3+1], thick2);
1664       glEnd();
1665     }
1666 # endif /* HAVE_TESS */
1667
1668   /* Compute the holes */
1669   step = M_PI * 2 / discfaces;
1670   for (k = 0; k < nholes; k++)
1671     {
1672       GLdouble *p = holes + (discfaces * 3 * k);
1673       th = 0;
1674       j = 0;
1675       switch (k) {
1676         case 0: x = 0.34; y = 0.17; s = 0.05; break;
1677         case 1: x = 0.54; y = 0.17; s = 0.06; break;
1678         case 2: x = 0.55; y = 0.36; s = 0.06; break;
1679       default: abort(); break;
1680       }
1681       for (i = 0; i < discfaces; i++)
1682         {
1683           p[j++] = x + cos(M_PI*2 - th) * s;
1684           p[j++] = y + sin(M_PI*2 - th) * s;
1685           p[j++] = 0;
1686           th += step;
1687         }
1688     }
1689
1690
1691   /* Draw the inside rim of the holes */
1692   for (k = 0; k < nholes; k++)
1693     {
1694       GLdouble *p = holes + (discfaces * 3 * k);
1695
1696       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1697       for (i = 0; i < discfaces; i++)
1698         {
1699           GLdouble *p2 = p + (i*3);
1700           if (i > 0)
1701             do_normal (p2[0],  p2[1],  -thick2,
1702                        p2[0],  p2[1],   thick2,
1703                        p2[-3], p2[-2],  thick2);
1704           glVertex3f (p2[0], p2[1], -thick2);
1705           glVertex3f (p2[0], p2[1],  thick2);
1706           polys++;
1707         }
1708       glVertex3f (p[0], p[1], -thick2);
1709       glVertex3f (p[0], p[1],  thick2);
1710       polys++;
1711       glEnd();
1712 # ifndef HAVE_TESS
1713       if (wire)
1714         {
1715           /* Outline of holes */
1716           glBegin (GL_LINE_LOOP);
1717           for (i = 0; i < discfaces; i++)
1718             glVertex3f (p[i*3], p[i*3+1], -thick2);
1719           glEnd();
1720           glBegin (GL_LINE_LOOP);
1721           for (i = 0; i < discfaces; i++)
1722             glVertex3f (p[i*3], p[i*3+1], thick2);
1723           glEnd();
1724         }
1725 # endif /* !HAVE_TESS */
1726     }
1727
1728 # ifdef HAVE_TESS
1729   glTranslatef (0, 0, -thick2);
1730   for (y = 0; y <= 1; y++)
1731     {
1732       if (y) glTranslatef (0, 0, thick2*2);
1733
1734       /* A non-convex polygon */
1735       gluTessBeginPolygon (tess, to);
1736
1737       glNormal3f (0, 0, (y > 0 ? 1 : -1));
1738       gluTessNormal (tess, 0, 0, (y > 0 ? 1 : -1));
1739       glFrontFace (GL_CCW);
1740
1741       /* Tess the wedge */
1742       gluTessBeginContour (tess);
1743       for (i = 0; i < endpoints; i++)
1744         {
1745           GLdouble *p = points + (i*3);
1746           gluTessVertex (tess, p, p);
1747           polys++;
1748         }
1749       gluTessVertex (tess, points, points);
1750       gluTessEndContour (tess);
1751
1752       /* Tess the holes */
1753       for (k = 0; k < nholes; k++)
1754         {
1755           GLdouble *p = holes + (discfaces * 3 * k);
1756           gluTessBeginContour (tess);
1757           for (i = 0; i < discfaces; i++)
1758             {
1759               GLdouble *p2 = p + (i*3);
1760               gluTessVertex (tess, p2, p2);
1761               polys++;
1762             }
1763           gluTessEndContour (tess);
1764         }
1765
1766       gluTessEndPolygon (tess);
1767     }
1768
1769   glTranslatef (0, 0, -thick2);
1770
1771 # else  /* !HAVE_TESS */
1772   if (! wire)
1773     {
1774       glTranslatef(0, 0, thick2);
1775       glNormal3f (0, 0, 1);
1776       glFrontFace (GL_CW);
1777
1778       /* Sadly, jwzgl's glVertexPointer seems not to be recordable inside
1779          display lists. */
1780 #  if 0
1781       glDisableClientState (GL_COLOR_ARRAY);
1782       glDisableClientState (GL_NORMAL_ARRAY);
1783       glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1784       glEnableClientState (GL_VERTEX_ARRAY);
1785       glVertexPointer (3, GL_FLOAT, 0, dnapizza_triangles);
1786       glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
1787 #  else
1788       glBegin (GL_TRIANGLES);
1789       for (i = 0; i < countof (dnapizza_triangles); i += 3)
1790         glVertex3fv (dnapizza_triangles + i);
1791       glEnd();
1792 #  endif
1793
1794       glTranslatef(0, 0, -thick2*2);
1795       glNormal3f (0, 0, -1);
1796       glFrontFace (GL_CCW);
1797
1798 #  if 0
1799       glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
1800 #  else
1801       int i;
1802       glBegin (GL_TRIANGLES);
1803       for (i = 0; i < countof (dnapizza_triangles); i += 3)
1804         glVertex3fv (dnapizza_triangles + i);
1805       glEnd();
1806 #  endif
1807
1808       glTranslatef(0, 0, thick2);
1809     }
1810 # endif /* !HAVE_TESS */
1811
1812
1813   /* Compute the crust */
1814
1815   s = 0.861;  /* radius of inside of crust */
1816   step = M_PI * 2 / 6 / topfaces;
1817   th = 0;
1818   j = 0;
1819   for (i = 0; i < topfaces; i++)
1820     {
1821       points[j++] = cos(th) * s;
1822       points[j++] = sin(th) * s;
1823       points[j++] = 0;
1824       th += step;
1825     }
1826
1827   s = 1;
1828   for (i = 0; i < topfaces; i++)
1829     {
1830       points[j++] = cos(th) * s;
1831       points[j++] = sin(th) * s;
1832       points[j++] = 0;
1833       th -= step;
1834     }
1835
1836   /* Draw the rim of the crust */
1837   glFrontFace (GL_CCW);
1838   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1839   for (i = 0; i < topfaces * 2; i++)
1840     {
1841       GLdouble *p = points + (i*3);
1842       if (i == 0 || i == (topfaces*2)-1)
1843         glNormal3f (0, -1, 0);
1844       else if (i == topfaces-1 || i == topfaces)
1845         glNormal3f (0, 1, 0);
1846       else
1847         do_normal (p[-3], p[-2], thick2,
1848                    p[0], p[1],   thick2,
1849                    p[0], p[1],  -thick2);
1850       glVertex3f (p[0], p[1], -thick2);
1851       glVertex3f (p[0], p[1],  thick2);
1852       polys++;
1853     }
1854   glVertex3f (points[0], points[1], -thick2);
1855   glVertex3f (points[0], points[1],  thick2);
1856   polys++;
1857   glEnd();
1858
1859   if (wire)
1860     {
1861       glBegin (GL_LINE_STRIP);
1862       for (i = 0; i < topfaces * 2; i++)
1863         {
1864           GLdouble *p = points + (i*3);
1865           glVertex3f (p[0], p[1], -thick2);
1866           polys++;
1867         }
1868       glVertex3f (points[0], points[1], -thick2);
1869       glEnd();
1870
1871       glBegin (GL_LINE_STRIP);
1872       for (i = 0; i < topfaces * 2; i++)
1873         {
1874           GLdouble *p = points + (i*3);
1875           glVertex3f (p[0], p[1], thick2);
1876           polys++;
1877         }
1878       glVertex3f (points[0], points[1], thick2);
1879       glEnd();
1880     }
1881
1882   /* Draw the top of the crust */
1883   if (! wire)
1884     {
1885       glFrontFace (GL_CW);
1886       glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
1887       glNormal3f (0, 0, -1);
1888       if (!wire)
1889         for (i = 0; i < topfaces; i++)
1890           {
1891             int ii = topfaces + (topfaces - i - 1);
1892             GLdouble *p1 = points + (i*3);
1893             GLdouble *p2 = points + (ii*3);
1894             glVertex3f (p1[0], p1[1], -thick2);
1895             glVertex3f (p2[0], p2[1], -thick2);
1896             polys++;
1897           }
1898       polys++;
1899       glEnd();
1900
1901       /* Draw the bottom of the crust */
1902       glFrontFace (GL_CCW);
1903       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
1904       glNormal3f (0, 0, 1);
1905       for (i = 0; i < topfaces; i++)
1906         {
1907           int ii = topfaces + (topfaces - i - 1);
1908           GLdouble *p1 = points + (i*3);
1909           GLdouble *p2 = points + (ii*3);
1910           glVertex3f (p1[0], p1[1], thick2);
1911           glVertex3f (p2[0], p2[1], thick2);
1912           polys++;
1913         }
1914       polys++;
1915       glEnd();
1916     }
1917
1918 # ifdef HAVE_TESS
1919   gluDeleteTess (tess);
1920   free (to->points);
1921 # endif /* HAVE_TESS */
1922
1923   free (points);
1924   free (holes);
1925
1926   glPopMatrix();
1927
1928   return polys;
1929 }
1930
1931
1932 # ifdef CW
1933
1934 /* Upcase string, convert Unicrud to ASCII, remove any non-letters.
1935  */
1936 static char *
1937 codeword_simplify_text (const char *s0)
1938 {
1939   char *s1 = utf8_to_latin1 ((s0 ? s0 : ""), True);
1940   int L = strlen(s1);
1941   char *s2 = (char *) malloc (L + 10);
1942   char *s3 = s2;
1943   int i;
1944   for (i = 0; i < L; i++)
1945     {
1946       char c = s1[i];
1947       if (c >= 'a' && c <= 'z')
1948         c -= 'a'-'A';
1949       if (c >= 'A' && c <= 'Z')
1950         *s3++ = c;
1951     }
1952   *s3 = 0;
1953   if (! *s2)
1954     strcpy (s2, "CODEWORD");
1955   return s2;
1956 }
1957
1958
1959 static void
1960 make_codeword_path (ModeInfo *mi)
1961 {
1962   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
1963   int letters = strlen (dc->codeword_text);
1964
1965   GLfloat rtick = dc->codeword_spread;
1966   GLfloat iradius = rtick * dc->codeword_cap_size;
1967
1968   int dial = 0;
1969   int letter;
1970   GLfloat last_r = 0;
1971
1972   GLfloat inner_circum = M_PI * 2 * (iradius + rtick * 2);
1973   GLfloat outer_circum = M_PI * 2 * (iradius + rtick * (letters + 1));
1974   GLfloat facet_length = inner_circum / (26 * dc->codeword_facets);
1975   int outer_facets = ceil (outer_circum / facet_length);
1976
1977   int *histo = (int *) calloc (letters * 26, sizeof(*histo));
1978   XYZ *points = (XYZ *) calloc (letters * outer_facets, sizeof (*points));
1979   int npoints = 0;
1980
1981   for (letter = -1; letter < letters; letter++)
1982     {
1983       if (letter == -1)                 /* Inner starting point */
1984         {
1985           points[npoints].x = iradius;
1986           points[npoints].y = 0;
1987           last_r = iradius;
1988           npoints++;
1989         }
1990       else                              /* Add arc for this letter */
1991         {
1992           int direction = (letter & 1 ? -1 : 1);
1993           int v = (dc->codeword_text[letter] - 'A' + 1);
1994           int dial1 = dial + v * direction;
1995
1996           GLfloat th;
1997           GLfloat th0 = M_PI * 2 / 26 * dial;
1998           GLfloat th1 = M_PI * 2 / 26 * dial1;
1999           GLfloat r  = iradius + rtick * (letter + 2);
2000           GLfloat circum = M_PI * 2 * r;
2001           GLfloat arc_length = circum * v / 26;
2002           int arc_facets = ceil (fabs (arc_length / facet_length));
2003           GLfloat facet_th = (th1 - th0) / arc_facets;
2004
2005           if (arc_facets > outer_facets) abort();
2006
2007           /* Let's put some intermediate facets on the crossbars too,
2008              so that the animation doesn't speed up on those. */
2009           {
2010             GLfloat rr;
2011             for (rr = last_r + facet_length;
2012                  rr <= r - facet_length;
2013                  rr += facet_length)
2014               {
2015                 points[npoints].x = rr * cos (th0);
2016                 points[npoints].y = rr * sin (th0);
2017                 npoints++;
2018               }
2019             last_r = r;
2020           }
2021
2022
2023           for (th = th0;
2024                (th0 < th1
2025                 ? th <= th1 + facet_th
2026                 : th >= th1 + facet_th);
2027                th += facet_th)
2028             {
2029               GLfloat th2 = th;
2030               if (th0 < th1 && th > th1)
2031                 th2 = th1;
2032               if (th0 > th1 && th < th1)
2033                 th2 = th1;
2034               points[npoints].x = r * cos (th2);
2035               points[npoints].y = r * sin (th2);
2036
2037               /* Ugh, add point only if it differs from prev.
2038                  Not sure how this happens. */
2039               if (npoints == 0 ||
2040                   points[npoints-1].x != points[npoints].x ||
2041                   points[npoints-1].y != points[npoints].y)
2042                 npoints++;
2043             }
2044
2045           /* Mark up the histo array to find the outer border. */
2046           {
2047             int i;
2048             for (i = dial;
2049                  (direction > 0
2050                   ? i <= dial1
2051                   : i >= dial1);
2052                   i += direction)
2053               {
2054                 int x = (i + 26) % 26;
2055                 int y;
2056                 for (y = 0; y <= letter; y++)
2057                   histo[y * 26 + x]++;
2058               }
2059           }
2060
2061           dc->codeword_text_points[letter] = npoints;
2062
2063           dial = dial1;
2064         }
2065     }
2066
2067   if (npoints >= letters * outer_facets) abort();
2068
2069 # if 0
2070   { /* Print histo */
2071     int x, y;
2072     for (y = 0; y < letters; y++)
2073       {
2074         fprintf (stderr, "%2d: ", y);
2075         for (x = 0; x < 26; x++)
2076           fprintf (stderr, "%x", histo[y * 26 + x]);
2077         fprintf (stderr, "\n");
2078       }
2079     fprintf (stderr, "\n");
2080   }
2081 # endif
2082
2083
2084   /* Find a gap in the outer edge, to draw guide dots. */
2085   {
2086     int x, y;
2087     int last_row = letters;
2088     int start_dial = -1, end_dial = -1;
2089
2090     for (y = letters-1; y >= 0; y--)
2091       {
2092         for (x = 0; x < 26; x++)
2093           {
2094             if (histo[y * 26 + x] == 0)
2095               {
2096                 if (last_row != y)
2097                   start_dial = end_dial = -1;
2098                 last_row = y;
2099                 if (start_dial == -1)
2100                   start_dial = x;
2101                 end_dial = x;
2102               }
2103           }
2104       }
2105
2106     if (last_row < letters-1 && start_dial >= 0)
2107       {
2108         GLfloat r = iradius + rtick * (last_row + 2);
2109         int i;
2110
2111         dc->codeword_nguides = 0;
2112         dc->codeword_guides = (XYZ *) 
2113           calloc (end_dial - start_dial + 1, sizeof (*dc->codeword_guides));
2114         for (i = start_dial; i <= end_dial; i++)
2115           {
2116             GLfloat th = i * M_PI * 2 / 26;
2117             GLfloat x = r * cos (th);
2118             GLfloat y = r * sin (th);
2119             dc->codeword_guides[dc->codeword_nguides].x = x;
2120             dc->codeword_guides[dc->codeword_nguides].y = y;
2121             dc->codeword_nguides++;
2122           }
2123       }
2124     free (histo);
2125     histo = 0;
2126   }
2127
2128   dc->codeword_path_npoints = npoints;
2129   dc->codeword_path = points;
2130 }
2131
2132
2133 static int
2134 draw_codeword_cap (ModeInfo *mi)
2135 {
2136   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
2137   int wire = MI_IS_WIREFRAME(mi);
2138   int polys = 0;
2139
2140   int segments  = dc->codeword_disc_facets;
2141   GLfloat size  = dc->codeword_spread * dc->codeword_cap_size;
2142   GLfloat width = dc->codeword_line_width / 2;
2143   GLfloat thick = dc->codeword_thickness  / 2;
2144   GLfloat r1 = size + width/2;
2145   GLfloat r2 = size - width/2;
2146   GLfloat facet, th, z;
2147
2148   if (wire) segments = 12;
2149   facet = M_PI * 2 / segments;
2150
2151   glPushMatrix();
2152
2153   /* Top and bottom */
2154
2155   for (z = -thick; z <= thick; z += thick*2)
2156     {
2157       glNormal3f (0, 0, (z < 0 ? -1 : 1));
2158       glFrontFace (z < 0 ? GL_CCW : GL_CW);
2159
2160       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
2161       for (th = 0; th <= M_PI*2; th += facet)
2162         {
2163           GLfloat x = cos (th);
2164           GLfloat y = sin (th);
2165           glVertex3f (r1 * x, r1 * y, z);
2166           glVertex3f (r2 * x, r2 * y, z);
2167         }
2168       glEnd();
2169
2170       if (wire)
2171         {
2172           glBegin (GL_LINE_LOOP);
2173           for (th = 0; th <= M_PI*2; th += facet)
2174             {
2175               GLfloat x = cos (th);
2176               GLfloat y = sin (th);
2177               glVertex3f (r1 * x, r1 * y, z);
2178             }
2179           glEnd();
2180           glBegin (GL_LINE_LOOP);
2181           for (th = 0; th <= M_PI*2; th += facet)
2182             {
2183               GLfloat x = cos (th);
2184               GLfloat y = sin (th);
2185               glVertex3f (r2 * x, r2 * y, z);
2186             }
2187           glEnd();
2188         }
2189     }
2190
2191   /* Inside and outside */
2192
2193   for (z = -1; z <= 1; z += 2)
2194     {
2195       glFrontFace (z < 0 ? GL_CCW : GL_CW);
2196
2197       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
2198       for (th = 0; th <= M_PI*2; th += facet)
2199         {
2200           GLfloat th1 = th + facet;
2201           GLfloat x0 = cos (th);
2202           GLfloat y0 = sin (th);
2203           GLfloat x1 = cos (th1);
2204           GLfloat y1 = sin (th1);
2205           GLfloat r = z < 0 ? r1 : r2;
2206
2207           if (z < 0)
2208             do_normal (r * x0, r * y0,  thick,
2209                        r * x0, r * y0, -thick,
2210                        r * x1, r * y1, -thick);
2211           else
2212             do_normal (r * x1, r * y1,  thick,
2213                        r * x1, r * y1, -thick,
2214                        r * x0, r * y0, -thick);
2215
2216           glVertex3f (r * x0, r * y0,  thick);
2217           glVertex3f (r * x0, r * y0, -thick);
2218         }
2219       glEnd();
2220     }
2221
2222   glPopMatrix();
2223
2224   return polys;
2225 }
2226
2227
2228 static int
2229 draw_codeword_guides (ModeInfo *mi, GLfloat anim_ratio)
2230 {
2231   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
2232   int wire = MI_IS_WIREFRAME(mi);
2233   int polys = 0;
2234
2235   int segments = dc->codeword_disc_facets;
2236   GLfloat s = dc->codeword_line_width / 2;
2237   GLfloat each = 1.0 / dc->codeword_nguides;
2238   int i;
2239
2240   if (wire) segments = 6;
2241
2242   for (i = 0; i < dc->codeword_nguides; i++)
2243     {
2244       GLfloat ratio;
2245       if      (anim_ratio <  i*each)     ratio = 0;
2246       else if (anim_ratio >= (i+1)*each) ratio = 1;
2247       else    ratio = (anim_ratio - i*each) / each;
2248
2249       if (ratio <= 0) continue;
2250       if (ratio == 0) ratio = 0.001;
2251
2252       glPushMatrix();
2253       glTranslatef (dc->codeword_guides[i].x,
2254                     dc->codeword_guides[i].y,
2255                     dc->codeword_guides[i].z);
2256
2257       glScalef (ratio, ratio, ratio);
2258
2259       /* If the line width and thickness are pretty close to each other,
2260          use spheres. Otherwise use tubes. 
2261        */
2262       if (dc->codeword_thickness < dc->codeword_line_width * 1.3 &&
2263           dc->codeword_thickness > dc->codeword_line_width / 1.3)
2264         {
2265           glScalef (s, s, s);
2266           glFrontFace (GL_CCW);
2267           polys += unit_sphere (segments, segments, wire);
2268         }
2269       else
2270         {
2271           polys += tube (0, 0, -dc->codeword_thickness / 2,
2272                          0, 0,  dc->codeword_thickness / 2,
2273                          s, 0, segments, True, True, wire);
2274         }
2275
2276       glPopMatrix();
2277     }
2278
2279   return polys;
2280 }
2281
2282
2283 /* Compute the characters at which the cursor is currently pointing,
2284    and render it on the logo. 
2285  */
2286 static void
2287 codeword_text_output (ModeInfo *mi, GLfloat anim_ratio)
2288 {
2289   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
2290   int i;
2291   int L = strlen (dc->codeword_text);
2292   int point = dc->codeword_path_npoints * anim_ratio;
2293   Bool hit = False;
2294
2295   if (dc->anim_state == CODEWORD_BLANK)
2296     point = 0;
2297
2298   for (i = 0; i < L; i++)
2299     {
2300       if (point >= dc->codeword_text_points[i])
2301         dc->codeword_text_out[i] = dc->codeword_text[i];
2302       else if (hit)
2303         dc->codeword_text_out[i] = 0;
2304       else
2305         {
2306           int steps = dc->codeword_text[i] - 'A' + 1;
2307           int last = (i > 0 ? dc->codeword_text_points[i-1] : 0);
2308           double ratio = ((point - last) /
2309                           (double) (dc->codeword_text_points[i] - last));
2310           char chr = 'A' + (ratio * steps);
2311           if (ratio < 0.1) chr = 0;
2312           dc->codeword_text_out[i] = chr;
2313           hit = True;
2314         }
2315     }
2316   dc->codeword_text_out[i] = 0;
2317
2318   if (*dc->codeword_text_out &&
2319       !strcmp (dc->codeword_text, "CODEWORD"))
2320     {
2321       int i;
2322       int L2 = strlen (dc->codeword_text_out);
2323       GLfloat ss = 0.01;
2324       int ascent, descent;
2325
2326       glPushMatrix();
2327       glColor4fv (dc->codeword_color);
2328       glRotatef (90, 0, 1, 0);
2329       glRotatef (-90, 0, 0, 1);
2330
2331       for (i = 0; i < L2; i++)
2332         {
2333           XCharStruct e;
2334           char buf[2];
2335           glPushMatrix();
2336           glRotatef ((i + 0.5) * 360 / 26.0, 0, 0, 1);
2337
2338 # if 0
2339           {
2340             GLfloat th;
2341             glDisable(GL_LIGHTING);
2342             glBegin(GL_LINES);
2343             glVertex3f(0,0,0);
2344             glVertex3f(0,-4,0);
2345             glEnd();
2346             glBegin(GL_LINE_STRIP);
2347             for (th = M_PI * 1.45; th < M_PI * 1.55; th += 0.1)
2348               {
2349                 GLfloat r = 3.85;
2350                 glVertex3f (r * cos(th), r * sin(th), 0);
2351               }
2352             glEnd();
2353             if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2354           }
2355 # endif
2356
2357           glTranslatef (0, -dc->codeword_spread * (L - 1), 0);
2358           glScalef (ss, ss, ss);
2359           buf[0] = dc->codeword_text_out[i] + ('a' - 'A');
2360           buf[1] = 0;
2361           texture_string_metrics (dc->font, buf, &e, &ascent, &descent);
2362
2363 # ifdef HAVE_MOBILE
2364           /* #### Magic magic magic WTF... */
2365           glScalef (0.5, 0.5, 0.5);
2366 # endif
2367
2368           glTranslatef (-e.width * 1.0,
2369                         -(ascent + descent + e.descent * 2.4), /* #### WTF */
2370                           0);
2371
2372           glScalef (2, 2, 2);
2373
2374 # if 0
2375           glDisable(GL_LIGHTING);
2376           glBegin(GL_LINE_LOOP);
2377           glVertex3f(0, 0, 0);
2378           glVertex3f(e.width, 0, 0);
2379           glVertex3f(e.width, e.ascent + e.descent, 0);
2380           glVertex3f(0, e.ascent + e.descent, 0);
2381           glEnd();
2382           if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2383 # endif
2384
2385           glDisable(GL_CULL_FACE);  /* tell texfont.c to draw both sides */
2386           print_texture_string (dc->font, buf);
2387           glEnable(GL_CULL_FACE);
2388
2389           glPopMatrix();
2390         }
2391       glPopMatrix();
2392     }
2393 }
2394
2395
2396 /* Convert the precomputed path to a thick line of polygons.
2397    We could actually precompute all of these polygons too,
2398    but it's fast enough.
2399  */
2400 static int
2401 draw_codeword_path (ModeInfo *mi)
2402 {
2403   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
2404   int wire = MI_IS_WIREFRAME(mi);
2405   int polys = 0;
2406
2407   GLfloat anim_ratio = (dc->anim_state == CODEWORD_IN ?      dc->anim_ratio :
2408                         dc->anim_state == CODEWORD_OUT ? 1 - dc->anim_ratio :
2409                         dc->anim_state == CODEWORD_BLANK ? 0 :
2410                         1);
2411   int last_anim_point = 0;
2412
2413   GLfloat width = dc->codeword_line_width / 2;
2414   GLfloat thick = dc->codeword_thickness  / 2;
2415   int i, k;
2416   GLfloat j;
2417
2418   int quad_size = (dc->codeword_path_npoints + 1) * 2;
2419   XYZ *quads = (XYZ *) calloc (quad_size, sizeof(*quads));
2420   XYZ *norms = (XYZ *) calloc (quad_size, sizeof(*norms));
2421   int nquads = 0;
2422
2423   for (i = 0; i < dc->codeword_path_npoints; i++)
2424     {
2425       XYZ p1 = dc->codeword_path[i];
2426       XYZ p2 = (i < dc->codeword_path_npoints-1
2427                 ? dc->codeword_path[i+1]
2428                 : dc->codeword_path[i-1]);
2429       XYZ p1a, p1b;
2430
2431       XYZ n;          /* normal of the first line segment */
2432       n.x = -(p2.y - p1.y);
2433       n.y =  (p2.x - p1.x);
2434       n.z =  0;
2435       normalize (&n);
2436
2437       if (i == 0)
2438         {
2439           p1a.x = p1.x - width / 2 * n.x;
2440           p1a.y = p1.y - width / 2 * n.y;
2441           p1a.z = 0;
2442
2443           p1b.x = p1.x + width / 2 * n.x;
2444           p1b.y = p1.y + width / 2 * n.y;
2445           p1b.z = 0;
2446         }
2447       else if (i == dc->codeword_path_npoints - 1)
2448         {
2449           p1b.x = p1.x - width / 2 * n.x;
2450           p1b.y = p1.y - width / 2 * n.y;
2451           p1b.z = 0;
2452
2453           p1a.x = p1.x + width / 2 * n.x;
2454           p1a.y = p1.y + width / 2 * n.y;
2455           p1a.z = 0;
2456         }
2457       else
2458         {
2459           XYZ p0 = dc->codeword_path[i-1];
2460
2461           XYZ t, t0, t1;  /* tangent of corner between two line segments */
2462           XYZ m;          /* miter line: normal of tangent */
2463           GLfloat d;      /* length of miter */
2464
2465           t0.x = p2.x - p1.x;
2466           t0.y = p2.y - p1.y;
2467           t0.z = p2.z - p1.z;
2468           normalize (&t0);
2469
2470           t1.x = p1.x - p0.x;
2471           t1.y = p1.y - p0.y;
2472           t1.z = p1.z - p0.z;
2473           normalize (&t1);
2474
2475           t.x = t0.x + t1.x;
2476           t.y = t0.y + t1.y;
2477           t.z = t0.z + t1.z;
2478           normalize (&t);
2479
2480           m.x = -t.y;
2481           m.y =  t.x;
2482           m.z =  0;
2483
2484           /* find length of miter by projecting it on one of the normals */
2485           d = width / 2 / dot (m, n);
2486
2487           p1a.x = p1.x - d * m.x;
2488           p1a.y = p1.y - d * m.y;
2489           p1a.z = 0;
2490
2491           p1b.x = p1.x + d * m.x;
2492           p1b.y = p1.y + d * m.y;
2493           p1b.z = 0;
2494         }
2495
2496       quads[nquads++] = p1a;
2497       quads[nquads++] = p1b;
2498
2499       if (nquads >= quad_size) abort();
2500
2501       if (i / (double) dc->codeword_path_npoints > anim_ratio)
2502         break;
2503
2504       last_anim_point = i;
2505     }
2506
2507
2508   /* Compute normals for each point along the interior edge */
2509   for (k = 0; k <= 1; k++)
2510     {
2511       for (i = k; i < nquads-2; i += 2)
2512         {
2513           XYZ p1a = quads[i];
2514           XYZ p2a = quads[i+2];
2515           XYZ p1b = p1a;
2516           XYZ p2b = p2a;
2517           p1a.z =  thick;  /* a: top */
2518           p1b.z = -thick;  /* b: bottom */
2519           p2a.z =  thick;
2520           p2b.z = -thick;
2521
2522           norms[i] = (k == 0
2523                       ? calc_normal (p1a, p1b, p2b)
2524                       : calc_normal (p2a, p2b, p1a));
2525         }
2526     }
2527
2528   glPushMatrix();
2529   glColor4fv (dc->codeword_color);
2530   glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dc->codeword_color);
2531
2532 # ifdef HAVE_MOBILE  /* Make the whole thing fit on the phone screen */
2533   {
2534     GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
2535     glScalef (0.9, 0.9, 0.9);
2536     if (size <= 768)  /* iPad retina / iPhone 6 */
2537       glScalef (0.7, 0.7, 0.7);
2538   }
2539 # endif
2540
2541   codeword_text_output (mi, anim_ratio);
2542
2543   glRotatef (90, 1, 0, 0);
2544   glRotatef (90, 0, 1, 0);
2545   glRotatef (-90, 0, 0, 1);
2546   glScalef (0.8, 0.8, 0.8);
2547
2548   glNormal3f (0, 0, -1);
2549
2550   if (anim_ratio <= 0)
2551     {
2552       polys += draw_codeword_cap (mi);
2553       goto DONE;
2554     }
2555
2556 # if 0
2557   glColor3f (1, 0, 0);
2558   glBegin(GL_LINE_STRIP);
2559   for (i = 0; i < dc->codeword_path_npoints; i++)
2560     {
2561       glVertex3f (dc->codeword_path[i].x,
2562                   dc->codeword_path[i].y,
2563                   dc->codeword_path[i].z);
2564       polys++;
2565     }
2566   glEnd();
2567   glColor4fv (dc->codeword_color);
2568 # endif
2569
2570   if (wire)
2571     {
2572       int k;
2573       GLfloat j;
2574       for (i = 0; i <= 1; i++)
2575         for (j = -thick; j <= thick; j += thick*2)
2576           {
2577             glBegin (GL_LINE_STRIP);
2578             for (k = i; k < nquads; k += 2)
2579               {
2580                 glVertex3f (quads[k].x, quads[k].y, j);
2581                 polys++;
2582               }
2583             glEnd();
2584           }
2585     }
2586
2587   /* Top and bottom */
2588
2589   for (j = -thick; j <= thick; j += thick*2)
2590     {
2591       if (j < 0)
2592         {
2593           glNormal3f (0, 0, -1);
2594           glFrontFace (GL_CW);
2595         }
2596       else
2597         {
2598           glNormal3f (0, 0, 1);
2599           glFrontFace (GL_CCW);
2600         }
2601       glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
2602       for (i = 0; i < nquads; i += 2)
2603         {
2604           glVertex3f (quads[i+1].x, quads[i+1].y, j);
2605           glVertex3f (quads[i].x,   quads[i].y,   j);
2606           polys++;
2607         }
2608       glEnd();
2609     }
2610
2611   /* Edges */
2612
2613   for (k = 0; k <= 1; k++)
2614     {
2615       if (k > 0)
2616         {
2617           glNormal3f (0, 0, -1);
2618           glFrontFace (GL_CW);
2619         }
2620       else
2621         {
2622           glNormal3f (0, 0, 1);
2623           glFrontFace (GL_CCW);
2624         }
2625
2626       glBegin (wire ? GL_LINES : GL_QUADS);
2627       for (i = k; i < nquads; i += 2)
2628         {
2629           XYZ p1a = quads[i];
2630           XYZ p2a = (i < nquads-2) ? quads[i+2] : p1a;
2631           XYZ p1b = p1a;
2632           XYZ p2b = p2a;
2633
2634           XYZ n1 = norms[i];
2635           XYZ n2 = (i < nquads-2) ? norms[i+2] : n1;
2636
2637           /* If the two normals are very similar, smooth the face.
2638              If they are different, it's a sharp turn, and use the
2639              same normal for both edges (not quite right, but close).
2640           */
2641           GLfloat angle = vector_angle (n1.x, n1.y, n1.z,
2642                                         n2.x, n2.y, n2.z);
2643           GLfloat pointy = 0.8;
2644
2645           p1a.z =  thick;
2646           p1b.z = -thick;
2647           p2a.z =  thick;
2648           p2b.z = -thick;
2649
2650           glNormal3f (n1.x, n1.y, n1.z);
2651           glVertex3f (p1a.x, p1a.y, p1a.z);
2652           glVertex3f (p1b.x, p1b.y, p1b.z);
2653
2654           if (angle < pointy)
2655             glNormal3f (n2.x, n2.y, n2.z);
2656           glVertex3f (p2b.x, p2b.y, p2b.z);
2657           glVertex3f (p2a.x, p2a.y, p2a.z);
2658           polys++;
2659         }
2660       glEnd();
2661     }
2662
2663
2664   /* Only draw the guides when the path is almost complete;
2665      fade them in and out based on completeness. */
2666   {
2667     GLfloat size = 0.95;
2668     GLfloat r = (anim_ratio > size
2669                  ? (anim_ratio - size) / (1 - size)
2670                  : 0);
2671     polys += draw_codeword_guides (mi, r);
2672   }
2673
2674
2675   /* Draw the start and end caps */
2676   {
2677     int i;
2678     GLfloat x, y, z, x2, y2, z2, X, Y, Z;
2679     GLfloat r = dc->codeword_spread * dc->codeword_cap_size;
2680
2681     i = 0;
2682     x = dc->codeword_path[i].x;
2683     y = dc->codeword_path[i].y;
2684     z = dc->codeword_path[i].z;
2685
2686     x -= r;
2687
2688     glPushMatrix();
2689     glTranslatef (x, y, z);
2690     polys += draw_codeword_cap (mi);
2691     glPopMatrix();
2692
2693     /* end cap */
2694
2695     i = last_anim_point + 1;
2696     if (i > dc->codeword_path_npoints - 1)
2697       i = dc->codeword_path_npoints - 1;
2698
2699     x = dc->codeword_path[i].x;
2700     y = dc->codeword_path[i].y;
2701     z = dc->codeword_path[i].z;
2702
2703     i--;
2704     x2 = dc->codeword_path[i].x;
2705     y2 = dc->codeword_path[i].y;
2706     z2 = dc->codeword_path[i].z;
2707
2708     X = (x2 - x);
2709     Y = (y2 - y);
2710     Z = (z2 - z);
2711
2712     glPushMatrix();
2713     glTranslatef (x, y, z);
2714     glRotatef (-atan2 (X, Y)               * (180 / M_PI), 0, 0, 1);
2715     glRotatef ( atan2 (Z, sqrt(X*X + Y*Y)) * (180 / M_PI), 1, 0, 0);
2716     glTranslatef (0, -r, 0);
2717     polys += draw_codeword_cap (mi);
2718     glPopMatrix();
2719   }
2720
2721  DONE:
2722
2723   glPopMatrix();
2724
2725   free (quads);
2726   free (norms);
2727
2728   return polys;
2729 }
2730
2731 #endif /* CW */
2732
2733 \f
2734 /* Window management, etc
2735  */
2736 ENTRYPOINT void
2737 reshape_logo (ModeInfo *mi, int width, int height)
2738 {
2739   GLfloat h = (GLfloat) height / (GLfloat) width;
2740
2741   glViewport (0, 0, (GLint) width, (GLint) height);
2742
2743   glMatrixMode(GL_PROJECTION);
2744   glLoadIdentity();
2745   gluPerspective (30.0, 1/h, 1.0, 100.0);
2746
2747   glMatrixMode(GL_MODELVIEW);
2748   glLoadIdentity();
2749   gluLookAt( 0.0, 0.0, 30.0,
2750              0.0, 0.0, 0.0,
2751              0.0, 1.0, 0.0);
2752
2753 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
2754   {
2755     int o = (int) current_device_rotation();
2756     if (o != 0 && o != 180 && o != -180)
2757       glScalef (1/h, 1/h, 1/h);  /* #### Why does this change the lighting? */
2758   }
2759 # endif
2760
2761   glClear(GL_COLOR_BUFFER_BIT);
2762 }
2763
2764
2765 static void
2766 gl_init (ModeInfo *mi)
2767 {
2768 /*  logo_configuration *dc = &dcs[MI_SCREEN(mi)]; */
2769   int wire = MI_IS_WIREFRAME(mi);
2770
2771   GLfloat position[]  = {0, 0, 0, 0};
2772   GLfloat direction[] = {3, -1, -3};
2773
2774   position[0] = -direction[0];
2775   position[1] = -direction[1];
2776   position[2] = -direction[2];
2777
2778   if (!wire)
2779     {
2780       glLightfv(GL_LIGHT0, GL_POSITION, position);
2781       glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
2782       glShadeModel(GL_SMOOTH);
2783       glEnable(GL_NORMALIZE);
2784       glEnable(GL_CULL_FACE);
2785       glEnable(GL_LIGHTING);
2786       glEnable(GL_LIGHT0);
2787       glEnable(GL_DEPTH_TEST);
2788     }
2789 }
2790
2791
2792 ENTRYPOINT void 
2793 init_logo (ModeInfo *mi)
2794 {
2795   logo_configuration *dc;
2796   int do_gasket = get_boolean_resource(mi->dpy, "doGasket", "Boolean");
2797   int do_helix = get_boolean_resource(mi->dpy, "doHelix", "Boolean");
2798   int do_ladder = (do_helix && 
2799                    get_boolean_resource(mi->dpy, "doLadder", "Boolean"));
2800   int do_frame = get_boolean_resource(mi->dpy, "doFrame", "Boolean");
2801   GLfloat helix_rot = 147.0;
2802
2803   if (!do_gasket && !do_helix)
2804     {
2805       fprintf (stderr, "%s: no helix or gasket?\n", progname);
2806       exit (1);
2807     }
2808
2809   MI_INIT (mi, dcs);
2810
2811   dc = &dcs[MI_SCREEN(mi)];
2812
2813   if ((dc->glx_context = init_GL(mi)) != NULL) {
2814     gl_init(mi);
2815     reshape_logo (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2816   }
2817
2818   dc->wall_facets    = get_integer_resource(mi->dpy, "wallFacets",  "Integer");
2819   dc->bar_facets     = get_integer_resource(mi->dpy, "barFacets",   "Integer");
2820   dc->clockwise      = get_boolean_resource(mi->dpy, "clockwise",   "Boolean");
2821   dc->turns          = get_float_resource(mi->dpy, "turns",         "Float");
2822   dc->turn_spacing   = get_float_resource(mi->dpy, "turnSpacing",   "Float");
2823   dc->bar_spacing    = get_float_resource(mi->dpy, "barSpacing",    "Float");
2824   dc->wall_height    = get_float_resource(mi->dpy, "wallHeight",    "Float");
2825   dc->wall_thickness = get_float_resource(mi->dpy, "wallThickness", "Float");
2826   dc->bar_thickness  = get_float_resource(mi->dpy, "barThickness",  "Float");
2827   dc->wall_taper     = get_float_resource(mi->dpy, "wallTaper",     "Float");
2828
2829   dc->gasket_size      = get_float_resource(mi->dpy,"gasketSize",     "Float");
2830   dc->gasket_depth     = get_float_resource(mi->dpy,"gasketDepth",    "Float");
2831   dc->gasket_thickness = get_float_resource(mi->dpy,"gasketThickness","Float");
2832
2833   dc->frame_size      = get_float_resource(mi->dpy, "frameSize",      "Float");
2834   dc->frame_depth     = get_float_resource(mi->dpy, "frameDepth",     "Float");
2835   dc->frame_thickness = get_float_resource(mi->dpy, "frameThickness", "Float");
2836   dc->triangle_size   = get_float_resource(mi->dpy, "triangleSize",   "Float");
2837
2838   dc->speed           = get_float_resource(mi->dpy, "speed",          "Float");
2839 # ifdef CW
2840   dc->codeword_text   = get_string_resource(mi->dpy, "text",         "String");
2841   dc->codeword_text   = codeword_simplify_text (dc->codeword_text);
2842   dc->codeword_text_out =
2843     calloc (strlen(dc->codeword_text) + 1, sizeof(*dc->codeword_text_out));
2844   dc->codeword_text_points =
2845     (int *) calloc (strlen(dc->codeword_text) + 1,
2846                     sizeof(*dc->codeword_text_points));
2847
2848   dc->codeword_facets = get_integer_resource(mi->dpy, "cwFacets",  "Integer");
2849   dc->codeword_disc_facets = get_integer_resource(mi->dpy,
2850                                                   "cwDiscFacets",  "Integer");
2851   dc->codeword_spread = get_float_resource(mi->dpy, "cwSpread",    "Float");
2852   dc->codeword_line_width = get_float_resource(mi->dpy, "cwLineWidth", "Float");
2853   dc->codeword_thickness  = get_float_resource(mi->dpy, "cwThickness", "Float");
2854   dc->codeword_cap_size = get_float_resource(mi->dpy, "cwCapSize",     "Float");
2855 # endif
2856
2857   {
2858     char *s = get_string_resource (MI_DISPLAY (mi), "mode", "String");
2859     if (!s || !*s || !strcasecmp (s, "helix"))
2860       dc->mode = HELIX;
2861     else if (!strcasecmp (s, "pizza"))
2862       dc->mode = PIZZA;
2863     else if (!strcasecmp (s, "both"))
2864       dc->mode = HELIX_AND_PIZZA;
2865 # ifdef CW
2866     else if (!strcasecmp (s, "codeword"))
2867       dc->mode = CODEWORD_IN;
2868 # endif
2869     else
2870       {
2871         fprintf (stderr,
2872                "%s: mode must be helix, pizza or both, not \"%s\"\n", 
2873                  progname, s);
2874         exit (1);
2875       }
2876     if (s) free (s);
2877
2878     dc->anim_state = (dc->mode == HELIX_AND_PIZZA
2879                       ? ((random() & 1) ? HELIX : PIZZA)
2880                       : dc->mode);
2881     dc->anim_ratio = 0;
2882   }
2883
2884 # ifdef CW
2885   if (dc->mode == CODEWORD_IN)
2886     dc->font = load_texture_font (MI_DISPLAY(mi), "cwFont");
2887 # endif
2888
2889   {
2890     XColor xcolor;
2891
2892     char *color_name =
2893       get_string_resource (mi->dpy, "foreground", "Foreground");
2894     char *s2;
2895     for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
2896       if (*s2 == ' ' || *s2 == '\t')
2897         *s2 = 0;
2898       else
2899         break;
2900
2901     if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
2902       {
2903         fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
2904         exit (1);
2905       }
2906
2907     dc->color[0] = xcolor.red   / 65535.0;
2908     dc->color[1] = xcolor.green / 65535.0;
2909     dc->color[2] = xcolor.blue  / 65535.0;
2910     dc->color[3] = 1.0;
2911
2912     color_name = get_string_resource (mi->dpy, "cwForeground", "Foreground");
2913     for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
2914       if (*s2 == ' ' || *s2 == '\t')
2915         *s2 = 0;
2916       else
2917         break;
2918
2919     if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
2920       {
2921         fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
2922         exit (1);
2923       }
2924
2925 # ifdef CW
2926     dc->codeword_color[0] = xcolor.red   / 65535.0;
2927     dc->codeword_color[1] = xcolor.green / 65535.0;
2928     dc->codeword_color[2] = xcolor.blue  / 65535.0;
2929     dc->codeword_color[3] = 1.0;
2930
2931     color_name = get_string_resource (mi->dpy, "cwBackground", "Background");
2932     for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
2933       if (*s2 == ' ' || *s2 == '\t')
2934         *s2 = 0;
2935       else
2936         break;
2937
2938     if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
2939       {
2940         fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
2941         exit (1);
2942       }
2943
2944     dc->codeword_bg[0] = xcolor.red   / 65535.0;
2945     dc->codeword_bg[1] = xcolor.green / 65535.0;
2946     dc->codeword_bg[2] = xcolor.blue  / 65535.0;
2947     dc->codeword_bg[3] = 1.0;
2948 # endif /* CW */
2949   }
2950
2951   dc->trackball = gltrackball_init (False);
2952
2953   dc->gasket_spinnerx.probability = 0.1;
2954   dc->gasket_spinnery.probability = 0.1;
2955   dc->gasket_spinnerz.probability = 1.0;
2956   dc->gasket_spinnerx.easement    = 0.08;
2957   dc->gasket_spinnery.easement    = 0.08;
2958   dc->gasket_spinnerz.easement    = 0.08;
2959
2960   dc->helix_spinnerz.probability  = 0.6;
2961   dc->helix_spinnerz.easement     = 0.2;
2962
2963   dc->pizza_spinnerz.probability  = 0.6;
2964   dc->pizza_spinnery.probability  = 0.6;
2965   dc->pizza_spinnerz.easement     = 0.2;
2966   dc->pizza_spinnery.easement     = 0.2;
2967
2968   dc->frame_spinner.probability   = 5.0;
2969   dc->frame_spinner.easement      = 0.2;
2970
2971   dc->scene_spinnerx.probability  = 0.1;
2972   dc->scene_spinnery.probability  = 0.0;
2973   dc->scene_spinnerx.easement     = 0.1;
2974   dc->scene_spinnery.easement     = 0.1;
2975
2976 # ifdef CW
2977   if (dc->mode == CODEWORD_IN)
2978     {
2979       double tilt_speed = 0.003;
2980       dc->scene_rot = make_rotator (0, 0, 0, 0, tilt_speed, True);
2981     }
2982 # endif
2983
2984   /* start the frame off-screen */
2985   dc->frame_spinner.spinning_p = True;
2986   dc->frame_spinner.position = 0.3;
2987   dc->frame_spinner.speed = 0.001;
2988
2989   if (dc->speed > 0)    /* start off with the gasket in motion */
2990     {
2991       dc->gasket_spinnerz.spinning_p = True;
2992       dc->gasket_spinnerz.speed = (0.002
2993                                    * ((random() & 1) ? 1 : -1)
2994                                    * dc->speed);
2995     }
2996
2997 # ifdef DXF_OUTPUT_HACK
2998   {
2999 #  if 0
3000     dc->frame_depth = dc->gasket_depth;
3001     dxf_layer = 1;
3002     dxf_color = 3;
3003     dxf_start();
3004     glPushMatrix();
3005     glRotatef(90, 1, 0, 0);
3006     glRotatef(90, 0, 0, 1);
3007     make_pizza (dc, 0, 0);
3008
3009     glPushMatrix();
3010     glRotatef(helix_rot, 0, 0, 1);
3011     make_ladder (dc, 0, 0);
3012     make_helix  (dc, 0, 0);
3013     glRotatef (180, 0, 0, 1);
3014     make_helix  (dc, 0, 0);
3015     glPopMatrix();
3016
3017     dxf_layer++;
3018     make_gasket (dc, 0);
3019     dxf_layer++;
3020     make_frame (dc, 0);
3021     glPopMatrix();
3022     dxf_end();
3023 #  else
3024     dxf_start();
3025     glPushMatrix();
3026     glRotatef(90, 1, 0, 0);
3027     glRotatef(90, 0, 0, 1);
3028     dc->anim_state = CODEWORD;
3029     make_codeword_path (mi);
3030     draw_codeword_path (mi);
3031     glPopMatrix();
3032     dxf_end();
3033 #  endif
3034   }
3035 # endif
3036
3037   glPushMatrix();
3038   dc->helix_list = glGenLists (1);
3039   glNewList (dc->helix_list, GL_COMPILE);
3040   glRotatef(helix_rot, 0, 0, 1);
3041   if (do_ladder) dc->polys[0] += make_ladder (dc, 0, 0);
3042   if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
3043   glRotatef(180, 0, 0, 1);
3044   if (do_helix)  dc->polys[0] += make_helix  (dc, 0, 0);
3045   glEndList ();
3046   glPopMatrix();
3047
3048   glPushMatrix();
3049   dc->helix_list_wire = glGenLists (1);
3050   glNewList (dc->helix_list_wire, GL_COMPILE);
3051 /*  glRotatef(helix_rot, 0, 0, 1); wtf? */
3052   if (do_ladder) dc->polys[1] += make_ladder (dc, 1, 1);
3053   if (do_helix)  dc->polys[1] += make_helix  (dc, 1, 1);
3054   glRotatef(180, 0, 0, 1);
3055   if (do_helix)  dc->polys[1] += make_helix  (dc, 1, 1);
3056   glEndList ();
3057   glPopMatrix();
3058
3059   glPushMatrix();
3060   dc->helix_list_facetted = glGenLists (1);
3061   glNewList (dc->helix_list_facetted, GL_COMPILE);
3062   glRotatef(helix_rot, 0, 0, 1);
3063   if (do_ladder) dc->polys[2] += make_ladder (dc, 1, 0);
3064   if (do_helix)  dc->polys[2] += make_helix  (dc, 1, 0);
3065   glRotatef(180, 0, 0, 1);
3066   if (do_helix)  dc->polys[2] += make_helix  (dc, 1, 0);
3067   glEndList ();
3068   glPopMatrix();
3069
3070   dc->pizza_list = glGenLists (1);
3071   glNewList (dc->pizza_list, GL_COMPILE);
3072   if (do_frame) dc->polys[5] += make_pizza (dc, 0, 0);
3073   glEndList ();
3074
3075   dc->pizza_list_wire = glGenLists (1);
3076   glNewList (dc->pizza_list_wire, GL_COMPILE);
3077   if (do_frame) dc->polys[6] += make_pizza (dc, 1, 1);
3078   glEndList ();
3079
3080   dc->pizza_list_facetted = glGenLists (1);
3081   glNewList (dc->pizza_list_facetted, GL_COMPILE);
3082   if (do_frame) dc->polys[6] += make_pizza (dc, 1, 0);
3083   glEndList ();
3084
3085   dc->gasket_list = glGenLists (1);
3086   glNewList (dc->gasket_list, GL_COMPILE);
3087   if (do_gasket) dc->polys[3] += make_gasket (dc, 0);
3088   glEndList ();
3089
3090   dc->gasket_list_wire = glGenLists (1);
3091   glNewList (dc->gasket_list_wire, GL_COMPILE);
3092   if (do_gasket) dc->polys[4] += make_gasket (dc, 1);
3093   glEndList ();
3094
3095   dc->frame_list = glGenLists (1);
3096   glNewList (dc->frame_list, GL_COMPILE);
3097   if (do_frame) dc->polys[5] += make_frame (dc, 0);
3098   glEndList ();
3099
3100   dc->frame_list_wire = glGenLists (1);
3101   glNewList (dc->frame_list_wire, GL_COMPILE);
3102   if (do_frame) dc->polys[6] += make_frame (dc, 1);
3103   glEndList ();
3104
3105 # ifdef CW
3106   make_codeword_path (mi);
3107 # endif
3108
3109   /* When drawing both solid and wireframe objects,
3110      make sure the wireframe actually shows up! */
3111   glEnable (GL_POLYGON_OFFSET_FILL);
3112   glPolygonOffset (1.0, 1.0);
3113 }
3114
3115
3116 ENTRYPOINT Bool
3117 logo_handle_event (ModeInfo *mi, XEvent *event)
3118 {
3119   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
3120
3121   if (gltrackball_event_handler (event, dc->trackball,
3122                                  MI_WIDTH (mi), MI_HEIGHT (mi),
3123                                  &dc->button_down_p))
3124     return True;
3125   else if (event->xany.type == KeyPress)
3126     {
3127       KeySym keysym;
3128       char c = 0;
3129       XLookupString (&event->xkey, &c, 1, &keysym, 0);
3130       if (c == ' ' || c == '\t')
3131         {
3132           switch (dc->anim_state) {
3133           case HELIX:    
3134             dc->anim_state = HELIX_OUT;
3135             dc->anim_ratio = 0.0;
3136             return True;
3137           case PIZZA:
3138             dc->anim_state = PIZZA_OUT;
3139             dc->anim_ratio = 0.0;
3140             return True;
3141 # ifdef CW
3142           case CODEWORD:
3143             dc->anim_state = CODEWORD_OUT;
3144             dc->anim_ratio = 0.0;
3145             return True;
3146 # endif
3147           default:
3148             break;
3149           }
3150         }
3151     }
3152
3153   return False;
3154 }
3155
3156
3157 static GLfloat
3158 spinner_ease (GLfloat x)
3159 {
3160   /* Smooth curve up, ending at slope = 1. */
3161   return cos ((x/2 + 1) * M_PI) + 1;
3162 }
3163
3164
3165 static void
3166 tick_spinner (ModeInfo *mi, spinner *s)
3167 {
3168   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
3169
3170   if (dc->speed == 0) return;
3171   if (dc->button_down_p) return;
3172
3173   if (s->spinning_p)
3174     {
3175       s->position += s->speed;
3176       if (s->position >=  1.0 || s->position <= 0.0)
3177         {
3178           s->position = 0;
3179           s->position_eased = 0;
3180           s->spinning_p = False;
3181         }
3182       else if (s->easement > 0 && s->position <= s->easement)
3183         s->position_eased = (s->easement *
3184                              spinner_ease (s->position / s->easement));
3185       else if (s->easement > 0 && s->position >= 1-s->easement)
3186         s->position_eased = (1 - s->easement *
3187                              spinner_ease ((1 - s->position) / s->easement));
3188       else
3189         s->position_eased = s->position;
3190     }
3191   else if (s->probability &&
3192            (random() % (int) (PROBABILITY_SCALE / s->probability)) == 0)
3193     {
3194       GLfloat ss = 0.004;
3195       s->spinning_p = True;
3196       s->position = 0;
3197       do {
3198         s->speed = dc->speed * (frand(ss/3) + frand(ss/3) + frand(ss/3));
3199       } while (s->speed <= 0);
3200       if (random() & 1)
3201         {
3202           s->speed = -s->speed;
3203           s->position = 1.0;
3204         }
3205     }
3206 }
3207
3208
3209 static void
3210 link_spinners (ModeInfo *mi, spinner *s0, spinner *s1)
3211 {
3212   if (s0->spinning_p && !s1->spinning_p)
3213     {
3214       GLfloat op = s1->probability;
3215       s1->probability = PROBABILITY_SCALE;
3216       tick_spinner (mi, s1);
3217       s1->probability = op;
3218     }
3219 }
3220
3221
3222 ENTRYPOINT void
3223 draw_logo (ModeInfo *mi)
3224 {
3225   logo_configuration *dc = &dcs[MI_SCREEN(mi)];
3226   Display *dpy = MI_DISPLAY(mi);
3227   Window window = MI_WINDOW(mi);
3228   int wire = MI_IS_WIREFRAME(mi);
3229   GLfloat gcolor[4];
3230   GLfloat specular[]  = {0.8, 0.8, 0.8, 1.0};
3231   GLfloat shininess   = 50.0;
3232   Bool pizza_p;
3233 # ifdef CW
3234   Bool codeword_p;
3235 # endif
3236
3237   if (!dc->glx_context)
3238     return;
3239
3240   mi->polygon_count = 0;
3241   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(dc->glx_context));
3242
3243   if (!wire &&
3244       dc->wire_overlay == 0 &&
3245       (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
3246     dc->wire_overlay = ((random() % 200) +
3247                         (random() % 200) +
3248                         (random() % 200));
3249       
3250   tick_spinner (mi, &dc->gasket_spinnerx);
3251   tick_spinner (mi, &dc->gasket_spinnery);
3252   tick_spinner (mi, &dc->gasket_spinnerz);
3253   tick_spinner (mi, &dc->helix_spinnerz);
3254   tick_spinner (mi, &dc->pizza_spinnery);
3255   tick_spinner (mi, &dc->pizza_spinnerz);
3256   tick_spinner (mi, &dc->scene_spinnerx);
3257   tick_spinner (mi, &dc->scene_spinnery);
3258   tick_spinner (mi, &dc->frame_spinner);
3259   link_spinners (mi, &dc->scene_spinnerx, &dc->scene_spinnery);
3260
3261   switch (dc->anim_state)
3262     {
3263     case HELIX:
3264       if (dc->mode == HELIX_AND_PIZZA &&
3265           (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
3266         dc->anim_state = HELIX_OUT;
3267       break;
3268
3269     case HELIX_OUT:
3270       dc->anim_ratio += 0.1 * dc->speed;
3271       if (dc->anim_ratio >= 1.0)
3272         {
3273           dc->anim_ratio = 0.0;
3274           dc->anim_state = PIZZA_IN;
3275         }
3276       break;
3277
3278     case PIZZA_IN:
3279       dc->anim_ratio += 0.1 * dc->speed;
3280       if (dc->anim_ratio >= 1.0)
3281         {
3282           dc->anim_ratio = 0.0;
3283           dc->anim_state = PIZZA;
3284         }
3285       break;
3286
3287     case PIZZA:
3288       if (dc->mode == HELIX_AND_PIZZA &&
3289           (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
3290         dc->anim_state = PIZZA_OUT;
3291       break;
3292
3293     case PIZZA_OUT:
3294       dc->anim_ratio += 0.1 * dc->speed;
3295       if (dc->anim_ratio >= 1.0)
3296         {
3297           dc->anim_ratio = 0.0;
3298           dc->anim_state = HELIX_IN;
3299         }
3300       break;
3301
3302     case HELIX_IN:
3303       dc->anim_ratio += 0.1 * dc->speed;
3304       if (dc->anim_ratio >= 1.0)
3305         {
3306           dc->anim_ratio = 0.0;
3307           dc->anim_state = HELIX;
3308         }
3309       break;
3310
3311
3312 # ifdef CW
3313     case CODEWORD_IN:
3314       dc->scene_spinnerx.probability = 0.2;
3315       dc->scene_spinnery.probability = 0.05;
3316       if (! dc->button_down_p)
3317         dc->anim_ratio += 0.004 * dc->speed;
3318       if (dc->anim_ratio >= 1.0)
3319         {
3320           dc->anim_state = CODEWORD;
3321           dc->anim_ratio = frand (0.5);
3322         }
3323       break;
3324
3325     case CODEWORD:
3326       dc->scene_spinnerx.probability = 0.5;
3327       dc->scene_spinnery.probability = 0.2;
3328       if (! dc->button_down_p)
3329         dc->anim_ratio += (0.0005 + frand(0.002)) * dc->speed;
3330       if (dc->anim_ratio >= 1.0)
3331         {
3332           dc->anim_ratio = 0.0;
3333           dc->anim_state = CODEWORD_OUT;
3334         }
3335       break;
3336
3337     case CODEWORD_OUT:
3338       dc->scene_spinnerx.probability = 0;
3339       dc->scene_spinnery.probability = 0;
3340       if (! dc->button_down_p)
3341         dc->anim_ratio += 0.02 * dc->speed;
3342       if (dc->anim_ratio >= 1.0)
3343         {
3344           dc->anim_ratio = 0.0;
3345           dc->anim_state = CODEWORD_BLANK;
3346         }
3347       break;
3348
3349     case CODEWORD_BLANK:
3350       dc->scene_spinnerx.probability = 0;
3351       dc->scene_spinnery.probability = 0;
3352       if (! dc->button_down_p)
3353         dc->anim_ratio += 0.01 * dc->speed;
3354       if (dc->anim_ratio >= 1.0)
3355         {
3356           dc->anim_ratio = 0.0;
3357           dc->anim_state = CODEWORD_IN;
3358         }
3359       break;
3360 # endif /* CW */
3361
3362     default:
3363       abort();
3364       break;
3365     }
3366
3367   pizza_p = (dc->anim_state == PIZZA ||
3368              dc->anim_state == PIZZA_IN ||
3369              dc->anim_state == PIZZA_OUT);
3370
3371 # ifdef CW
3372   codeword_p = (dc->anim_state == CODEWORD ||
3373                 dc->anim_state == CODEWORD_IN ||
3374                 dc->anim_state == CODEWORD_OUT ||
3375                 dc->anim_state == CODEWORD_BLANK);
3376 # endif
3377
3378   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
3379
3380   glPushMatrix ();
3381   glRotatef(current_device_rotation(), 0, 0, 1);
3382   {
3383     GLfloat scale = 1.8;
3384     glScalef(scale, scale, scale);
3385
3386     glColor3f(dc->color[0], dc->color[1], dc->color[2]);
3387
3388
3389     /* Draw frame before trackball rotation */
3390 # ifdef CW
3391     if (! codeword_p)
3392 # endif
3393       {
3394         GLfloat p = (dc->frame_spinner.position_eased >= 0
3395                      ? dc->frame_spinner.position_eased
3396                      : -dc->frame_spinner.position_eased);
3397         GLfloat size = (p > 0.5 ? 1-p : p);
3398         scale = 1 + (size * 10);
3399         glPushMatrix();
3400         /* gltrackball_rotate (dc->trackball); */
3401         glRotatef(90, 1, 0, 0);
3402         glRotatef(90, 0, 0, 1);
3403
3404         glScalef (1, scale, scale);
3405         if (wire)
3406           {
3407             glDisable (GL_LIGHTING);
3408             glCallList (dc->frame_list_wire);
3409             mi->polygon_count += dc->polys[6];
3410           }
3411         else if (dc->wire_overlay != 0)
3412           {
3413             glCallList (dc->frame_list);
3414             glDisable (GL_LIGHTING);
3415             glColor3fv (dc->color);
3416             glCallList (dc->frame_list_wire);
3417             mi->polygon_count += dc->polys[6];
3418             if (!wire) glEnable (GL_LIGHTING);
3419           }
3420         else
3421           {
3422             glCallList (dc->frame_list);
3423             mi->polygon_count += dc->polys[5];
3424           }
3425         glPopMatrix();
3426       }
3427
3428     gltrackball_rotate (dc->trackball);
3429
3430     glRotatef(90, 1, 0, 0);
3431     glRotatef(90, 0, 0, 1);
3432
3433 # ifdef CW
3434     if (! codeword_p)
3435 # endif
3436       {
3437         glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
3438         glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
3439
3440         glPushMatrix();
3441
3442         glRotatef (360 * dc->gasket_spinnerx.position_eased, 0, 1, 0);
3443         glRotatef (360 * dc->gasket_spinnery.position_eased, 0, 0, 1);
3444         glRotatef (360 * dc->gasket_spinnerz.position_eased, 1, 0, 0);
3445
3446         memcpy (gcolor, dc->color, sizeof (dc->color));
3447         if (dc->wire_overlay != 0)
3448           {
3449             gcolor[0]   = gcolor[1]   = gcolor[2]   = 0;
3450             specular[0] = specular[1] = specular[2] = 0;
3451             shininess = 0;
3452           }
3453         glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
3454         glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  specular);
3455         glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS, shininess);
3456
3457         if (wire)
3458           {
3459             glDisable (GL_LIGHTING);
3460             glCallList (dc->gasket_list_wire);
3461             mi->polygon_count += dc->polys[4];
3462           }
3463         else if (dc->wire_overlay != 0)
3464           {
3465             glCallList (dc->gasket_list);
3466             glDisable (GL_LIGHTING);
3467             glColor3fv (dc->color);
3468             glCallList (dc->gasket_list_wire);
3469             mi->polygon_count += dc->polys[4];
3470             if (!wire) glEnable (GL_LIGHTING);
3471             glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
3472           }
3473         else
3474           {
3475             glCallList (dc->gasket_list);
3476             mi->polygon_count += dc->polys[3];
3477           }
3478         glPopMatrix();
3479
3480         if (pizza_p)
3481           {
3482             glRotatef (360 * dc->pizza_spinnery.position_eased, 1, 0, 0);
3483             glRotatef (360 * dc->pizza_spinnerz.position_eased, 0, 0, 1);
3484           }
3485         else
3486           {
3487             glRotatef (360 * dc->helix_spinnerz.position_eased, 0, 0, 1);
3488           }
3489
3490         scale = ((dc->anim_state == PIZZA_IN || dc->anim_state == HELIX_IN)
3491                  ? dc->anim_ratio
3492                  : ((dc->anim_state == PIZZA_OUT || dc->anim_state == HELIX_OUT)
3493                     ? 1.0 - dc->anim_ratio
3494                     : 1.0));
3495         if (scale <= 0) scale = 0.001;
3496         glScalef (scale, scale, scale);
3497
3498         if (wire)
3499           {
3500             glDisable (GL_LIGHTING);
3501             if (pizza_p)
3502               glCallList (dc->pizza_list_wire);
3503             else
3504               glCallList (dc->helix_list_wire);
3505             mi->polygon_count += dc->polys[1];
3506           }
3507         else if (dc->wire_overlay != 0)
3508           {
3509             if (pizza_p)
3510               glCallList (dc->pizza_list_facetted);
3511             else
3512               glCallList (dc->helix_list_facetted);
3513
3514             glDisable (GL_LIGHTING);
3515             glColor3fv (dc->color);
3516
3517             if (pizza_p)
3518               glCallList (dc->pizza_list_wire);
3519             else
3520               glCallList (dc->helix_list_wire);
3521
3522             mi->polygon_count += dc->polys[2];
3523             if (!wire) glEnable (GL_LIGHTING);
3524           }
3525         else
3526           {
3527             if (pizza_p)
3528               glCallList (dc->pizza_list);
3529             else
3530               glCallList (dc->helix_list);
3531             mi->polygon_count += dc->polys[0];
3532           }
3533       }
3534 # ifdef CW
3535     else        /* codeword_p */
3536       {
3537 #  if 0
3538         double max = 70;  /* face front */
3539         double x, y, z;
3540         get_position (dc->scene_rot, &x, &y, &z, !dc->button_down_p);
3541         glRotatef (max/2 - x*max, 0, 0, 1);
3542         glRotatef (max/2 - y*max, 0, 1, 0);
3543         /* glRotatef (max/2 - z*max, 1, 0, 0); */
3544 #  else
3545         glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
3546         glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
3547 #  endif
3548
3549         glClearColor (dc->codeword_bg[0],
3550                       dc->codeword_bg[1],
3551                       dc->codeword_bg[2],
3552                       dc->codeword_bg[3]);
3553         mi->polygon_count += draw_codeword_path (mi);
3554       }
3555 # endif /* CW */
3556   }
3557   glPopMatrix();
3558
3559   if (dc->wire_overlay > 0)
3560     dc->wire_overlay--;
3561
3562   if (mi->fps_p) do_fps (mi);
3563   glFinish();
3564
3565   glXSwapBuffers(dpy, window);
3566 }
3567
3568 XSCREENSAVER_MODULE_2 ("DNALogo", dnalogo, logo)
3569
3570 #endif /* USE_GL */