http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / glx / dnalogo.c
diff --git a/hacks/glx/dnalogo.c b/hacks/glx/dnalogo.c
new file mode 100644 (file)
index 0000000..465b7ca
--- /dev/null
@@ -0,0 +1,1270 @@
+/* DNA Logo, Copyright (c) 2001, 2002, 2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ */
+
+#define DEFAULTS       __extension__ \
+                       "*delay:            25000   \n" \
+                       "*showFPS:          False   \n" \
+                       "*wireframe:        False   \n" \
+                       "*doGasket:         True    \n" \
+                       "*doHelix:          True    \n" \
+                       "*doLadder:         True    \n" \
+                       "*wallFacets:       360     \n" \
+                       "*tubeFacets:       90      \n" \
+                       "*clockwise:        False   \n" \
+                       "*turns:            0.8     \n" \
+                       "*turnSpacing:      2.40    \n" \
+                       "*barSpacing:       0.30    \n" \
+                       "*wallHeight:       0.4     \n" \
+                       "*wallThickness:    0.1     \n" \
+                       "*tubeThickness:    0.075   \n" \
+                       "*wallTaper:        1.047   \n" \
+                       "*gasketSize:       2.15    \n" \
+                       "*gasketDepth:      0.15    \n" \
+                       "*gasketThickness:  0.4     \n" \
+                       "*speed:            1.0     \n" \
+                       ".foreground:       #00AA00 \n" \
+
+# define refresh_logo 0
+# define release_logo 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "normals.h"
+#include "tube.h"
+#include "rotator.h"
+#include "gltrackball.h"
+
+#ifdef USE_GL /* whole file */
+
+typedef struct {
+  Bool spinning_p;
+  GLfloat position;     /* 0.0 - 1.0 */
+  GLfloat speed;        /* how far along the path (may be negative) */
+  GLfloat probability;  /* relative likelyhood to start spinning */
+} spinner;
+
+typedef struct {
+  GLXContext *glx_context;
+
+  GLuint helix_list,  helix_list_wire,  helix_list_facetted;
+  GLuint gasket_list, gasket_list_wire;
+
+  int wall_facets;
+  int tube_facets;
+  Bool clockwise;
+  GLfloat color[4];
+
+  GLfloat turns;
+  GLfloat turn_spacing;
+  GLfloat bar_spacing;
+  GLfloat wall_height;
+  GLfloat wall_thickness;
+  GLfloat tube_thickness;
+  GLfloat wall_taper;
+
+  GLfloat gasket_size;
+  GLfloat gasket_depth;
+  GLfloat gasket_thickness;
+
+  GLfloat speed;
+
+  spinner gasket_spinnerx, gasket_spinnery, gasket_spinnerz;
+  spinner scene_spinnerx,  scene_spinnery;
+  spinner helix_spinnerz;
+
+  trackball_state *trackball;
+  Bool button_down_p;
+
+  int wire_overlay;  /* frame countdown */
+
+} logo_configuration;
+
+static logo_configuration *dcs = NULL;
+
+static XrmOptionDescRec opts[] = {
+  { "-speed",  ".speed",  XrmoptionSepArg, 0 },
+};
+
+ENTRYPOINT ModeSpecOpt logo_opts = {countof(opts), opts, 0, NULL, NULL};
+
+#define PROBABILITY_SCALE 600
+
+
+\f
+/* Calculate the angle (in degrees) between two vectors.
+ */
+static GLfloat
+vector_angle (double ax, double ay, double az,
+              double bx, double by, double bz)
+{
+  double La = sqrt (ax*ax + ay*ay + az*az);
+  double Lb = sqrt (bx*bx + by*by + bz*bz);
+  double cc, angle;
+
+  if (La == 0 || Lb == 0) return 0;
+  if (ax == bx && ay == by && az == bz) return 0;
+
+  /* dot product of two vectors is defined as:
+       La * Lb * cos(angle between vectors)
+     and is also defined as:
+       ax*bx + ay*by + az*bz
+     so:
+      La * Lb * cos(angle) = ax*bx + ay*by + az*bz
+      cos(angle)  = (ax*bx + ay*by + az*bz) / (La * Lb)
+      angle = acos ((ax*bx + ay*by + az*bz) / (La * Lb));
+  */
+  cc = (ax*bx + ay*by + az*bz) / (La * Lb);
+  if (cc > 1) cc = 1;  /* avoid fp rounding error (1.000001 => sqrt error) */
+  angle = acos (cc);
+
+  return (angle * M_PI / 180);
+}
+
+\f
+/* Make the helix
+ */
+
+static void
+make_helix (logo_configuration *dc, int facetted, int wire)
+{
+  int wall_facets = dc->wall_facets / (facetted ? 15 : 1);
+  GLfloat th;
+  GLfloat max_th = M_PI * 2 * dc->turns;
+  GLfloat th_inc = M_PI * 2 / wall_facets;
+
+  GLfloat x1=0,  y1=0,  x2=0,  y2=0;
+  GLfloat x1b=0, y1b=0, x2b=0, y2b=0;
+  GLfloat z1=0, z2=0;
+  GLfloat h1=0, h2=0;
+  GLfloat h1off=0, h2off=0;
+  GLfloat z_inc = dc->turn_spacing / wall_facets;
+
+  th  = 0;
+  x1  = 1;
+  y1  = 0;
+  x1b = 1 - dc->wall_thickness;
+  y1b = 0;
+
+  z1 = -(dc->turn_spacing * dc->turns / 2);
+
+  h1 = (dc->wall_taper > 0 ? 0 : dc->wall_height / 2);
+  h1off = (dc->wall_taper > 0 ? -dc->wall_height / 2 : 0);
+
+  if (!dc->clockwise)
+    z1 = -z1, z_inc = -z_inc, h1off = -h1off;
+
+  /* Leading end-cap
+   */
+  if (!wire && h1 > 0)
+    {
+      GLfloat nx, ny;
+      glFrontFace(GL_CCW);
+      glBegin(GL_QUADS);
+      nx = cos (th + M_PI/2);
+      ny = sin (th + M_PI/2);
+      glNormal3f(nx, ny, 0);
+      glVertex3f( x1, y1,  z1 - h1 + h1off);
+      glVertex3f( x1, y1,  z1 + h1 + h1off);
+      glVertex3f(x1b, y1b, z1 + h1 + h1off);
+      glVertex3f(x1b, y1b, z1 - h1 + h1off);
+      glEnd();
+    }
+
+  while (th + th_inc <= max_th)
+    {
+      th += th_inc;
+
+      x2 = cos (th);
+      y2 = sin (th);
+      z2 = z1 + z_inc;
+      x2b = x2 * (1 - dc->wall_thickness);
+      y2b = y2 * (1 - dc->wall_thickness);
+
+      h2 = h1;
+      h2off = h1off;
+
+      if (dc->wall_taper > 0)
+        {
+          h2off = 0;
+          if (th < dc->wall_taper)
+            {
+              h2 = dc->wall_height/2 * cos (M_PI / 2
+                                            * (1 - (th / dc->wall_taper)));
+              if (dc->clockwise)
+                h2off = h2 - dc->wall_height/2;
+              else
+                h2off = dc->wall_height/2 - h2;
+            }
+          else if (th >= max_th - dc->wall_taper)
+            {
+              if (th + th_inc > max_th) /* edge case: always come to a point */
+                h2 = 0;
+              else
+                h2 = dc->wall_height/2 * cos (M_PI / 2
+                                              * (1 - ((max_th - th)
+                                                      / dc->wall_taper)));
+              if (dc->clockwise)
+                h2off = dc->wall_height/2 - h2;
+              else
+                h2off = h2 - dc->wall_height/2;
+            }
+        }
+
+      /* outer face
+       */
+      glFrontFace(GL_CW);
+      glBegin(wire ? GL_LINES : GL_QUADS);
+      glNormal3f(x1, y1, 0);
+      glVertex3f(x1, y1, z1 - h1 + h1off);
+      glVertex3f(x1, y1, z1 + h1 + h1off);
+      glNormal3f(x2, y2, 0);
+      glVertex3f(x2, y2, z2 + h2 + h2off);
+      glVertex3f(x2, y2, z2 - h2 + h2off);
+      glEnd();
+
+      /* inner face
+       */
+      glFrontFace(GL_CCW);
+      glBegin(wire ? GL_LINES : GL_QUADS);
+      glNormal3f(-x1b, -y1b, 0);
+      glVertex3f( x1b,  y1b, z1 - h1 + h1off);
+      glVertex3f( x1b,  y1b, z1 + h1 + h1off);
+      glNormal3f(-x2b, -y2b, 0);
+      glVertex3f( x2b,  y2b, z2 + h2 + h2off);
+      glVertex3f( x2b,  y2b, z2 - h2 + h2off);
+      glEnd();
+
+      /* top face
+       */
+      glFrontFace(GL_CCW);
+      /* glNormal3f(0, 0, 1);*/
+      do_normal (x2,   y2,  z2 + h2 + h2off,
+                 x2b,  y2b, z2 + h2 + h2off,
+                 x1b,  y1b, z1 + h1 + h1off);
+      glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
+      glVertex3f( x2,   y2,  z2 + h2 + h2off);
+      glVertex3f( x2b,  y2b, z2 + h2 + h2off);
+      glVertex3f( x1b,  y1b, z1 + h1 + h1off);
+      glVertex3f( x1,   y1,  z1 + h1 + h1off);
+
+      glEnd();
+
+      /* bottom face
+       */
+      glFrontFace(GL_CCW);
+      do_normal ( x1,   y1,  z1 - h1 + h1off,
+                  x1b,  y1b, z1 - h1 + h1off,
+                  x2b,  y2b, z2 - h2 + h2off);
+      glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
+      glNormal3f(0, 0, -1);
+      glVertex3f( x1,   y1,  z1 - h1 + h1off);
+      glVertex3f( x1b,  y1b, z1 - h1 + h1off);
+      glVertex3f( x2b,  y2b, z2 - h2 + h2off);
+      glVertex3f( x2,   y2,  z2 - h2 + h2off);
+      glEnd();
+
+      x1 = x2;
+      y1 = y2;
+      x1b = x2b;
+      y1b = y2b;
+      z1 += z_inc;
+      h1 = h2;
+      h1off = h2off;
+    }
+
+  /* Trailing end-cap
+   */
+  if (!wire && h2 > 0)
+    {
+      GLfloat nx, ny;
+      glFrontFace(GL_CW);
+      glBegin(GL_QUADS);
+      nx = cos (th + M_PI/2);
+      ny = sin (th + M_PI/2);
+      glNormal3f(nx, ny, 0);
+      glVertex3f(x2,  y2,  z1 - h2 + h2off);
+      glVertex3f(x2,  y2,  z1 + h2 + h2off);
+      glVertex3f(x2b, y2b, z1 + h2 + h2off);
+      glVertex3f(x2b, y2b, z1 - h2 + h2off);
+      glEnd();
+    }
+}
+
+
+static void
+make_ladder (logo_configuration *dc, int facetted, int wire)
+{
+  GLfloat th;
+  GLfloat max_th = dc->turns * M_PI * 2;
+  GLfloat max_z  = dc->turns * dc->turn_spacing;
+  GLfloat z_inc  = dc->bar_spacing;
+  GLfloat th_inc = M_PI * 2 * (dc->bar_spacing / dc->turn_spacing);
+  GLfloat x, y, z;
+
+  /* skip forward to center the bars in the helix... */
+  int i;
+  GLfloat usable_th = max_th - dc->wall_taper;
+  GLfloat usable_z  = max_z / (max_th / usable_th);
+  int nbars = usable_z / dc->bar_spacing;
+  GLfloat used_z = (nbars - 1) * dc->bar_spacing;
+  GLfloat pad_z = max_z - used_z;
+  GLfloat pad_ratio = pad_z / max_z;
+
+  th = (max_th * pad_ratio/2);
+  z  = -(max_z / 2) + (max_z * pad_ratio/2);
+
+  if (!dc->clockwise)
+    z = -z, z_inc = -z_inc;
+
+  for (i = 0; i < nbars; i++)
+    {
+      int facets = dc->tube_facets / (facetted ? 14 : 1);
+      if (facets <= 3) facets = 3;
+      x = cos (th) * (1 - dc->wall_thickness);
+      y = sin (th) * (1 - dc->wall_thickness);
+      tube ( x,  y, z,
+            -x, -y, z,
+             dc->tube_thickness, 0, facets,
+             True, True, wire);
+      z  += z_inc;
+      th += th_inc;
+    }
+}
+
+
+\f
+/* Make the gasket
+ */
+
+
+static void
+make_gasket (logo_configuration *dc, int wire)
+{
+  int i;
+  int points_size;
+  int npoints = 0;
+  int nctrls = 0;
+  int res = 360/8;
+  GLfloat d2r = M_PI / 180;
+
+  GLfloat thick2 = (dc->gasket_thickness / dc->gasket_size) / 2;
+
+  GLfloat *pointsx0, *pointsy0, *pointsx1, *pointsy1, *normals;
+
+  GLfloat r0  = 0.780;  /* 395 */
+  GLfloat r1a = 0.855;                /* top of wall below upper left hole */
+  GLfloat r1b = 0.890;                /* center of upper left hole */
+  GLfloat r1c = 0.922;                /* bottom of wall above hole */
+  GLfloat r1  = 0.928;  /* 471 */
+  GLfloat r2  = 0.966;  /* 490 */
+  GLfloat r3  = 0.984;  /* 499 */
+  GLfloat r4  = 1.000;  /* 507 */
+  GLfloat r5  = 1.090;  /* 553 */
+
+  GLfloat ctrl_r[100], ctrl_th[100];
+
+  glPushMatrix();
+
+# define POINT(r,th) \
+    ctrl_r [nctrls] = r, \
+    ctrl_th[nctrls] = (th * d2r), \
+    nctrls++
+
+  POINT (0.829, 0);      /* top indentation, right half */
+  POINT (0.831, 0.85);
+  POINT (0.835, 1.81);
+  POINT (0.841, 2.65);
+  POINT (0.851, 3.30);
+  POINT (0.862, 3.81);
+  POINT (0.872, 3.95);
+
+  POINT (r4,    4.0);   /* moving clockwise... */
+  POINT (r4,   48.2);
+  POINT (r1,   48.2);
+  POINT (r1,   54.2);
+  POINT (r2,   55.8);
+  POINT (r2,   73.2);
+  POINT (r1,   74.8);
+  POINT (r1,  101.2);
+  POINT (r3,  103.4);
+  POINT (r3,  132.0);
+  POINT (r1,  133.4);
+
+  POINT (r1,  180.7);
+  POINT (r2,  183.6);
+  POINT (r2,  209.8);
+  POINT (r1,  211.0);
+  POINT (r1,  221.8);
+  POINT (r5,  221.8);
+  POINT (r5,  223.2);
+  POINT (r4,  223.2);
+
+  POINT (r4,  316.8);      /* upper left indentation */
+  POINT (0.990, 326.87);
+  POINT (0.880, 327.21);
+  POINT (0.872, 327.45);
+  POINT (0.869, 327.80);
+  POINT (0.867, 328.10);
+
+  POINT (0.867, 328.85);
+  POINT (0.869, 329.15);
+  POINT (0.872, 329.50);
+  POINT (0.880, 329.74);
+  POINT (0.990, 330.08);
+
+  POINT (r4,  339.0);
+  if (! wire)
+    {
+      POINT (r1a, 339.0);      /* cut-out disc */
+      POINT (r1a, 343.0);
+    }
+  POINT (r4,  343.0);
+  POINT (r4,  356.0);
+
+  POINT (0.872, 356.05);   /* top indentation, left half */
+  POINT (0.862, 356.19);
+  POINT (0.851, 356.70);
+  POINT (0.841, 357.35);
+  POINT (0.835, 358.19);
+  POINT (0.831, 359.15);
+  POINT (0.829, 360);
+# undef POINT
+
+  points_size = res + (nctrls * 2);
+  pointsx0 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
+  pointsy0 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
+  pointsx1 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
+  pointsy1 = (GLfloat *) malloc (points_size * sizeof(GLfloat));
+  normals  = (GLfloat *) malloc (points_size * sizeof(GLfloat) * 2);
+
+  npoints = 0;
+  for (i = 1; i < nctrls; i++)
+    {
+      GLfloat from_r  = ctrl_r [i-1];
+      GLfloat from_th = ctrl_th[i-1];
+      GLfloat to_r    = ctrl_r [i];
+      GLfloat to_th   = ctrl_th[i];
+
+      GLfloat step = 2*M_PI / res;
+      int nsteps = 1 + ((to_th - from_th) / step);
+      int j;
+
+      for (j = 0; j < nsteps + (i == nctrls-1); j++)
+        {
+          GLfloat r  = from_r  + (j * (to_r  - from_r)  / nsteps);
+          GLfloat th = from_th + (j * (to_th - from_th) / nsteps);
+
+          GLfloat cth = cos(th) * dc->gasket_size;
+          GLfloat sth = sin(th) * dc->gasket_size;
+
+          pointsx0[npoints] = r0 * cth;  /* inner ring */
+          pointsy0[npoints] = r0 * sth;
+          pointsx1[npoints] = r  * cth;  /* outer ring */
+          pointsy1[npoints] = r  * sth;
+          npoints++;
+
+          if (npoints >= points_size) abort();
+        }
+    }
+
+  /* normals for the outer ring */
+  for (i = 1; i < npoints; i++)
+    {
+      XYZ a, b, c, n;
+      a.x = pointsx1[i-1];
+      a.y = pointsy1[i-1];
+      a.z = 0;
+      b.x = pointsx1[i];
+      b.y = pointsy1[i];
+      b.z = 0;
+      c = b;
+      c.z = 1;
+      n = calc_normal (a, b, c);
+      normals[(i-1)*2  ] = n.x;
+      normals[(i-1)*2+1] = n.y;
+    }
+
+  glRotatef(-90, 0, 1, 0);
+  glRotatef(180, 0, 0, 1);
+
+  if (wire)
+    {
+      GLfloat z;
+      for (z = -thick2; z <= thick2; z += thick2*2)
+        {
+# if 1
+          /* inside edge */
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx0[i], pointsy0[i], z);
+          glEnd();
+
+          /* outside edge */
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx1[i], pointsy1[i], z);
+          glEnd();
+# else
+          for (i = 1; i < npoints; i++)
+            {
+              glBegin (GL_LINE_STRIP);
+              glVertex3f (pointsx0[i-1], pointsy0[i-1], z);
+              glVertex3f (pointsx0[i  ], pointsy0[i  ], z);
+              glVertex3f (pointsx1[i  ], pointsy1[i  ], z);
+              glVertex3f (pointsx1[i-1], pointsy1[i-1], z);
+              glEnd();
+            }
+# endif
+        }
+#if 1
+      glBegin (GL_LINES);
+      for (i = 0; i < npoints; i++)
+        {
+          /* inside rim */
+          glVertex3f (pointsx0[i], pointsy0[i], -thick2);
+          glVertex3f (pointsx0[i], pointsy0[i],  thick2);
+          /* outside rim */
+          glVertex3f (pointsx1[i], pointsy1[i], -thick2);
+          glVertex3f (pointsx1[i], pointsy1[i],  thick2);
+        }
+      glEnd();
+#endif
+    }
+  else
+    {
+      /* top */
+      glFrontFace(GL_CW);
+      glNormal3f(0, 0, -1);
+      glBegin (GL_QUAD_STRIP);
+      for (i = 0; i < npoints; i++)
+        {
+          glVertex3f (pointsx0[i], pointsy0[i], -thick2);
+          glVertex3f (pointsx1[i], pointsy1[i], -thick2);
+        }
+      glEnd();
+
+      /* bottom */
+      glFrontFace(GL_CCW);
+      glNormal3f(0, 0, 1);
+      glBegin (GL_QUAD_STRIP);
+      for (i = 0; i < npoints; i++)
+        {
+          glVertex3f (pointsx0[i], pointsy0[i], thick2);
+          glVertex3f (pointsx1[i], pointsy1[i], thick2);
+        }
+      glEnd();
+
+      /* inside edge */
+      glFrontFace(GL_CW);
+      glBegin (GL_QUAD_STRIP);
+      for (i = 0; i < npoints; i++)
+        {
+          glNormal3f (-pointsx0[i], -pointsy0[i],  0);
+          glVertex3f ( pointsx0[i],  pointsy0[i],  thick2);
+          glVertex3f ( pointsx0[i],  pointsy0[i], -thick2);
+        }
+      glEnd();
+
+      /* outside edge */
+      glFrontFace(GL_CCW);
+      glBegin (GL_QUADS);
+      {
+        for (i = 0; i < npoints-1; i++)
+          {
+            int ia = (i == 0 ? npoints-2 : i-1);
+            int iz = (i == npoints-2 ? 0 : i+1);
+            GLfloat  x = pointsx1[i];
+            GLfloat  y = pointsy1[i];
+            GLfloat xz = pointsx1[iz];
+            GLfloat yz = pointsy1[iz];
+
+            GLfloat nxa = normals[ia*2];   /* normal of [i-1 - i] face */
+            GLfloat nya = normals[ia*2+1];
+            GLfloat nx  = normals[i*2];    /* normal of [i - i+1] face */
+            GLfloat ny  = normals[i*2+1];
+            GLfloat nxz = normals[iz*2];    /* normal of [i+1 - i+2] face */
+            GLfloat nyz = normals[iz*2+1];
+
+            GLfloat anglea = vector_angle (nx, ny, 0, nxa, nya, 0);
+            GLfloat anglez = vector_angle (nx, ny, 0, nxz, nyz, 0);
+            GLfloat pointy = 0.005;
+
+            if (anglea > pointy)
+              {
+                glNormal3f (nx, ny, 0);
+                glVertex3f (x,  y,   thick2);
+                glVertex3f (x,  y,  -thick2);
+              }
+            else
+              {
+                glNormal3f ((nxa + nx) / 2, (nya + ny) / 2, 0);
+                glVertex3f (x,  y,   thick2);
+                glVertex3f (x,  y,  -thick2);
+              }
+
+            if (anglez > pointy)
+              {
+                glNormal3f (nx, ny, 0);
+                glVertex3f (xz, yz, -thick2);
+                glVertex3f (xz, yz,  thick2);
+              }
+            else
+              {
+                glNormal3f ((nx + nxz) / 2, (ny + nyz) / 2, 0);
+                glVertex3f (xz, yz, -thick2);
+                glVertex3f (xz, yz,  thick2);
+              }
+          }
+      }
+      glEnd();
+    }
+
+  /* Fill in the upper left hole...
+   */
+  {
+    GLfloat th;
+    npoints = 0;
+
+    th = 339.0 * d2r;
+    pointsx0[npoints] = r1c * cos(th) * dc->gasket_size;
+    pointsy0[npoints] = r1c * sin(th) * dc->gasket_size;
+    npoints++;
+    pointsx0[npoints] = r4 * cos(th) * dc->gasket_size;
+    pointsy0[npoints] = r4 * sin(th) * dc->gasket_size;
+    npoints++;
+
+    th = 343.0 * d2r;
+    pointsx0[npoints] = r1c * cos(th) * dc->gasket_size;
+    pointsy0[npoints] = r1c * sin(th) * dc->gasket_size;
+    npoints++;
+    pointsx0[npoints] = r4 * cos(th) * dc->gasket_size;
+    pointsy0[npoints] = r4 * sin(th) * dc->gasket_size;
+    npoints++;
+
+    if (! wire)
+      {
+        /* front wall */
+        glNormal3f (0, 0, -1);
+        glFrontFace(GL_CW);
+        glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+        glVertex3f (pointsx0[0], pointsy0[0], -thick2);
+        glVertex3f (pointsx0[1], pointsy0[1], -thick2);
+        glVertex3f (pointsx0[3], pointsy0[3], -thick2);
+        glVertex3f (pointsx0[2], pointsy0[2], -thick2);
+        glEnd();
+
+        /* back wall */
+        glNormal3f (0, 0, 1);
+        glFrontFace(GL_CCW);
+        glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+        glVertex3f (pointsx0[0], pointsy0[0],  thick2);
+        glVertex3f (pointsx0[1], pointsy0[1],  thick2);
+        glVertex3f (pointsx0[3], pointsy0[3],  thick2);
+        glVertex3f (pointsx0[2], pointsy0[2],  thick2);
+        glEnd();
+      }
+
+    /* top wall */
+    glFrontFace(GL_CW);
+    glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
+    glNormal3f (pointsx0[1], pointsy0[1], 0);
+    glVertex3f (pointsx0[1], pointsy0[1],  thick2);
+    glNormal3f (pointsx0[3], pointsy0[3], 0);
+    glVertex3f (pointsx0[3], pointsy0[3],  thick2);
+    glVertex3f (pointsx0[3], pointsy0[3], -thick2);
+    glNormal3f (pointsx0[1], pointsy0[1], 0);
+    glVertex3f (pointsx0[1], pointsy0[1], -thick2);
+    glEnd();
+
+
+    /* Now make a donut.
+     */
+    {
+      int nsteps = 12;
+      GLfloat r0 = 0.026;
+      GLfloat r1 = 0.060;
+      GLfloat th, cth, sth;
+
+      glPushMatrix ();
+
+      th = ((339.0 + 343.0) / 2) * d2r;
+      
+      glTranslatef (r1b * cos(th) * dc->gasket_size,
+                    r1b * sin(th) * dc->gasket_size,
+                    0);
+
+      npoints = 0;
+      for (i = 0; i < nsteps; i++)
+        {
+          th = 2 * M_PI * i / nsteps;
+          cth = cos (th) * dc->gasket_size;
+          sth = sin (th) * dc->gasket_size;
+          pointsx0[npoints] = r0 * cth;
+          pointsy0[npoints] = r0 * sth;
+          pointsx1[npoints] = r1 * cth;
+          pointsy1[npoints] = r1 * sth;
+          npoints++;
+        }
+
+      pointsx0[npoints] = pointsx0[0];
+      pointsy0[npoints] = pointsy0[0];
+      pointsx1[npoints] = pointsx1[0];
+      pointsy1[npoints] = pointsy1[0];
+      npoints++;
+
+      if (wire)
+        {
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx0[i], pointsy0[i], -thick2);
+          glEnd();
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx0[i], pointsy0[i],  thick2);
+          glEnd();
+# if 0
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx1[i], pointsy1[i], -thick2);
+          glEnd();
+          glBegin (GL_LINE_LOOP);
+          for (i = 0; i < npoints; i++)
+            glVertex3f (pointsx1[i], pointsy1[i],  thick2);
+          glEnd();
+# endif
+        }
+      else
+        {
+          /* top */
+          glFrontFace(GL_CW);
+          glNormal3f(0, 0, -1);
+          glBegin (GL_QUAD_STRIP);
+          for (i = 0; i < npoints; i++)
+            {
+              glVertex3f (pointsx0[i], pointsy0[i], -thick2);
+              glVertex3f (pointsx1[i], pointsy1[i], -thick2);
+            }
+          glEnd();
+
+          /* bottom */
+          glFrontFace(GL_CCW);
+          glNormal3f(0, 0, 1);
+          glBegin (GL_QUAD_STRIP);
+          for (i = 0; i < npoints; i++)
+            {
+              glVertex3f (pointsx0[i], pointsy0[i],  thick2);
+              glVertex3f (pointsx1[i], pointsy1[i],  thick2);
+            }
+          glEnd();
+        }
+
+      /* inside edge */
+      glFrontFace(GL_CW);
+      glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+      for (i = 0; i < npoints; i++)
+        {
+          glNormal3f (-pointsx0[i], -pointsy0[i],  0);
+          glVertex3f ( pointsx0[i],  pointsy0[i],  thick2);
+          glVertex3f ( pointsx0[i],  pointsy0[i], -thick2);
+        }
+      glEnd();
+
+      glPopMatrix();
+    }
+  }
+
+
+  /* Attach the bottom-right dingus...
+   */
+  {
+    GLfloat w = 0.04;
+    GLfloat h = 0.17;
+    GLfloat th;
+
+    glRotatef (50, 0, 0, 1);
+    glScalef (dc->gasket_size, dc->gasket_size, 1);
+    glTranslatef (0, (r0+r1)/2, 0);
+
+    /* buried box */
+    if (! wire)
+      {
+        glFrontFace(GL_CCW);
+        glBegin (wire ? GL_LINE_STRIP : GL_QUADS);
+        glNormal3f (0, 0, -1);
+        glVertex3f (-w/2, -h/2, -thick2); glVertex3f (-w/2,  h/2, -thick2);
+        glVertex3f ( w/2,  h/2, -thick2); glVertex3f ( w/2, -h/2, -thick2);
+        glNormal3f (1, 0, 0);
+        glVertex3f ( w/2, -h/2, -thick2); glVertex3f ( w/2,  h/2, -thick2);
+        glVertex3f ( w/2,  h/2,  thick2); glVertex3f ( w/2, -h/2,  thick2);
+        glNormal3f (0, 0, 1);
+        glVertex3f ( w/2, -h/2,  thick2); glVertex3f ( w/2,  h/2,  thick2);
+        glVertex3f (-w/2,  h/2,  thick2); glVertex3f (-w/2, -h/2,  thick2);
+        glNormal3f (-1, 0, 0);
+        glVertex3f (-w/2, -h/2,  thick2); glVertex3f (-w/2,  h/2,  thick2);
+        glVertex3f (-w/2,  h/2, -thick2); glVertex3f (-w/2, -h/2, -thick2);
+        glEnd();
+      }
+
+    npoints = 0;
+    for (th = 0; th < M_PI; th += (M_PI / 6))
+      {
+        pointsx0[npoints] = w/2 * cos(th);
+        pointsy0[npoints] = w/2 * sin(th);
+        npoints++;
+      }
+
+    /* front inside curve */
+    glNormal3f (0, 0, -1);
+    glFrontFace(GL_CW);
+    glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
+    if (! wire) glVertex3f (0, h/2, -thick2);
+    for (i = 0; i < npoints; i++)
+      glVertex3f (pointsx0[i], h/2 + pointsy0[i], -thick2);
+    glEnd();
+
+    /* front outside curve */
+    glFrontFace(GL_CCW);
+    glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
+    if (! wire) glVertex3f (0, -h/2, -thick2);
+    for (i = 0; i < npoints; i++)
+      glVertex3f (pointsx0[i], -h/2 - pointsy0[i], -thick2);
+    glEnd();
+
+    /* back inside curve */
+    glNormal3f (0, 0, 1);
+    glFrontFace(GL_CCW);
+    glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
+    if (! wire) glVertex3f (0, h/2, thick2);
+    for (i = 0; i < npoints; i++)
+      glVertex3f (pointsx0[i], h/2 + pointsy0[i], thick2);
+    glEnd();
+
+    /* back outside curve */
+    glFrontFace(GL_CW);
+    glBegin (wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
+    if (! wire) glVertex3f (0, -h/2, thick2);
+    for (i = 0; i < npoints; i++)
+      glVertex3f (pointsx0[i], -h/2 - pointsy0[i], thick2);
+    glEnd();
+
+    /* inside curve */
+    glFrontFace(GL_CCW);
+    glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+    for (i = 0; i < npoints; i++)
+      {
+        glNormal3f (pointsx0[i], pointsy0[i], 0);
+        glVertex3f (pointsx0[i], h/2 + pointsy0[i],  thick2);
+        glVertex3f (pointsx0[i], h/2 + pointsy0[i], -thick2);
+      }
+    glEnd();
+
+    /* outside curve */
+    glFrontFace(GL_CW);
+    glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
+    for (i = 0; i < npoints; i++)
+      {
+        glNormal3f (pointsx0[i], -pointsy0[i], 0);
+        glVertex3f (pointsx0[i], -h/2 - pointsy0[i],  thick2);
+        glVertex3f (pointsx0[i], -h/2 - pointsy0[i], -thick2);
+      }
+    glEnd();
+  }
+
+  free (pointsx0);
+  free (pointsy0);
+  free (pointsx1);
+  free (pointsy1);
+  free (normals);
+
+  glPopMatrix();
+}
+
+\f
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_logo (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+
+  glViewport (0, 0, (GLint) width, (GLint) height);
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt( 0.0, 0.0, 30.0,
+             0.0, 0.0, 0.0,
+             0.0, 1.0, 0.0);
+
+  glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+static void
+gl_init (ModeInfo *mi)
+{
+/*  logo_configuration *dc = &dcs[MI_SCREEN(mi)]; */
+  int wire = MI_IS_WIREFRAME(mi);
+
+  GLfloat position[]  = {0, 0, 0, 0};
+  GLfloat direction[] = {3, -1, -3};
+
+  position[0] = -direction[0];
+  position[1] = -direction[1];
+  position[2] = -direction[2];
+
+  if (!wire)
+    {
+      glLightfv(GL_LIGHT0, GL_POSITION, position);
+      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
+      glShadeModel(GL_SMOOTH);
+      glEnable(GL_NORMALIZE);
+      glEnable(GL_CULL_FACE);
+      glEnable(GL_LIGHTING);
+      glEnable(GL_LIGHT0);
+      glEnable(GL_DEPTH_TEST);
+    }
+}
+
+
+ENTRYPOINT void 
+init_logo (ModeInfo *mi)
+{
+  logo_configuration *dc;
+  int do_gasket = get_boolean_resource(mi->dpy, "doGasket", "Boolean");
+  int do_helix = get_boolean_resource(mi->dpy, "doHelix", "Boolean");
+  int do_ladder = do_helix && get_boolean_resource(mi->dpy, "doLadder", "Boolean");
+
+  if (!do_gasket && !do_helix)
+    {
+      fprintf (stderr, "%s: no helix or gasket?\n", progname);
+      exit (1);
+    }
+
+  if (!dcs) {
+    dcs = (logo_configuration *)
+      calloc (MI_NUM_SCREENS(mi), sizeof (logo_configuration));
+    if (!dcs) {
+      fprintf(stderr, "%s: out of memory\n", progname);
+      exit(1);
+    }
+  }
+
+  dc = &dcs[MI_SCREEN(mi)];
+
+  if ((dc->glx_context = init_GL(mi)) != NULL) {
+    gl_init(mi);
+    reshape_logo (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+  }
+
+  dc->wall_facets    = get_integer_resource(mi->dpy, "wallFacets",  "Integer");
+  dc->tube_facets    = get_integer_resource(mi->dpy, "tubeFacets",  "Integer");
+  dc->clockwise      = get_boolean_resource(mi->dpy, "clockwise",   "Boolean");
+  dc->turns          = get_float_resource(mi->dpy, "turns",         "Float");
+  dc->turn_spacing   = get_float_resource(mi->dpy, "turnSpacing",   "Float");
+  dc->bar_spacing    = get_float_resource(mi->dpy, "barSpacing",    "Float");
+  dc->wall_height    = get_float_resource(mi->dpy, "wallHeight",    "Float");
+  dc->wall_thickness = get_float_resource(mi->dpy, "wallThickness", "Float");
+  dc->tube_thickness = get_float_resource(mi->dpy, "tubeThickness", "Float");
+  dc->wall_taper     = get_float_resource(mi->dpy, "wallTaper",     "Float");
+
+  dc->gasket_size      = get_float_resource(mi->dpy, "gasketSize",      "Float");
+  dc->gasket_depth     = get_float_resource(mi->dpy, "gasketDepth",     "Float");
+  dc->gasket_thickness = get_float_resource(mi->dpy, "gasketThickness", "Float");
+
+  dc->speed          = get_float_resource(mi->dpy, "speed",         "Float");
+
+  {
+    XColor xcolor;
+
+    char *color_name = get_string_resource (mi->dpy, "foreground", "Foreground");
+    char *s2;
+    for (s2 = color_name + strlen(color_name) - 1; s2 > color_name; s2--)
+      if (*s2 == ' ' || *s2 == '\t')
+        *s2 = 0;
+      else
+        break;
+
+    if (! XParseColor (MI_DISPLAY(mi), mi->xgwa.colormap, color_name, &xcolor))
+      {
+        fprintf (stderr, "%s: can't parse color %s\n", progname, color_name);
+        exit (1);
+      }
+
+    dc->color[0] = xcolor.red   / 65535.0;
+    dc->color[1] = xcolor.green / 65535.0;
+    dc->color[2] = xcolor.blue  / 65535.0;
+    dc->color[3] = 1.0;
+  }
+
+  dc->trackball = gltrackball_init ();
+
+  dc->gasket_spinnery.probability = 0.1;
+  dc->gasket_spinnerx.probability = 0.1;
+  dc->gasket_spinnerz.probability = 1.0;
+  dc->helix_spinnerz.probability  = 0.6;
+  dc->scene_spinnerx.probability  = 0.1;
+  dc->scene_spinnery.probability  = 0.0;
+
+  if (dc->speed > 0)    /* start off with the gasket in motion */
+    {
+      dc->gasket_spinnerz.spinning_p = True;
+      dc->gasket_spinnerz.speed = (0.002
+                                   * ((random() & 1) ? 1 : -1)
+                                   * dc->speed);
+    }
+
+  glPushMatrix();
+  dc->helix_list = glGenLists (1);
+  glNewList (dc->helix_list, GL_COMPILE);
+  glRotatef(126, 0, 0, 1);
+  if (do_ladder) make_ladder (dc, 0, 0);
+  if (do_helix)  make_helix  (dc, 0, 0);
+  glRotatef(180, 0, 0, 1);
+  if (do_helix)  make_helix  (dc, 0, 0);
+  glEndList ();
+  glPopMatrix();
+
+  glPushMatrix();
+  dc->helix_list_wire = glGenLists (1);
+  glNewList (dc->helix_list_wire, GL_COMPILE);
+  /* glRotatef(126, 0, 0, 1); wtf? */
+  if (do_ladder) make_ladder (dc, 1, 1);
+  if (do_helix)  make_helix  (dc, 1, 1);
+  glRotatef(180, 0, 0, 1);
+  if (do_helix)  make_helix  (dc, 1, 1);
+  glEndList ();
+  glPopMatrix();
+
+  glPushMatrix();
+  dc->helix_list_facetted = glGenLists (1);
+  glNewList (dc->helix_list_facetted, GL_COMPILE);
+  glRotatef(126, 0, 0, 1);
+  if (do_ladder) make_ladder (dc, 1, 0);
+  if (do_helix)  make_helix  (dc, 1, 0);
+  glRotatef(180, 0, 0, 1);
+  if (do_helix)  make_helix  (dc, 1, 0);
+  glEndList ();
+  glPopMatrix();
+
+  dc->gasket_list = glGenLists (1);
+  glNewList (dc->gasket_list, GL_COMPILE);
+  if (do_gasket) make_gasket (dc, 0);
+  glEndList ();
+
+  dc->gasket_list_wire = glGenLists (1);
+  glNewList (dc->gasket_list_wire, GL_COMPILE);
+  if (do_gasket) make_gasket (dc, 1);
+  glEndList ();
+
+  /* When drawing both solid and wireframe objects,
+     make sure the wireframe actually shows up! */
+  glEnable (GL_POLYGON_OFFSET_FILL);
+  glPolygonOffset (1.0, 1.0);
+}
+
+
+ENTRYPOINT Bool
+logo_handle_event (ModeInfo *mi, XEvent *event)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+
+  if (event->xany.type == ButtonPress &&
+      event->xbutton.button == Button1)
+    {
+      dc->button_down_p = True;
+      gltrackball_start (dc->trackball,
+                         event->xbutton.x, event->xbutton.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+  else if (event->xany.type == ButtonRelease &&
+           event->xbutton.button == Button1)
+    {
+      dc->button_down_p = False;
+      return True;
+    }
+  else if (event->xany.type == ButtonPress &&
+           (event->xbutton.button == Button4 ||
+            event->xbutton.button == Button5))
+    {
+      gltrackball_mousewheel (dc->trackball, event->xbutton.button, 10,
+                              !!event->xbutton.state);
+      return True;
+    }
+  else if (event->xany.type == MotionNotify &&
+           dc->button_down_p)
+    {
+      gltrackball_track (dc->trackball,
+                         event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH (mi), MI_HEIGHT (mi));
+      return True;
+    }
+
+  return False;
+}
+
+
+static void
+tick_spinner (ModeInfo *mi, spinner *s)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+
+  if (dc->speed == 0) return;
+  if (dc->button_down_p) return;
+
+  if (s->spinning_p)
+    {
+      s->position += s->speed;
+      if (s->position >=  1.0 || s->position <= -1.0)
+          
+        {
+          s->position = 0;
+          s->spinning_p = False;
+        }
+    }
+  else if (s->probability &&
+           (random() % (int) (PROBABILITY_SCALE / s->probability)) == 0)
+    {
+      GLfloat ss = 0.004;
+      s->spinning_p = True;
+      s->position = 0;
+      do {
+        s->speed = dc->speed * (frand(ss/3) + frand(ss/3) + frand(ss/3));
+      } while (s->speed <= 0);
+      if (random() & 1)
+        s->speed = -s->speed;
+    }
+}
+
+
+static void
+link_spinners (ModeInfo *mi, spinner *s0, spinner *s1)
+{
+  if (s0->spinning_p && !s1->spinning_p)
+    {
+      GLfloat op = s1->probability;
+      s1->probability = PROBABILITY_SCALE;
+      tick_spinner (mi, s1);
+      s1->probability = op;
+    }
+}
+
+
+ENTRYPOINT void
+draw_logo (ModeInfo *mi)
+{
+  logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+  Display *dpy = MI_DISPLAY(mi);
+  Window window = MI_WINDOW(mi);
+  int wire = MI_IS_WIREFRAME(mi);
+  GLfloat gcolor[4];
+  GLfloat specular[]  = {0.8, 0.8, 0.8, 1.0};
+  GLfloat shininess   = 50.0;
+
+  if (!dc->glx_context)
+    return;
+
+  glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(dc->glx_context));
+
+  if (dc->wire_overlay == 0 &&
+      (random() % (int) (PROBABILITY_SCALE / 0.2)) == 0)
+    dc->wire_overlay = ((random() % 200) +
+                        (random() % 200) +
+                        (random() % 200));
+      
+  tick_spinner (mi, &dc->gasket_spinnerx);
+  tick_spinner (mi, &dc->gasket_spinnery);
+  tick_spinner (mi, &dc->gasket_spinnerz);
+  tick_spinner (mi, &dc->helix_spinnerz);
+  tick_spinner (mi, &dc->scene_spinnerx);
+  tick_spinner (mi, &dc->scene_spinnery);
+  link_spinners (mi, &dc->scene_spinnerx, &dc->scene_spinnery);
+
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glPushMatrix ();
+  {
+    glScalef(3.3, 3.3, 3.3);
+
+    gltrackball_rotate (dc->trackball);
+
+    glRotatef(90, 1, 0, 0);
+    glRotatef(90, 0, 0, 1);
+
+    glColor3f(dc->color[0], dc->color[1], dc->color[2]);
+
+    glRotatef (360 * sin (M_PI/2 * dc->scene_spinnerx.position), 0, 1, 0);
+    glRotatef (360 * sin (M_PI/2 * dc->scene_spinnery.position), 0, 0, 1);
+
+    glPushMatrix();
+    {
+      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnerx.position), 0, 1, 0);
+      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnery.position), 0, 0, 1);
+      glRotatef (360 * sin (M_PI/2 * dc->gasket_spinnerz.position), 1, 0, 0);
+
+      memcpy (gcolor, dc->color, sizeof (dc->color));
+      if (dc->wire_overlay != 0)
+        {
+          gcolor[0]   = gcolor[1]   = gcolor[2]   = 0;
+          specular[0] = specular[1] = specular[2] = 0;
+          shininess = 0;
+        }
+      glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, gcolor);
+      glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR,  specular);
+      glMaterialf  (GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+
+      if (wire)
+        glCallList (dc->gasket_list_wire);
+      else if (dc->wire_overlay != 0)
+        {
+          glCallList (dc->gasket_list);
+          glDisable (GL_LIGHTING);
+          glCallList (dc->gasket_list_wire);
+          if (!wire) glEnable (GL_LIGHTING);
+        }
+      else
+        glCallList (dc->gasket_list);
+    }
+    glPopMatrix();
+
+    glRotatef (360 * sin (M_PI/2 * dc->helix_spinnerz.position), 0, 0, 1);
+
+    if (wire)
+      glCallList (dc->helix_list_wire);
+    else if (dc->wire_overlay != 0)
+      {
+        glCallList (dc->helix_list_facetted);
+        glDisable (GL_LIGHTING);
+        glCallList (dc->helix_list_wire);
+        if (!wire) glEnable (GL_LIGHTING);
+      }
+    else
+      glCallList (dc->helix_list);
+  }
+  glPopMatrix();
+
+  if (dc->wire_overlay > 0)
+    dc->wire_overlay--;
+
+  if (mi->fps_p) do_fps (mi);
+  glFinish();
+
+  glXSwapBuffers(dpy, window);
+}
+
+XSCREENSAVER_MODULE_2 ("DNAlogo", dnalogo, logo)
+
+#endif /* USE_GL */