http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.30.tar.gz
[xscreensaver] / hacks / glx / molecule.c
1 /* molecule, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2  * Draws molecules, based on coordinates from PDB (Protein Data Base) files.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13
14 /* Documentation on the PDB file format:
15    http://www.rcsb.org/pdb/docs/format/pdbguide2.2/guide2.2_frame.html
16
17    Good source of PDB files:
18    http://www.sci.ouc.bc.ca/chem/molecule/molecule.html
19
20    TO DO:
21
22      - I'm not sure the text labels are being done in the best way;
23        they are sometimes, but not always, occluded by spheres that
24        pass in front of them. 
25  */
26
27 #include <X11/Intrinsic.h>
28
29 #define PROGCLASS       "Molecule"
30 #define HACK_INIT       init_molecule
31 #define HACK_DRAW       draw_molecule
32 #define HACK_RESHAPE    reshape_molecule
33 #define molecule_opts   xlockmore_opts
34
35 #define DEF_TIMEOUT     "20"
36 #define DEF_SPIN        "XYZ"
37 #define DEF_WANDER      "False"
38 #define DEF_LABELS      "True"
39 #define DEF_TITLES      "True"
40 #define DEF_ATOMS       "True"
41 #define DEF_BONDS       "True"
42 #define DEF_BBOX        "False"
43 #define DEF_MOLECULE    "(default)"
44
45 #define DEFAULTS        "*delay:        10000         \n" \
46                         "*timeout:    " DEF_TIMEOUT  "\n" \
47                         "*showFPS:      False         \n" \
48                         "*wireframe:    False         \n" \
49                         "*molecule:   " DEF_MOLECULE "\n" \
50                         "*spin:       " DEF_SPIN     "\n" \
51                         "*wander:     " DEF_WANDER   "\n" \
52                         "*labels:     " DEF_LABELS   "\n" \
53                         "*atoms:      " DEF_ATOMS    "\n" \
54                         "*bonds:      " DEF_BONDS    "\n" \
55                         "*bbox:       " DEF_BBOX     "\n" \
56                         "*atomFont:   -*-times-bold-r-normal-*-240-*\n" \
57                         "*titleFont:  -*-times-bold-r-normal-*-180-*\n" \
58                         "*noLabelThreshold:    30     \n" \
59                         "*wireframeThreshold:  150    \n" \
60
61
62 #undef countof
63 #define countof(x) (sizeof((x))/sizeof((*x)))
64
65 #include "xlockmore.h"
66 #include "colors.h"
67
68 #ifdef USE_GL /* whole file */
69
70 #include <ctype.h>
71 #include <GL/glu.h>
72
73
74 #define SPHERE_SLICES 16  /* how densely to render spheres */
75 #define SPHERE_STACKS 10
76
77
78 #define SMOOTH_TUBE       /* whether to have smooth or faceted tubes */
79
80 #ifdef SMOOTH_TUBE
81 # define TUBE_FACES  12   /* how densely to render tubes */
82 #else
83 # define TUBE_FACES  8
84 #endif
85
86 static int scale_down;
87 #define SPHERE_SLICES_2  7
88 #define SPHERE_STACKS_2  4
89 #define TUBE_FACES_2     3
90
91
92 const char * const builtin_pdb_data[] = {
93 # include "molecules.h"
94 };
95
96
97 typedef struct {
98   const char *name;
99   GLfloat size, size2;
100   const char *color;
101   const char *text_color;
102   GLfloat gl_color[8];
103 } atom_data;
104
105
106 /* These are the traditional colors used to render these atoms,
107    and their approximate size in angstroms.
108  */
109 static atom_data all_atom_data[] = {
110   { "H",    1.17,  0,  "White",           "Grey70",        { 0, }},
111   { "C",    1.75,  0,  "Grey60",          "White",         { 0, }},
112   { "N",    1.55,  0,  "LightSteelBlue3", "SlateBlue1",    { 0, }},
113   { "O",    1.40,  0,  "Red",             "LightPink",     { 0, }},
114   { "P",    1.28,  0,  "MediumPurple",    "PaleVioletRed", { 0, }},
115   { "S",    1.80,  0,  "Yellow4",         "Yellow1",       { 0, }},
116   { "bond", 0,     0,  "Grey70",          "Yellow1",       { 0, }},
117   { "*",    1.40,  0,  "Green4",          "LightGreen",    { 0, }}
118 };
119
120
121 typedef struct {
122   int id;               /* sequence number in the PDB file */
123   const char *label;    /* The atom name */
124   GLfloat x, y, z;      /* position in 3-space (angstroms) */
125   atom_data *data;      /* computed: which style of atom this is */
126 } molecule_atom;
127
128 typedef struct {
129   int from, to;         /* atom sequence numbers */
130   int strength;         /* how many bonds are between these two atoms */
131 } molecule_bond;
132
133
134 typedef struct {
135   const char *label;            /* description of this compound */
136   int natoms, atoms_size;
137   int nbonds, bonds_size;
138   molecule_atom *atoms;
139   molecule_bond *bonds;
140 } molecule;
141
142
143 typedef struct {
144   GLXContext *glx_context;
145
146   GLfloat rotx, roty, rotz;        /* current object rotation */
147   GLfloat dx, dy, dz;              /* current rotational velocity */
148   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
149   GLfloat d_max;                   /* max velocity */
150
151   Bool spin_x, spin_y, spin_z;
152
153   GLfloat molecule_size;           /* max dimension of molecule bounding box */
154
155   GLfloat no_label_threshold;      /* Things happen when molecules are huge */
156   GLfloat wireframe_threshold;
157
158   int which;                       /* which of the molecules is being shown */
159   int nmolecules;
160   molecule *molecules;
161
162   GLuint molecule_dlist;
163
164   XFontStruct *xfont1, *xfont2;
165   GLuint font1_dlist, font2_dlist;
166
167 } molecule_configuration;
168
169
170 static molecule_configuration *mcs = NULL;
171
172 static int timeout;
173 static char *molecule_str;
174 static char *do_spin;
175 static Bool do_wander;
176 static Bool do_titles;
177 static Bool do_labels;
178 static Bool do_atoms;
179 static Bool do_bonds;
180 static Bool do_bbox;
181
182 static Bool orig_do_labels, orig_do_bonds, orig_wire; /* saved to reset */
183
184
185 static XrmOptionDescRec opts[] = {
186   { "-molecule", ".molecule", XrmoptionSepArg, 0 },
187   { "-timeout",".timeout",XrmoptionSepArg, 0 },
188   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
189   { "+spin",   ".spin",   XrmoptionNoArg, "" },
190   { "-wander", ".wander", XrmoptionNoArg, "True" },
191   { "+wander", ".wander", XrmoptionNoArg, "False" },
192   { "-labels", ".labels", XrmoptionNoArg, "True" },
193   { "+labels", ".labels", XrmoptionNoArg, "False" },
194   { "-atoms",  ".atoms",  XrmoptionNoArg, "True" },
195   { "+atoms",  ".atoms",  XrmoptionNoArg, "False" },
196   { "-bonds",  ".bonds",  XrmoptionNoArg, "True" },
197   { "+bonds",  ".bonds",  XrmoptionNoArg, "False" },
198   { "-bbox",   ".bbox",  XrmoptionNoArg, "True" },
199   { "+bbox",   ".bbox",  XrmoptionNoArg, "False" },
200 };
201
202 static argtype vars[] = {
203   {(caddr_t *) &molecule_str, "molecule",   "Molecule", DEF_MOLECULE,t_String},
204   {(caddr_t *) &timeout,   "timeout","Seconds",DEF_TIMEOUT,t_Int},
205   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
206   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
207   {(caddr_t *) &do_labels, "labels", "Labels", DEF_LABELS, t_Bool},
208   {(caddr_t *) &do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
209   {(caddr_t *) &do_atoms,  "atoms",  "Atoms",  DEF_ATOMS,  t_Bool},
210   {(caddr_t *) &do_bonds,  "bonds",  "Bonds",  DEF_BONDS,  t_Bool},
211   {(caddr_t *) &do_bbox,   "bbox",   "BBox",   DEF_BBOX,   t_Bool},
212 };
213
214 ModeSpecOpt molecule_opts = {countof(opts), opts, countof(vars), vars, NULL};
215
216
217
218 \f
219 /* shapes */
220
221 static void
222 unit_tube (Bool wire)
223 {
224   int i;
225   int faces = (scale_down ? TUBE_FACES_2 : TUBE_FACES);
226   GLfloat step = M_PI * 2 / faces;
227   GLfloat th;
228   int z = 0;
229
230   /* side walls
231    */
232   glFrontFace(GL_CCW);
233
234 # ifdef SMOOTH_TUBE
235   glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
236 # else
237   glBegin(wire ? GL_LINES : GL_QUADS);
238 # endif
239
240   for (i = 0, th = 0; i <= faces; i++)
241     {
242       GLfloat x = cos (th);
243       GLfloat y = sin (th);
244       glNormal3f(x, 0, y);
245       glVertex3f(x, 0.0, y);
246       glVertex3f(x, 1.0, y);
247       th += step;
248
249 # ifndef SMOOTH_TUBE
250       x = cos (th);
251       y = sin (th);
252       glVertex3f(x, 1.0, y);
253       glVertex3f(x, 0.0, y);
254 # endif
255     }
256   glEnd();
257
258   /* End caps
259    */
260   for (z = 0; z <= 1; z++)
261     {
262       glFrontFace(z == 0 ? GL_CCW : GL_CW);
263       glNormal3f(0, (z == 0 ? -1 : 1), 0);
264       glBegin(wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
265       if (! wire) glVertex3f(0, z, 0);
266       for (i = 0, th = 0; i <= faces; i++)
267         {
268           GLfloat x = cos (th);
269           GLfloat y = sin (th);
270           glVertex3f(x, z, y);
271           th += step;
272         }
273       glEnd();
274     }
275 }
276
277
278 static void
279 tube (GLfloat x1, GLfloat y1, GLfloat z1,
280       GLfloat x2, GLfloat y2, GLfloat z2,
281       GLfloat diameter, GLfloat cap_size,
282       Bool wire)
283 {
284   GLfloat length, angle, a, b, c;
285
286   if (diameter <= 0) abort();
287
288   a = (x2 - x1);
289   b = (y2 - y1);
290   c = (z2 - z1);
291
292   length = sqrt (a*a + b*b + c*c);
293   angle = acos (a / length);
294
295   glPushMatrix();
296   glTranslatef(x1, y1, z1);
297   glScalef (length, length, length);
298
299   if (c == 0 && b == 0)
300     glRotatef (angle / (M_PI / 180), 0, 1, 0);
301   else
302     glRotatef (angle / (M_PI / 180), 0, -c, b);
303
304   glRotatef (-90, 0, 0, 1);
305   glScalef (diameter/length, 1, diameter/length);
306
307   /* extend the endpoints of the tube by the cap size in both directions */
308   if (cap_size != 0)
309     {
310       GLfloat c = cap_size/length;
311       glTranslatef (0, -c, 0);
312       glScalef (1, 1+c+c, 1);
313     }
314
315   unit_tube (wire);
316   glPopMatrix();
317 }
318
319
320 /* lifted from glplanet */
321 /* Function for determining points on the surface of the sphere */
322 static void
323 parametric_sphere (float theta, float rho, GLfloat *vector)
324 {
325   vector[0] = -sin(theta) * sin(rho);
326   vector[1] = cos(theta) * sin(rho);
327   vector[2] = cos(rho);
328 }
329
330 /* lifted from glplanet */
331 static void
332 unit_sphere (Bool wire)
333 {
334   int stacks = (scale_down ? SPHERE_STACKS_2 : SPHERE_STACKS);
335   int slices = (scale_down ? SPHERE_SLICES_2 : SPHERE_SLICES);
336
337   int i, j;
338   float drho, dtheta;
339   float rho, theta;
340   GLfloat vector[3];
341   GLfloat ds, dt, t, s;
342
343   if (!do_bonds && !scale_down)  /* if balls are bigger, be smoother... */
344     slices *= 2, stacks *= 2;
345
346   if (!wire)
347     glShadeModel(GL_SMOOTH);
348
349   /* Generate a sphere with quadrilaterals.
350    * Quad vertices are determined using a parametric sphere function.
351    * For fun, you could generate practically any parameteric surface and
352    * map an image onto it. 
353    */
354   drho = M_PI / stacks;
355   dtheta = 2.0 * M_PI / slices;
356   ds = 1.0 / slices;
357   dt = 1.0 / stacks;
358
359   glFrontFace(GL_CCW);
360   glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
361
362   t = 0.0;
363   for (i=0; i < stacks; i++) {
364     rho = i * drho;
365     s = 0.0;
366     for (j=0; j < slices; j++) {
367       theta = j * dtheta;
368
369       glTexCoord2f (s,t);
370       parametric_sphere (theta, rho, vector);
371       glNormal3fv (vector);
372       parametric_sphere (theta, rho, vector);
373       glVertex3f (vector[0], vector[1], vector[2]);
374
375       glTexCoord2f (s,t+dt);
376       parametric_sphere (theta, rho+drho, vector);
377       glNormal3fv (vector);
378       parametric_sphere (theta, rho+drho, vector);
379       glVertex3f (vector[0], vector[1], vector[2]);
380
381       glTexCoord2f (s+ds,t+dt);
382       parametric_sphere (theta + dtheta, rho+drho, vector);
383       glNormal3fv (vector);
384       parametric_sphere (theta + dtheta, rho+drho, vector);
385       glVertex3f (vector[0], vector[1], vector[2]);
386
387       glTexCoord2f (s+ds, t);
388       parametric_sphere (theta + dtheta, rho, vector);
389       glNormal3fv (vector);
390       parametric_sphere (theta + dtheta, rho, vector);
391       glVertex3f (vector[0], vector[1], vector[2]);
392
393       s = s + ds;
394     }
395     t = t + dt;
396   }
397   glEnd();
398 }
399
400
401 static void
402 sphere (GLfloat x, GLfloat y, GLfloat z, GLfloat diameter, Bool wire)
403 {
404   glPushMatrix ();
405   glTranslatef (x, y, z);
406   glScalef (diameter, diameter, diameter);
407   unit_sphere (wire);
408   glPopMatrix ();
409 }
410
411
412 static void
413 load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
414 {
415   const char *font = get_string_resource (res, "Font");
416   XFontStruct *f;
417   Font id;
418   int first, last;
419
420   if (!font) font = "-*-times-bold-r-normal-*-180-*";
421
422   f = XLoadQueryFont(mi->dpy, font);
423   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
424
425   id = f->fid;
426   first = f->min_char_or_byte2;
427   last = f->max_char_or_byte2;
428   
429   clear_gl_error ();
430   *dlistP = glGenLists ((GLuint) last+1);
431   check_gl_error ("glGenLists");
432   glXUseXFont(id, first, last-first+1, *dlistP + first);
433   check_gl_error ("glXUseXFont");
434
435   *fontP = f;
436 }
437
438
439 static void
440 load_fonts (ModeInfo *mi)
441 {
442   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
443   load_font (mi, "atomFont",  &mc->xfont1, &mc->font1_dlist);
444   load_font (mi, "titleFont", &mc->xfont2, &mc->font2_dlist);
445 }
446
447
448 static int
449 string_width (XFontStruct *f, const char *c)
450 {
451   int w = 0;
452   while (*c)
453     {
454       int cc = *((unsigned char *) c);
455       w += (f->per_char
456             ? f->per_char[cc-f->min_char_or_byte2].rbearing
457             : f->min_bounds.rbearing);
458       c++;
459     }
460   return w;
461 }
462
463
464 static atom_data *
465 get_atom_data (const char *atom_name)
466 {
467   int i;
468   atom_data *d = 0;
469   char *n = strdup (atom_name);
470   char *n2 = n;
471   int L;
472
473   while (!isalpha(*n)) n++;
474   L = strlen(n);
475   while (L > 0 && !isalpha(n[L-1]))
476     n[--L] = 0;
477
478   for (i = 0; i < countof(all_atom_data); i++)
479     {
480       d = &all_atom_data[i];
481       if (!strcmp (n, all_atom_data[i].name))
482         break;
483     }
484
485   free (n2);
486   return d;
487 }
488
489
490 static void
491 set_atom_color (ModeInfo *mi, molecule_atom *a, Bool font_p)
492 {
493   atom_data *d;
494   GLfloat *gl_color;
495
496   if (a)
497     d = a->data;
498   else
499     {
500       static atom_data *def_data = 0;
501       if (!def_data) def_data = get_atom_data ("bond");
502       d = def_data;
503     }
504
505   gl_color = (!font_p ? d->gl_color : (d->gl_color + 4));
506
507   if (gl_color[3] == 0)
508     {
509       const char *string = !font_p ? d->color : d->text_color;
510       XColor xcolor;
511       if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
512         {
513           fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
514                    (a ? a->label : d->name), string);
515           exit (1);
516         }
517
518       gl_color[0] = xcolor.red   / 65536.0;
519       gl_color[1] = xcolor.green / 65536.0;
520       gl_color[2] = xcolor.blue  / 65536.0;
521       gl_color[3] = 1.0;
522     }
523   
524   if (font_p)
525     glColor3f (gl_color[0], gl_color[1], gl_color[2]);
526   else
527     glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gl_color);
528 }
529
530
531 static GLfloat
532 atom_size (molecule_atom *a)
533 {
534   if (do_bonds)
535     {
536       if (a->data->size2 == 0)
537         {
538           /* let the molecules have the same relative sizes, but scale
539              them to a smaller range, so that the bond-tubes are
540              actually visible...
541            */
542           GLfloat bot = 0.4;
543           GLfloat top = 0.6;
544           GLfloat min = 1.17;
545           GLfloat max = 1.80;
546           GLfloat ratio = (a->data->size - min) / (max - min);
547           a->data->size2 = bot + (ratio * (top - bot));
548         }
549       return a->data->size2;
550     }
551   else
552     return a->data->size;
553 }
554
555
556 static molecule_atom *
557 get_atom (molecule_atom *atoms, int natoms, int id)
558 {
559   int i;
560
561   /* quick short-circuit */
562   if (id < natoms)
563     {
564       if (atoms[id].id == id)
565         return &atoms[id];
566       if (id > 0 && atoms[id-1].id == id)
567         return &atoms[id-1];
568       if (id < natoms-1 && atoms[id+1].id == id)
569         return &atoms[id+1];
570     }
571
572   for (i = 0; i < natoms; i++)
573     if (id == atoms[i].id)
574       return &atoms[i];
575
576   fprintf (stderr, "%s: no atom %d\n", progname, id);
577   abort();
578 }
579
580
581 static void
582 molecule_bounding_box (ModeInfo *mi,
583                        GLfloat *x1, GLfloat *y1, GLfloat *z1,
584                        GLfloat *x2, GLfloat *y2, GLfloat *z2)
585 {
586   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
587   molecule *m = &mc->molecules[mc->which];
588   int i;
589
590   if (m->natoms == 0)
591     {
592       *x1 = *y1 = *z1 = *x2 = *y2 = *z2 = 0;
593     }
594   else
595     {
596       *x1 = *x2 = m->atoms[0].x;
597       *y1 = *y2 = m->atoms[0].y;
598       *z1 = *z2 = m->atoms[0].z;
599     }
600
601   for (i = 1; i < m->natoms; i++)
602     {
603       if (m->atoms[i].x < *x1) *x1 = m->atoms[i].x;
604       if (m->atoms[i].y < *y1) *y1 = m->atoms[i].y;
605       if (m->atoms[i].z < *z1) *z1 = m->atoms[i].z;
606
607       if (m->atoms[i].x > *x2) *x2 = m->atoms[i].x;
608       if (m->atoms[i].y > *y2) *y2 = m->atoms[i].y;
609       if (m->atoms[i].z > *z2) *z2 = m->atoms[i].z;
610     }
611
612   *x1 -= 1;
613   *y1 -= 1;
614   *z1 -= 1;
615   *x2 += 1;
616   *y2 += 1;
617   *z2 += 1;
618 }
619
620
621 static void
622 draw_bounding_box (ModeInfo *mi)
623 {
624   static GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 };
625   static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
626   int wire = MI_IS_WIREFRAME(mi);
627   GLfloat x1, y1, z1, x2, y2, z2;
628   molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
629
630   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);
631   glFrontFace(GL_CCW);
632
633   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
634   glNormal3f(0, 1, 0);
635   glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
636   glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
637   glEnd();
638   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
639   glNormal3f(0, -1, 0);
640   glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
641   glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
642   glEnd();
643   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
644   glNormal3f(0, 0, 1);
645   glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
646   glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
647   glEnd();
648   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
649   glNormal3f(0, 0, -1);
650   glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
651   glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
652   glEnd();
653   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
654   glNormal3f(1, 0, 0);
655   glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
656   glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
657   glEnd();
658   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
659   glNormal3f(-1, 0, 0);
660   glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
661   glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
662   glEnd();
663
664   glPushAttrib (GL_LIGHTING);
665   glDisable (GL_LIGHTING);
666
667   glColor3f (c2[0], c2[1], c2[2]);
668   glBegin(GL_LINES);
669   if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
670   if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
671   if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
672   glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
673   glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
674   glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
675   glEnd();
676
677   glPopAttrib();
678 }
679
680
681 /* Since PDB files don't always have the molecule centered around the
682    origin, and since some molecules are pretty large, scale and/or
683    translate so that the whole molecule is visible in the window.
684  */
685 static void
686 ensure_bounding_box_visible (ModeInfo *mi)
687 {
688   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
689
690   GLfloat x1, y1, z1, x2, y2, z2;
691   GLfloat w, h, d;
692   GLfloat size;
693   GLfloat max_size = 10;  /* don't bother scaling down if the molecule
694                              is already smaller than this */
695
696   molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
697   w = x2-x1;
698   h = y2-y1;
699   d = z2-z1;
700
701   size = (w > h ? w : h);
702   size = (size > d ? size : d);
703
704   mc->molecule_size = size;
705
706   scale_down = 0;
707
708   if (size > max_size)
709     {
710       GLfloat scale = max_size / size;
711       glScalef (scale, scale, scale);
712
713       scale_down = scale < 0.3;
714     }
715
716   glTranslatef (-(x1 + w/2),
717                 -(y1 + h/2),
718                 -(z1 + d/2));
719 }
720
721
722 static void
723 print_title_string (ModeInfo *mi, const char *string,
724                     GLfloat x, GLfloat y, GLfloat line_height)
725 {
726   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
727   
728   y -= line_height;
729
730   glPushAttrib (GL_LIGHTING | GL_DEPTH_TEST);
731   glDisable (GL_LIGHTING);
732   glDisable (GL_DEPTH_TEST);
733   {
734     glMatrixMode(GL_PROJECTION);
735     glPushMatrix();
736     {
737       glLoadIdentity();
738
739       glMatrixMode(GL_MODELVIEW);
740       glPushMatrix();
741       {
742         int i;
743         glLoadIdentity();
744
745         gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
746
747         set_atom_color (mi, 0, True);
748
749         glRasterPos2f (x, y);
750         for (i = 0; i < strlen(string); i++)
751           {
752             char c = string[i];
753             if (c == '\n')
754               glRasterPos2f (x, (y -= line_height));
755             else
756               glCallList (mc->font2_dlist + (int)(c));
757           }
758       }
759       glPopMatrix();
760     }
761     glMatrixMode(GL_PROJECTION);
762     glPopMatrix();
763   }
764   glPopAttrib();
765
766   glMatrixMode(GL_MODELVIEW);
767 }
768
769
770 /* Constructs the GL shapes of the current molecule
771  */
772 static void
773 build_molecule (ModeInfo *mi)
774 {
775   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
776   int wire = MI_IS_WIREFRAME(mi);
777   int i;
778
779   molecule *m = &mc->molecules[mc->which];
780
781   if (wire)
782     {
783       glDisable(GL_CULL_FACE);
784       glDisable(GL_LIGHTING);
785       glDisable(GL_LIGHT0);
786       glDisable(GL_DEPTH_TEST);
787       glDisable(GL_NORMALIZE);
788       glDisable(GL_CULL_FACE);
789     }
790   else
791     {
792       glEnable(GL_CULL_FACE);
793       glEnable(GL_LIGHTING);
794       glEnable(GL_LIGHT0);
795       glEnable(GL_DEPTH_TEST);
796       glEnable(GL_NORMALIZE);
797       glEnable(GL_CULL_FACE);
798     }
799
800   if (!wire)
801     set_atom_color (mi, 0, False);
802
803   if (do_bonds)
804     for (i = 0; i < m->nbonds; i++)
805       {
806         molecule_bond *b = &m->bonds[i];
807         molecule_atom *from = get_atom (m->atoms, m->natoms, b->from);
808         molecule_atom *to   = get_atom (m->atoms, m->natoms, b->to);
809
810         if (wire)
811           {
812             glBegin(GL_LINES);
813             glVertex3f(from->x, from->y, from->z);
814             glVertex3f(to->x,   to->y,   to->z);
815             glEnd();
816           }
817         else
818           {
819             GLfloat thickness = 0.07 * b->strength;
820             GLfloat cap_size = 0.03;
821             if (thickness > 0.3)
822               thickness = 0.3;
823             tube (from->x, from->y, from->z,
824                   to->x,   to->y,   to->z,
825                   thickness, cap_size, wire);
826           }
827       }
828
829   for (i = 0; i < m->natoms; i++)
830     {
831       molecule_atom *a = &m->atoms[i];
832       int i;
833
834       if (!wire && do_atoms)
835         {
836           GLfloat size = atom_size (a);
837           set_atom_color (mi, a, False);
838           sphere (a->x, a->y, a->z, size, wire);
839         }
840
841       if (do_labels)
842         {
843           glPushAttrib (GL_LIGHTING | GL_DEPTH_TEST);
844           glDisable (GL_LIGHTING);
845           glDisable (GL_DEPTH_TEST);
846
847           if (!wire)
848             set_atom_color (mi, a, True);
849
850           glRasterPos3f (a->x, a->y, a->z);
851
852           {
853             GLdouble mm[17], pm[17];
854             GLint vp[5];
855             GLdouble wx=-1, wy=-1, wz=-1;
856             glGetDoublev (GL_MODELVIEW_MATRIX, mm);
857             glGetDoublev (GL_PROJECTION_MATRIX, pm);
858             glGetIntegerv (GL_VIEWPORT, vp);
859
860             /* Convert 3D coordinates to window coordinates */
861             gluProject (a->x, a->y, a->z, mm, pm, vp, &wx, &wy, &wz);
862
863             /* Fudge the window coordinates to center the string */
864             wx -= string_width (mc->xfont1, a->label) / 2;
865             wy -= mc->xfont1->descent;
866
867             /* Convert new window coordinates back to 3D coordinates */
868             gluUnProject (wx, wy, wz, mm, pm, vp, &wx, &wy, &wz);
869             glRasterPos3f (wx, wy, wz);
870           }
871
872           for (i = 0; i < strlen(a->label); i++)
873             glCallList (mc->font1_dlist + (int)(a->label[i]));
874
875           glPopAttrib();
876         }
877     }
878
879   if (do_bbox)
880     draw_bounding_box (mi);
881
882   if (do_titles && m->label && *m->label)
883     print_title_string (mi, m->label,
884                         10, mi->xgwa.height - 10,
885                         mc->xfont2->ascent + mc->xfont2->descent);
886 }
887
888
889 \f
890 /* loading */
891
892 static void
893 push_atom (molecule *m,
894            int id, const char *label,
895            GLfloat x, GLfloat y, GLfloat z)
896 {
897   m->natoms++;
898   if (m->atoms_size < m->natoms)
899     {
900       m->atoms_size += 20;
901       m->atoms = (molecule_atom *) realloc (m->atoms,
902                                             m->atoms_size * sizeof(*m->atoms));
903     }
904   m->atoms[m->natoms-1].id = id;
905   m->atoms[m->natoms-1].label = label;
906   m->atoms[m->natoms-1].x = x;
907   m->atoms[m->natoms-1].y = y;
908   m->atoms[m->natoms-1].z = z;
909   m->atoms[m->natoms-1].data = get_atom_data (label);
910 }
911
912
913 static void
914 push_bond (molecule *m, int from, int to)
915 {
916   int i;
917
918   for (i = 0; i < m->nbonds; i++)
919     if ((m->bonds[i].from == from && m->bonds[i].to   == to) ||
920         (m->bonds[i].to   == from && m->bonds[i].from == to))
921       {
922         m->bonds[i].strength++;
923         return;
924       }
925
926   m->nbonds++;
927   if (m->bonds_size < m->nbonds)
928     {
929       m->bonds_size += 20;
930       m->bonds = (molecule_bond *) realloc (m->bonds,
931                                             m->bonds_size * sizeof(*m->bonds));
932     }
933   m->bonds[m->nbonds-1].from = from;
934   m->bonds[m->nbonds-1].to = to;
935   m->bonds[m->nbonds-1].strength = 1;
936 }
937
938
939
940 /* This function is crap.
941  */
942 static void
943 parse_pdb_data (molecule *m, const char *data, const char *filename, int line)
944 {
945   const char *s = data;
946   char *ss;
947   while (*s)
948     {
949       if ((!m->label || !*m->label) &&
950           (!strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6)))
951         {
952           char *name = calloc (1, 100);
953           char *n2 = name;
954           int L = strlen(s);
955           if (L > 99) L = 99;
956
957           strncpy (n2, s, L);
958           n2 += 7;
959           while (isspace(*n2)) n2++;
960
961           ss = strchr (n2, '\n');
962           if (ss) *ss = 0;
963           ss = strchr (n2, '\r');
964           if (ss) *ss = 0;
965
966           ss = n2+strlen(n2)-1;
967           while (isspace(*ss) && ss > n2)
968             *ss-- = 0;
969
970           if (strlen (n2) > 4 &&
971               !strcmp (n2 + strlen(n2) - 4, ".pdb"))
972             n2[strlen(n2)-4] = 0;
973
974           if (m->label) free ((char *) m->label);
975           m->label = strdup (n2);
976           free (name);
977         }
978       else if (!strncmp (s, "TITLE ", 6) ||
979                !strncmp (s, "HEADER", 6) ||
980                !strncmp (s, "COMPND", 6) ||
981                !strncmp (s, "AUTHOR", 6) ||
982                !strncmp (s, "REVDAT", 6) ||
983                !strncmp (s, "SOURCE", 6) ||
984                !strncmp (s, "EXPDTA", 6) ||
985                !strncmp (s, "JRNL  ", 6) ||
986                !strncmp (s, "REMARK", 6) ||
987                !strncmp (s, "SEQRES", 6) ||
988                !strncmp (s, "HET   ", 6) ||
989                !strncmp (s, "FORMUL", 6) ||
990                !strncmp (s, "CRYST1", 6) ||
991                !strncmp (s, "ORIGX1", 6) ||
992                !strncmp (s, "ORIGX2", 6) ||
993                !strncmp (s, "ORIGX3", 6) ||
994                !strncmp (s, "SCALE1", 6) ||
995                !strncmp (s, "SCALE2", 6) ||
996                !strncmp (s, "SCALE3", 6) ||
997                !strncmp (s, "MASTER", 6) ||
998                !strncmp (s, "KEYWDS", 6) ||
999                !strncmp (s, "DBREF ", 6) ||
1000                !strncmp (s, "HETNAM", 6) ||
1001                !strncmp (s, "HETSYN", 6) ||
1002                !strncmp (s, "HELIX ", 6) ||
1003                !strncmp (s, "LINK  ", 6) ||
1004                !strncmp (s, "MTRIX1", 6) ||
1005                !strncmp (s, "MTRIX2", 6) ||
1006                !strncmp (s, "MTRIX3", 6) ||
1007                !strncmp (s, "SHEET ", 6) ||
1008                !strncmp (s, "CISPEP", 6) ||
1009                !strncmp (s, "GENERATED BY", 12) ||
1010                !strncmp (s, "TER ", 4) ||
1011                !strncmp (s, "END ", 4) ||
1012                !strncmp (s, "TER\n", 4) ||
1013                !strncmp (s, "END\n", 4) ||
1014                !strncmp (s, "\n", 1))
1015         /* ignored. */
1016         ;
1017       else if (!strncmp (s, "ATOM   ", 7))
1018         {
1019           int id;
1020           char *name = (char *) calloc (1, 4);
1021           GLfloat x = -999, y = -999, z = -999;
1022
1023           sscanf (s+7, " %d ", &id);
1024
1025           strncpy (name, s+12, 3);
1026           while (isspace(*name)) name++;
1027           ss = name + strlen(name)-1;
1028           while (isspace(*ss) && ss > name)
1029             *ss-- = 0;
1030           sscanf (s + 32, " %f %f %f ", &x, &y, &z);
1031 /*
1032           fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
1033                    progname, filename, line,
1034                    id, name, x, y, z);
1035 */
1036           push_atom (m, id, name, x, y, z);
1037         }
1038       else if (!strncmp (s, "HETATM ", 7))
1039         {
1040           int id;
1041           char *name = (char *) calloc (1, 4);
1042           GLfloat x = -999, y = -999, z = -999;
1043
1044           sscanf (s+7, " %d ", &id);
1045
1046           strncpy (name, s+12, 3);
1047           while (isspace(*name)) name++;
1048           ss = name + strlen(name)-1;
1049           while (isspace(*ss) && ss > name)
1050             *ss-- = 0;
1051           sscanf (s + 30, " %f %f %f ", &x, &y, &z);
1052 /*
1053           fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
1054                    progname, filename, line,
1055                    id, name, x, y, z);
1056 */
1057           push_atom (m, id, name, x, y, z);
1058         }
1059       else if (!strncmp (s, "CONECT ", 7))
1060         {
1061           int atoms[11];
1062           int i = sscanf (s + 8, " %d %d %d %d %d %d %d %d %d %d %d ",
1063                           &atoms[0], &atoms[1], &atoms[2], &atoms[3], 
1064                           &atoms[4], &atoms[5], &atoms[6], &atoms[7], 
1065                           &atoms[8], &atoms[9], &atoms[10], &atoms[11]);
1066           int j;
1067           for (j = 1; j < i; j++)
1068             if (atoms[j] > 0)
1069               {
1070 /*
1071                 fprintf (stderr, "%s: %s: %d: bond: %d %d\n",
1072                          progname, filename, line, atoms[0], atoms[j]);
1073 */
1074                 push_bond (m, atoms[0], atoms[j]);
1075               }
1076         }
1077       else
1078         {
1079           char *s1 = strdup (s);
1080           for (ss = s1; *ss && *ss != '\n'; ss++)
1081             ;
1082           *ss = 0;
1083           fprintf (stderr, "%s: %s: %d: unrecognised line: %s\n",
1084                    progname, filename, line, s1);
1085         }
1086
1087       while (*s && *s != '\n')
1088         s++;
1089       if (*s == '\n')
1090         s++;
1091       line++;
1092     }
1093 }
1094
1095
1096 static void
1097 parse_pdb_file (molecule *m, const char *name)
1098 {
1099   FILE *in;
1100   int buf_size = 40960;
1101   char *buf;
1102   int line = 1;
1103
1104   in = fopen(name, "r");
1105   if (!in)
1106     {
1107       char *buf = (char *) malloc(1024 + strlen(name));
1108       sprintf(buf, "%s: error reading \"%s\"", progname, name);
1109       perror(buf);
1110       exit (1);
1111     }
1112
1113   buf = (char *) malloc (buf_size);
1114
1115   while (fgets (buf, buf_size-1, in))
1116     {
1117       char *s;
1118       for (s = buf; *s; s++)
1119         if (*s == '\r') *s = '\n';
1120       parse_pdb_data (m, buf, name, line++);
1121     }
1122
1123   free (buf);
1124   fclose (in);
1125
1126   if (!m->natoms)
1127     {
1128       fprintf (stderr, "%s: file %s contains no atomic coordinates!\n",
1129                progname, name);
1130       exit (1);
1131     }
1132
1133   if (!m->nbonds && do_bonds)
1134     {
1135       fprintf (stderr, "%s: warning: file %s contains no atomic bond info.\n",
1136                progname, name);
1137       do_bonds = 0;
1138     }
1139 }
1140
1141
1142 static void
1143 generate_molecule_formula (molecule *m)
1144 {
1145   char *buf = (char *) malloc (m->natoms * 10);
1146   char *s = buf;
1147   int i;
1148   struct { char *atom; int count; } counts[200];
1149   memset (counts, 0, sizeof(counts));
1150   *s = 0;
1151   for (i = 0; i < m->natoms; i++)
1152     {
1153       int j = 0;
1154       char *a = (char *) m->atoms[i].label;
1155       char *e;
1156       while (!isalpha(*a)) a++;
1157       a = strdup (a);
1158       for (e = a; isalpha(*e); e++);
1159       *e = 0;
1160       while (counts[j].atom && !!strcmp(a, counts[j].atom))
1161         j++;
1162       if (counts[j].atom)
1163         free (a);
1164       else
1165         counts[j].atom = a;
1166       counts[j].count++;
1167     }
1168
1169   i = 0;
1170   while (counts[i].atom)
1171     {
1172       strcat (s, counts[i].atom);
1173       free (counts[i].atom);
1174       s += strlen (s);
1175       if (counts[i].count > 1)
1176         sprintf (s, "(%d)", counts[i].count);
1177       s += strlen (s);
1178       i++;
1179     }
1180
1181   if (!m->label) m->label = strdup("");
1182   s = (char *) malloc (strlen (m->label) + strlen (buf) + 2);
1183   strcpy (s, m->label);
1184   strcat (s, "\n");
1185   strcat (s, buf);
1186   free ((char *) m->label);
1187   free (buf);
1188   m->label = s;
1189 }
1190
1191 static void
1192 insert_vertical_whitespace (char *string)
1193 {
1194   while (*string)
1195     {
1196       if ((string[0] == ',' ||
1197            string[0] == ';' ||
1198            string[0] == ':') &&
1199           string[1] == ' ')
1200         string[0] = ' ', string[1] = '\n';
1201       string++;
1202     }
1203 }
1204
1205
1206 /* Construct the molecule data from either: the builtins; or from
1207    the (one) .pdb file specified with -molecule.
1208  */
1209 static void
1210 load_molecules (ModeInfo *mi)
1211 {
1212   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1213   int wire = MI_IS_WIREFRAME(mi);
1214
1215   if (!molecule_str || !*molecule_str ||
1216       !strcmp(molecule_str, "(default)"))       /* do the builtins */
1217     {
1218       int i;
1219       mc->nmolecules = countof(builtin_pdb_data);
1220       mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1221       for (i = 0; i < mc->nmolecules; i++)
1222         {
1223           char name[100];
1224           sprintf (name, "<builtin-%d>", i);
1225           parse_pdb_data (&mc->molecules[i], builtin_pdb_data[i], name, 1);
1226           generate_molecule_formula (&mc->molecules[i]);
1227           insert_vertical_whitespace ((char *) mc->molecules[i].label);
1228         }
1229     }
1230   else                                          /* Load a file */
1231     {
1232       int i = 0;
1233       mc->nmolecules = 1;
1234       mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1235       parse_pdb_file (&mc->molecules[i], molecule_str);
1236       generate_molecule_formula (&mc->molecules[i]);
1237       insert_vertical_whitespace ((char *) mc->molecules[i].label);
1238
1239       if ((wire || !do_atoms) &&
1240           !do_labels &&
1241           mc->molecules[i].nbonds == 0)
1242         {
1243           /* If we're not drawing atoms (e.g., wireframe mode), and
1244              there is no bond info, then make sure labels are turned on,
1245              or we'll be looking at a black screen... */
1246           fprintf (stderr, "%s: no bonds: turning -label on.\n", progname);
1247           do_labels = 1;
1248         }
1249     }
1250 }
1251
1252
1253 \f
1254 /* Window management, etc
1255  */
1256 void
1257 reshape_molecule (ModeInfo *mi, int width, int height)
1258 {
1259   GLfloat h = (GLfloat) height / (GLfloat) width;
1260
1261   glViewport (0, 0, (GLint) width, (GLint) height);
1262
1263   glMatrixMode(GL_PROJECTION);
1264   glLoadIdentity();
1265
1266   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
1267   gluLookAt( 0.0, 0.0, 15.0,
1268              0.0, 0.0, 0.0,
1269              0.0, 1.0, 0.0);
1270   glMatrixMode(GL_MODELVIEW);
1271   glLoadIdentity();
1272   glTranslatef(0.0, 0.0, -15.0);
1273
1274   glClear(GL_COLOR_BUFFER_BIT);
1275 }
1276
1277
1278 static void
1279 gl_init (ModeInfo *mi)
1280 {
1281   static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
1282   glLightfv(GL_LIGHT0, GL_POSITION, pos);
1283
1284   orig_do_labels = do_labels;
1285   orig_do_bonds = do_bonds;
1286   orig_wire = MI_IS_WIREFRAME(mi);
1287 }
1288
1289
1290 /* lifted from lament.c */
1291 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
1292 #define RANDSIGN() ((random() & 1) ? 1 : -1)
1293
1294 static void
1295 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
1296 {
1297   double ppos = *pos;
1298
1299   /* tick position */
1300   if (ppos < 0)
1301     ppos = -(ppos + *v);
1302   else
1303     ppos += *v;
1304
1305   if (ppos > 1.0)
1306     ppos -= 1.0;
1307   else if (ppos < 0)
1308     ppos += 1.0;
1309
1310   if (ppos < 0) abort();
1311   if (ppos > 1.0) abort();
1312   *pos = (*pos > 0 ? ppos : -ppos);
1313
1314   /* accelerate */
1315   *v += *dv;
1316
1317   /* clamp velocity */
1318   if (*v > max_v || *v < -max_v)
1319     {
1320       *dv = -*dv;
1321     }
1322   /* If it stops, start it going in the other direction. */
1323   else if (*v < 0)
1324     {
1325       if (random() % 4)
1326         {
1327           *v = 0;
1328
1329           /* keep going in the same direction */
1330           if (random() % 2)
1331             *dv = 0;
1332           else if (*dv < 0)
1333             *dv = -*dv;
1334         }
1335       else
1336         {
1337           /* reverse gears */
1338           *v = -*v;
1339           *dv = -*dv;
1340           *pos = -*pos;
1341         }
1342     }
1343
1344   /* Alter direction of rotational acceleration randomly. */
1345   if (! (random() % 120))
1346     *dv = -*dv;
1347
1348   /* Change acceleration very occasionally. */
1349   if (! (random() % 200))
1350     {
1351       if (*dv == 0)
1352         *dv = 0.00001;
1353       else if (random() & 1)
1354         *dv *= 1.2;
1355       else
1356         *dv *= 0.8;
1357     }
1358 }
1359
1360
1361 static void
1362 startup_blurb (ModeInfo *mi)
1363 {
1364   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1365   const char *s = "Constructing molecules...";
1366   print_title_string (mi, s,
1367                       mi->xgwa.width - (string_width (mc->xfont2, s) + 40),
1368                       10 + mc->xfont2->ascent + mc->xfont2->descent,
1369                       mc->xfont2->ascent + mc->xfont2->descent);
1370   glFinish();
1371   glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1372 }
1373
1374 void 
1375 init_molecule (ModeInfo *mi)
1376 {
1377   molecule_configuration *mc;
1378   int wire;
1379
1380   if (!mcs) {
1381     mcs = (molecule_configuration *)
1382       calloc (MI_NUM_SCREENS(mi), sizeof (molecule_configuration));
1383     if (!mcs) {
1384       fprintf(stderr, "%s: out of memory\n", progname);
1385       exit(1);
1386     }
1387   }
1388
1389   mc = &mcs[MI_SCREEN(mi)];
1390
1391   if ((mc->glx_context = init_GL(mi)) != NULL) {
1392     gl_init(mi);
1393     reshape_molecule (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1394   }
1395
1396   load_fonts (mi);
1397   startup_blurb (mi);
1398
1399   wire = MI_IS_WIREFRAME(mi);
1400
1401   mc->rotx = frand(1.0) * RANDSIGN();
1402   mc->roty = frand(1.0) * RANDSIGN();
1403   mc->rotz = frand(1.0) * RANDSIGN();
1404
1405   /* bell curve from 0-6 degrees, avg 3 */
1406   mc->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
1407   mc->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
1408   mc->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
1409
1410   mc->d_max = mc->dx * 2;
1411
1412   mc->ddx = 0.00006 + frand(0.00003);
1413   mc->ddy = 0.00006 + frand(0.00003);
1414   mc->ddz = 0.00006 + frand(0.00003);
1415
1416   {
1417     char *s = do_spin;
1418     while (*s)
1419       {
1420         if      (*s == 'x' || *s == 'X') mc->spin_x = 1;
1421         else if (*s == 'y' || *s == 'Y') mc->spin_y = 1;
1422         else if (*s == 'z' || *s == 'Z') mc->spin_z = 1;
1423         else
1424           {
1425             fprintf (stderr,
1426          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
1427                      progname, do_spin);
1428             exit (1);
1429           }
1430         s++;
1431       }
1432   }
1433
1434   mc->molecule_dlist = glGenLists(1);
1435
1436   load_molecules (mi);
1437   mc->which = random() % mc->nmolecules;
1438
1439   mc->no_label_threshold = get_float_resource ("noLabelThreshold",
1440                                                "NoLabelThreshold");
1441   mc->wireframe_threshold = get_float_resource ("wireframeThreshold",
1442                                                 "WireframeThreshold");
1443
1444   if (wire)
1445     do_bonds = 1;
1446 }
1447
1448
1449 void
1450 draw_molecule (ModeInfo *mi)
1451 {
1452   static time_t last = 0;
1453   time_t now = time ((time_t *) 0);
1454
1455   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1456   Display *dpy = MI_DISPLAY(mi);
1457   Window window = MI_WINDOW(mi);
1458
1459   if (!mc->glx_context)
1460     return;
1461
1462   if (last + timeout <= now)   /* randomize molecules every -timeout seconds */
1463     {
1464       if (mc->nmolecules == 1)
1465         {
1466           if (last != 0) goto SKIP;
1467           mc->which = 0;
1468         }
1469       else if (last == 0)
1470         {
1471           mc->which = random() % mc->nmolecules;
1472         }
1473       else
1474         {
1475           int n = mc->which;
1476           while (n == mc->which)
1477             n = random() % mc->nmolecules;
1478           mc->which = n;
1479         }
1480
1481       last = now;
1482
1483
1484       glNewList (mc->molecule_dlist, GL_COMPILE);
1485       ensure_bounding_box_visible (mi);
1486
1487       do_labels = orig_do_labels;
1488       do_bonds = orig_do_bonds;
1489       MI_IS_WIREFRAME(mi) = orig_wire;
1490
1491       if (mc->molecule_size > mc->no_label_threshold)
1492         do_labels = 0;
1493       if (mc->molecule_size > mc->wireframe_threshold)
1494         MI_IS_WIREFRAME(mi) = 1;
1495
1496       if (MI_IS_WIREFRAME(mi))
1497         do_bonds = 1;
1498
1499       build_molecule (mi);
1500       glEndList();
1501     }
1502  SKIP:
1503
1504   glPushMatrix ();
1505   glScalef(1.1, 1.1, 1.1);
1506
1507   {
1508     GLfloat x, y, z;
1509
1510     if (do_wander)
1511       {
1512         static int frame = 0;
1513
1514 #       define SINOID(SCALE,SIZE) \
1515         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
1516
1517         x = SINOID(0.031, 9.0);
1518         y = SINOID(0.023, 9.0);
1519         z = SINOID(0.017, 9.0);
1520         frame++;
1521         glTranslatef(x, y, z);
1522       }
1523
1524     if (mc->spin_x || mc->spin_y || mc->spin_z)
1525       {
1526         x = mc->rotx;
1527         y = mc->roty;
1528         z = mc->rotz;
1529         if (x < 0) x = 1 - (x + 1);
1530         if (y < 0) y = 1 - (y + 1);
1531         if (z < 0) z = 1 - (z + 1);
1532
1533         if (mc->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0);
1534         if (mc->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0);
1535         if (mc->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0);
1536
1537         rotate(&mc->rotx, &mc->dx, &mc->ddx, mc->d_max);
1538         rotate(&mc->roty, &mc->dy, &mc->ddy, mc->d_max);
1539         rotate(&mc->rotz, &mc->dz, &mc->ddz, mc->d_max);
1540       }
1541   }
1542
1543   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1544   glCallList (mc->molecule_dlist);
1545   glPopMatrix ();
1546
1547   if (mi->fps_p) do_fps (mi);
1548   glFinish();
1549
1550   glXSwapBuffers(dpy, window);
1551 }
1552
1553 #endif /* USE_GL */