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