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