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