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