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