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