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