1 /* molecule, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2 * Draws molecules, based on coordinates from PDB (Protein Data Base) files.
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
14 /* Documentation on the PDB file format:
15 http://www.rcsb.org/pdb/docs/format/pdbguide2.2/guide2.2_frame.html
17 Good source of PDB files:
18 http://www.sci.ouc.bc.ca/chem/molecule/molecule.html
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.
27 #include <X11/Intrinsic.h>
29 #define PROGCLASS "Molecule"
30 #define HACK_INIT init_molecule
31 #define HACK_DRAW draw_molecule
32 #define HACK_RESHAPE reshape_molecule
33 #define molecule_opts xlockmore_opts
35 #define DEF_TIMEOUT "20"
36 #define DEF_SPIN "XYZ"
37 #define DEF_WANDER "False"
38 #define DEF_LABELS "True"
39 #define DEF_TITLES "True"
40 #define DEF_ATOMS "True"
41 #define DEF_BONDS "True"
42 #define DEF_BBOX "False"
43 #define DEF_MOLECULE "(default)"
45 #define DEFAULTS "*delay: 10000 \n" \
46 "*timeout: " DEF_TIMEOUT "\n" \
47 "*showFPS: False \n" \
48 "*wireframe: False \n" \
49 "*molecule: " DEF_MOLECULE "\n" \
50 "*spin: " DEF_SPIN "\n" \
51 "*wander: " DEF_WANDER "\n" \
52 "*labels: " DEF_LABELS "\n" \
53 "*atoms: " DEF_ATOMS "\n" \
54 "*bonds: " DEF_BONDS "\n" \
55 "*bbox: " DEF_BBOX "\n" \
56 "*atomFont: -*-times-bold-r-normal-*-240-*\n" \
57 "*titleFont: -*-times-bold-r-normal-*-180-*\n" \
58 "*noLabelThreshold: 30 \n" \
59 "*wireframeThreshold: 150 \n" \
63 #define countof(x) (sizeof((x))/sizeof((*x)))
65 #include "xlockmore.h"
70 #ifdef USE_GL /* whole file */
75 #define SPHERE_SLICES 16 /* how densely to render spheres */
76 #define SPHERE_STACKS 10
78 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
81 # define TUBE_FACES 12 /* how densely to render tubes */
86 static int scale_down;
87 #define SPHERE_SLICES_2 7
88 #define SPHERE_STACKS_2 4
89 #define TUBE_FACES_2 3
92 const char * const builtin_pdb_data[] = {
93 # include "molecules.h"
101 const char *text_color;
106 /* These are the traditional colors used to render these atoms,
107 and their approximate size in angstroms.
109 static atom_data all_atom_data[] = {
110 { "H", 1.17, 0, "White", "Grey70", { 0, }},
111 { "C", 1.75, 0, "Grey60", "White", { 0, }},
112 { "N", 1.55, 0, "LightSteelBlue3", "SlateBlue1", { 0, }},
113 { "O", 1.40, 0, "Red", "LightPink", { 0, }},
114 { "P", 1.28, 0, "MediumPurple", "PaleVioletRed", { 0, }},
115 { "S", 1.80, 0, "Yellow4", "Yellow1", { 0, }},
116 { "bond", 0, 0, "Grey70", "Yellow1", { 0, }},
117 { "*", 1.40, 0, "Green4", "LightGreen", { 0, }}
122 int id; /* sequence number in the PDB file */
123 const char *label; /* The atom name */
124 GLfloat x, y, z; /* position in 3-space (angstroms) */
125 atom_data *data; /* computed: which style of atom this is */
129 int from, to; /* atom sequence numbers */
130 int strength; /* how many bonds are between these two atoms */
135 const char *label; /* description of this compound */
136 int natoms, atoms_size;
137 int nbonds, bonds_size;
138 molecule_atom *atoms;
139 molecule_bond *bonds;
144 GLXContext *glx_context;
146 GLfloat rotx, roty, rotz; /* current object rotation */
147 GLfloat dx, dy, dz; /* current rotational velocity */
148 GLfloat ddx, ddy, ddz; /* current rotational acceleration */
149 GLfloat d_max; /* max velocity */
151 Bool spin_x, spin_y, spin_z;
153 GLfloat molecule_size; /* max dimension of molecule bounding box */
155 GLfloat no_label_threshold; /* Things happen when molecules are huge */
156 GLfloat wireframe_threshold;
158 int which; /* which of the molecules is being shown */
162 GLuint molecule_dlist;
164 XFontStruct *xfont1, *xfont2;
165 GLuint font1_dlist, font2_dlist;
167 } molecule_configuration;
170 static molecule_configuration *mcs = NULL;
173 static char *molecule_str;
174 static char *do_spin;
175 static Bool do_wander;
176 static Bool do_titles;
177 static Bool do_labels;
178 static Bool do_atoms;
179 static Bool do_bonds;
182 static Bool orig_do_labels, orig_do_bonds, orig_wire; /* saved to reset */
185 static XrmOptionDescRec opts[] = {
186 { "-molecule", ".molecule", XrmoptionSepArg, 0 },
187 { "-timeout",".timeout",XrmoptionSepArg, 0 },
188 { "-spin", ".spin", XrmoptionSepArg, 0 },
189 { "+spin", ".spin", XrmoptionNoArg, "" },
190 { "-wander", ".wander", XrmoptionNoArg, "True" },
191 { "+wander", ".wander", XrmoptionNoArg, "False" },
192 { "-labels", ".labels", XrmoptionNoArg, "True" },
193 { "+labels", ".labels", XrmoptionNoArg, "False" },
194 { "-titles", ".titles", XrmoptionNoArg, "True" },
195 { "+titles", ".titles", XrmoptionNoArg, "False" },
196 { "-atoms", ".atoms", XrmoptionNoArg, "True" },
197 { "+atoms", ".atoms", XrmoptionNoArg, "False" },
198 { "-bonds", ".bonds", XrmoptionNoArg, "True" },
199 { "+bonds", ".bonds", XrmoptionNoArg, "False" },
200 { "-bbox", ".bbox", XrmoptionNoArg, "True" },
201 { "+bbox", ".bbox", XrmoptionNoArg, "False" },
204 static argtype vars[] = {
205 {(caddr_t *) &molecule_str, "molecule", "Molecule", DEF_MOLECULE,t_String},
206 {(caddr_t *) &timeout, "timeout","Seconds",DEF_TIMEOUT,t_Int},
207 {(caddr_t *) &do_spin, "spin", "Spin", DEF_SPIN, t_String},
208 {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
209 {(caddr_t *) &do_labels, "labels", "Labels", DEF_LABELS, t_Bool},
210 {(caddr_t *) &do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
211 {(caddr_t *) &do_atoms, "atoms", "Atoms", DEF_ATOMS, t_Bool},
212 {(caddr_t *) &do_bonds, "bonds", "Bonds", DEF_BONDS, t_Bool},
213 {(caddr_t *) &do_bbox, "bbox", "BBox", DEF_BBOX, t_Bool},
216 ModeSpecOpt molecule_opts = {countof(opts), opts, countof(vars), vars, NULL};
224 sphere (GLfloat x, GLfloat y, GLfloat z, GLfloat diameter, Bool wire)
226 int stacks = (scale_down ? SPHERE_STACKS_2 : SPHERE_STACKS);
227 int slices = (scale_down ? SPHERE_SLICES_2 : SPHERE_SLICES);
230 glTranslatef (x, y, z);
231 glScalef (diameter, diameter, diameter);
232 unit_sphere (stacks, slices, wire);
238 load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
240 const char *font = get_string_resource (res, "Font");
245 if (!font) font = "-*-times-bold-r-normal-*-180-*";
247 f = XLoadQueryFont(mi->dpy, font);
248 if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
251 first = f->min_char_or_byte2;
252 last = f->max_char_or_byte2;
255 *dlistP = glGenLists ((GLuint) last+1);
256 check_gl_error ("glGenLists");
257 glXUseXFont(id, first, last-first+1, *dlistP + first);
258 check_gl_error ("glXUseXFont");
265 load_fonts (ModeInfo *mi)
267 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
268 load_font (mi, "atomFont", &mc->xfont1, &mc->font1_dlist);
269 load_font (mi, "titleFont", &mc->xfont2, &mc->font2_dlist);
274 string_width (XFontStruct *f, const char *c)
279 int cc = *((unsigned char *) c);
281 ? f->per_char[cc-f->min_char_or_byte2].rbearing
282 : f->min_bounds.rbearing);
290 get_atom_data (const char *atom_name)
294 char *n = strdup (atom_name);
298 while (!isalpha(*n)) n++;
300 while (L > 0 && !isalpha(n[L-1]))
303 for (i = 0; i < countof(all_atom_data); i++)
305 d = &all_atom_data[i];
306 if (!strcmp (n, all_atom_data[i].name))
316 set_atom_color (ModeInfo *mi, molecule_atom *a, Bool font_p)
325 static atom_data *def_data = 0;
326 if (!def_data) def_data = get_atom_data ("bond");
330 gl_color = (!font_p ? d->gl_color : (d->gl_color + 4));
332 if (gl_color[3] == 0)
334 const char *string = !font_p ? d->color : d->text_color;
336 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
338 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
339 (a ? a->label : d->name), string);
343 gl_color[0] = xcolor.red / 65536.0;
344 gl_color[1] = xcolor.green / 65536.0;
345 gl_color[2] = xcolor.blue / 65536.0;
350 glColor3f (gl_color[0], gl_color[1], gl_color[2]);
352 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gl_color);
357 atom_size (molecule_atom *a)
361 if (a->data->size2 == 0)
363 /* let the molecules have the same relative sizes, but scale
364 them to a smaller range, so that the bond-tubes are
371 GLfloat ratio = (a->data->size - min) / (max - min);
372 a->data->size2 = bot + (ratio * (top - bot));
374 return a->data->size2;
377 return a->data->size;
381 static molecule_atom *
382 get_atom (molecule_atom *atoms, int natoms, int id)
386 /* quick short-circuit */
389 if (atoms[id].id == id)
391 if (id > 0 && atoms[id-1].id == id)
393 if (id < natoms-1 && atoms[id+1].id == id)
397 for (i = 0; i < natoms; i++)
398 if (id == atoms[i].id)
401 fprintf (stderr, "%s: no atom %d\n", progname, id);
407 molecule_bounding_box (ModeInfo *mi,
408 GLfloat *x1, GLfloat *y1, GLfloat *z1,
409 GLfloat *x2, GLfloat *y2, GLfloat *z2)
411 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
412 molecule *m = &mc->molecules[mc->which];
417 *x1 = *y1 = *z1 = *x2 = *y2 = *z2 = 0;
421 *x1 = *x2 = m->atoms[0].x;
422 *y1 = *y2 = m->atoms[0].y;
423 *z1 = *z2 = m->atoms[0].z;
426 for (i = 1; i < m->natoms; i++)
428 if (m->atoms[i].x < *x1) *x1 = m->atoms[i].x;
429 if (m->atoms[i].y < *y1) *y1 = m->atoms[i].y;
430 if (m->atoms[i].z < *z1) *z1 = m->atoms[i].z;
432 if (m->atoms[i].x > *x2) *x2 = m->atoms[i].x;
433 if (m->atoms[i].y > *y2) *y2 = m->atoms[i].y;
434 if (m->atoms[i].z > *z2) *z2 = m->atoms[i].z;
447 draw_bounding_box (ModeInfo *mi)
449 static GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 };
450 static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
451 int wire = MI_IS_WIREFRAME(mi);
452 GLfloat x1, y1, z1, x2, y2, z2;
453 molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
455 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);
458 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
460 glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
461 glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
463 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
464 glNormal3f(0, -1, 0);
465 glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
466 glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
468 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
470 glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
471 glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
473 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
474 glNormal3f(0, 0, -1);
475 glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
476 glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
478 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
480 glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
481 glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
483 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
484 glNormal3f(-1, 0, 0);
485 glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
486 glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
489 glPushAttrib (GL_LIGHTING);
490 glDisable (GL_LIGHTING);
492 glColor3f (c2[0], c2[1], c2[2]);
494 if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
495 if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
496 if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
497 glVertex3f(x1, 0, 0); glVertex3f(x2, 0, 0);
498 glVertex3f(0 , y1, 0); glVertex3f(0, y2, 0);
499 glVertex3f(0, 0, z1); glVertex3f(0, 0, z2);
506 /* Since PDB files don't always have the molecule centered around the
507 origin, and since some molecules are pretty large, scale and/or
508 translate so that the whole molecule is visible in the window.
511 ensure_bounding_box_visible (ModeInfo *mi)
513 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
515 GLfloat x1, y1, z1, x2, y2, z2;
518 GLfloat max_size = 10; /* don't bother scaling down if the molecule
519 is already smaller than this */
521 molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
526 size = (w > h ? w : h);
527 size = (size > d ? size : d);
529 mc->molecule_size = size;
535 GLfloat scale = max_size / size;
536 glScalef (scale, scale, scale);
538 scale_down = scale < 0.3;
541 glTranslatef (-(x1 + w/2),
548 print_title_string (ModeInfo *mi, const char *string,
549 GLfloat x, GLfloat y, GLfloat line_height)
551 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
555 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
556 GL_ENABLE_BIT); /* for various glDisable calls */
557 glDisable (GL_LIGHTING);
558 glDisable (GL_DEPTH_TEST);
560 glMatrixMode(GL_PROJECTION);
565 glMatrixMode(GL_MODELVIEW);
571 gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
573 set_atom_color (mi, 0, True);
575 glRasterPos2f (x, y);
576 for (i = 0; i < strlen(string); i++)
580 glRasterPos2f (x, (y -= line_height));
582 glCallList (mc->font2_dlist + (int)(c));
587 glMatrixMode(GL_PROJECTION);
592 glMatrixMode(GL_MODELVIEW);
596 /* Constructs the GL shapes of the current molecule
599 build_molecule (ModeInfo *mi)
601 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
602 int wire = MI_IS_WIREFRAME(mi);
605 molecule *m = &mc->molecules[mc->which];
609 glDisable(GL_CULL_FACE);
610 glDisable(GL_LIGHTING);
611 glDisable(GL_LIGHT0);
612 glDisable(GL_DEPTH_TEST);
613 glDisable(GL_NORMALIZE);
614 glDisable(GL_CULL_FACE);
618 glEnable(GL_CULL_FACE);
619 glEnable(GL_LIGHTING);
621 glEnable(GL_DEPTH_TEST);
622 glEnable(GL_NORMALIZE);
623 glEnable(GL_CULL_FACE);
627 if (do_labels && !wire)
629 /* This is so all polygons are drawn slightly farther back in the depth
630 buffer, so that when we render text directly on top of the spheres,
631 it still shows up. */
632 glEnable (GL_POLYGON_OFFSET_FILL);
633 glPolygonOffset (1.0, (do_bonds ? 10.0 : 35.0));
637 glDisable (GL_POLYGON_OFFSET_FILL);
642 set_atom_color (mi, 0, False);
645 for (i = 0; i < m->nbonds; i++)
647 molecule_bond *b = &m->bonds[i];
648 molecule_atom *from = get_atom (m->atoms, m->natoms, b->from);
649 molecule_atom *to = get_atom (m->atoms, m->natoms, b->to);
654 glVertex3f(from->x, from->y, from->z);
655 glVertex3f(to->x, to->y, to->z);
660 int faces = (scale_down ? TUBE_FACES_2 : TUBE_FACES);
666 GLfloat thickness = 0.07 * b->strength;
667 GLfloat cap_size = 0.03;
671 tube (from->x, from->y, from->z,
674 faces, smooth, wire);
678 if (!wire && do_atoms)
679 for (i = 0; i < m->natoms; i++)
681 molecule_atom *a = &m->atoms[i];
682 GLfloat size = atom_size (a);
683 set_atom_color (mi, a, False);
684 sphere (a->x, a->y, a->z, size, wire);
687 /* Second pass to draw labels, after all atoms and bonds are in place
690 for (i = 0; i < m->natoms; i++)
692 molecule_atom *a = &m->atoms[i];
697 glDisable (GL_LIGHTING);
699 glDisable (GL_DEPTH_TEST);
704 set_atom_color (mi, a, True);
706 glRasterPos3f (a->x, a->y, a->z);
708 /* Before drawing the string, shift the origin to center
709 the text over the origin of the sphere. */
710 glBitmap (0, 0, 0, 0,
711 -string_width (mc->xfont1, a->label) / 2,
712 -mc->xfont1->descent,
715 for (j = 0; j < strlen(a->label); j++)
716 glCallList (mc->font1_dlist + (int)(a->label[j]));
718 /* More efficient to always call glEnable() with correct values
719 than to call glPushAttrib()/glPopAttrib(), since reading
720 attributes from GL does a round-trip and stalls the pipeline.
724 glEnable(GL_LIGHTING);
726 glEnable(GL_DEPTH_TEST);
732 draw_bounding_box (mi);
734 if (do_titles && m->label && *m->label)
735 print_title_string (mi, m->label,
736 10, mi->xgwa.height - 10,
737 mc->xfont2->ascent + mc->xfont2->descent);
745 push_atom (molecule *m,
746 int id, const char *label,
747 GLfloat x, GLfloat y, GLfloat z)
750 if (m->atoms_size < m->natoms)
753 m->atoms = (molecule_atom *) realloc (m->atoms,
754 m->atoms_size * sizeof(*m->atoms));
756 m->atoms[m->natoms-1].id = id;
757 m->atoms[m->natoms-1].label = label;
758 m->atoms[m->natoms-1].x = x;
759 m->atoms[m->natoms-1].y = y;
760 m->atoms[m->natoms-1].z = z;
761 m->atoms[m->natoms-1].data = get_atom_data (label);
766 push_bond (molecule *m, int from, int to)
770 for (i = 0; i < m->nbonds; i++)
771 if ((m->bonds[i].from == from && m->bonds[i].to == to) ||
772 (m->bonds[i].to == from && m->bonds[i].from == to))
774 m->bonds[i].strength++;
779 if (m->bonds_size < m->nbonds)
782 m->bonds = (molecule_bond *) realloc (m->bonds,
783 m->bonds_size * sizeof(*m->bonds));
785 m->bonds[m->nbonds-1].from = from;
786 m->bonds[m->nbonds-1].to = to;
787 m->bonds[m->nbonds-1].strength = 1;
792 /* This function is crap.
795 parse_pdb_data (molecule *m, const char *data, const char *filename, int line)
797 const char *s = data;
801 if ((!m->label || !*m->label) &&
802 (!strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6)))
804 char *name = calloc (1, 100);
811 while (isspace(*n2)) n2++;
813 ss = strchr (n2, '\n');
815 ss = strchr (n2, '\r');
818 ss = n2+strlen(n2)-1;
819 while (isspace(*ss) && ss > n2)
822 if (strlen (n2) > 4 &&
823 !strcmp (n2 + strlen(n2) - 4, ".pdb"))
824 n2[strlen(n2)-4] = 0;
826 if (m->label) free ((char *) m->label);
827 m->label = strdup (n2);
830 else if (!strncmp (s, "TITLE ", 6) ||
831 !strncmp (s, "HEADER", 6) ||
832 !strncmp (s, "COMPND", 6) ||
833 !strncmp (s, "AUTHOR", 6) ||
834 !strncmp (s, "REVDAT", 6) ||
835 !strncmp (s, "SOURCE", 6) ||
836 !strncmp (s, "EXPDTA", 6) ||
837 !strncmp (s, "JRNL ", 6) ||
838 !strncmp (s, "REMARK", 6) ||
839 !strncmp (s, "SEQRES", 6) ||
840 !strncmp (s, "HET ", 6) ||
841 !strncmp (s, "FORMUL", 6) ||
842 !strncmp (s, "CRYST1", 6) ||
843 !strncmp (s, "ORIGX1", 6) ||
844 !strncmp (s, "ORIGX2", 6) ||
845 !strncmp (s, "ORIGX3", 6) ||
846 !strncmp (s, "SCALE1", 6) ||
847 !strncmp (s, "SCALE2", 6) ||
848 !strncmp (s, "SCALE3", 6) ||
849 !strncmp (s, "MASTER", 6) ||
850 !strncmp (s, "KEYWDS", 6) ||
851 !strncmp (s, "DBREF ", 6) ||
852 !strncmp (s, "HETNAM", 6) ||
853 !strncmp (s, "HETSYN", 6) ||
854 !strncmp (s, "HELIX ", 6) ||
855 !strncmp (s, "LINK ", 6) ||
856 !strncmp (s, "MTRIX1", 6) ||
857 !strncmp (s, "MTRIX2", 6) ||
858 !strncmp (s, "MTRIX3", 6) ||
859 !strncmp (s, "SHEET ", 6) ||
860 !strncmp (s, "CISPEP", 6) ||
861 !strncmp (s, "GENERATED BY", 12) ||
862 !strncmp (s, "TER ", 4) ||
863 !strncmp (s, "END ", 4) ||
864 !strncmp (s, "TER\n", 4) ||
865 !strncmp (s, "END\n", 4) ||
866 !strncmp (s, "\n", 1))
869 else if (!strncmp (s, "ATOM ", 7))
872 char *name = (char *) calloc (1, 4);
873 GLfloat x = -999, y = -999, z = -999;
875 sscanf (s+7, " %d ", &id);
877 strncpy (name, s+12, 3);
878 while (isspace(*name)) name++;
879 ss = name + strlen(name)-1;
880 while (isspace(*ss) && ss > name)
882 sscanf (s + 32, " %f %f %f ", &x, &y, &z);
884 fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
885 progname, filename, line,
888 push_atom (m, id, name, x, y, z);
890 else if (!strncmp (s, "HETATM ", 7))
893 char *name = (char *) calloc (1, 4);
894 GLfloat x = -999, y = -999, z = -999;
896 sscanf (s+7, " %d ", &id);
898 strncpy (name, s+12, 3);
899 while (isspace(*name)) name++;
900 ss = name + strlen(name)-1;
901 while (isspace(*ss) && ss > name)
903 sscanf (s + 30, " %f %f %f ", &x, &y, &z);
905 fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
906 progname, filename, line,
909 push_atom (m, id, name, x, y, z);
911 else if (!strncmp (s, "CONECT ", 7))
914 int i = sscanf (s + 8, " %d %d %d %d %d %d %d %d %d %d %d ",
915 &atoms[0], &atoms[1], &atoms[2], &atoms[3],
916 &atoms[4], &atoms[5], &atoms[6], &atoms[7],
917 &atoms[8], &atoms[9], &atoms[10], &atoms[11]);
919 for (j = 1; j < i; j++)
923 fprintf (stderr, "%s: %s: %d: bond: %d %d\n",
924 progname, filename, line, atoms[0], atoms[j]);
926 push_bond (m, atoms[0], atoms[j]);
931 char *s1 = strdup (s);
932 for (ss = s1; *ss && *ss != '\n'; ss++)
935 fprintf (stderr, "%s: %s: %d: unrecognised line: %s\n",
936 progname, filename, line, s1);
939 while (*s && *s != '\n')
949 parse_pdb_file (molecule *m, const char *name)
952 int buf_size = 40960;
956 in = fopen(name, "r");
959 char *buf = (char *) malloc(1024 + strlen(name));
960 sprintf(buf, "%s: error reading \"%s\"", progname, name);
965 buf = (char *) malloc (buf_size);
967 while (fgets (buf, buf_size-1, in))
970 for (s = buf; *s; s++)
971 if (*s == '\r') *s = '\n';
972 parse_pdb_data (m, buf, name, line++);
980 fprintf (stderr, "%s: file %s contains no atomic coordinates!\n",
985 if (!m->nbonds && do_bonds)
987 fprintf (stderr, "%s: warning: file %s contains no atomic bond info.\n",
995 generate_molecule_formula (molecule *m)
997 char *buf = (char *) malloc (m->natoms * 10);
1000 struct { char *atom; int count; } counts[200];
1001 memset (counts, 0, sizeof(counts));
1003 for (i = 0; i < m->natoms; i++)
1006 char *a = (char *) m->atoms[i].label;
1008 while (!isalpha(*a)) a++;
1010 for (e = a; isalpha(*e); e++);
1012 while (counts[j].atom && !!strcmp(a, counts[j].atom))
1022 while (counts[i].atom)
1024 strcat (s, counts[i].atom);
1025 free (counts[i].atom);
1027 if (counts[i].count > 1)
1028 sprintf (s, "(%d)", counts[i].count);
1033 if (!m->label) m->label = strdup("");
1034 s = (char *) malloc (strlen (m->label) + strlen (buf) + 2);
1035 strcpy (s, m->label);
1038 free ((char *) m->label);
1044 insert_vertical_whitespace (char *string)
1048 if ((string[0] == ',' ||
1050 string[0] == ':') &&
1052 string[0] = ' ', string[1] = '\n';
1058 /* Construct the molecule data from either: the builtins; or from
1059 the (one) .pdb file specified with -molecule.
1062 load_molecules (ModeInfo *mi)
1064 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1065 int wire = MI_IS_WIREFRAME(mi);
1067 if (!molecule_str || !*molecule_str ||
1068 !strcmp(molecule_str, "(default)")) /* do the builtins */
1071 mc->nmolecules = countof(builtin_pdb_data);
1072 mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1073 for (i = 0; i < mc->nmolecules; i++)
1076 sprintf (name, "<builtin-%d>", i);
1077 parse_pdb_data (&mc->molecules[i], builtin_pdb_data[i], name, 1);
1078 generate_molecule_formula (&mc->molecules[i]);
1079 insert_vertical_whitespace ((char *) mc->molecules[i].label);
1082 else /* Load a file */
1086 mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1087 parse_pdb_file (&mc->molecules[i], molecule_str);
1088 generate_molecule_formula (&mc->molecules[i]);
1089 insert_vertical_whitespace ((char *) mc->molecules[i].label);
1091 if ((wire || !do_atoms) &&
1093 mc->molecules[i].nbonds == 0)
1095 /* If we're not drawing atoms (e.g., wireframe mode), and
1096 there is no bond info, then make sure labels are turned on,
1097 or we'll be looking at a black screen... */
1098 fprintf (stderr, "%s: no bonds: turning -label on.\n", progname);
1106 /* Window management, etc
1109 reshape_molecule (ModeInfo *mi, int width, int height)
1111 GLfloat h = (GLfloat) height / (GLfloat) width;
1113 glViewport (0, 0, (GLint) width, (GLint) height);
1115 glMatrixMode(GL_PROJECTION);
1118 gluPerspective( 30.0, 1/h, 20.0, 40.0 );
1119 gluLookAt( 0.0, 0.0, 15.0,
1123 glMatrixMode(GL_MODELVIEW);
1125 glTranslatef(0.0, 0.0, -15.0);
1127 glClear(GL_COLOR_BUFFER_BIT);
1132 gl_init (ModeInfo *mi)
1134 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
1135 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1137 orig_do_labels = do_labels;
1138 orig_do_bonds = do_bonds;
1139 orig_wire = MI_IS_WIREFRAME(mi);
1143 /* lifted from lament.c */
1144 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
1145 #define RANDSIGN() ((random() & 1) ? 1 : -1)
1148 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
1154 ppos = -(ppos + *v);
1163 if (ppos < 0) abort();
1164 if (ppos > 1.0) abort();
1165 *pos = (*pos > 0 ? ppos : -ppos);
1170 /* clamp velocity */
1171 if (*v > max_v || *v < -max_v)
1175 /* If it stops, start it going in the other direction. */
1182 /* keep going in the same direction */
1197 /* Alter direction of rotational acceleration randomly. */
1198 if (! (random() % 120))
1201 /* Change acceleration very occasionally. */
1202 if (! (random() % 200))
1206 else if (random() & 1)
1215 startup_blurb (ModeInfo *mi)
1217 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1218 const char *s = "Constructing molecules...";
1219 print_title_string (mi, s,
1220 mi->xgwa.width - (string_width (mc->xfont2, s) + 40),
1221 10 + mc->xfont2->ascent + mc->xfont2->descent,
1222 mc->xfont2->ascent + mc->xfont2->descent);
1224 glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1228 init_molecule (ModeInfo *mi)
1230 molecule_configuration *mc;
1234 mcs = (molecule_configuration *)
1235 calloc (MI_NUM_SCREENS(mi), sizeof (molecule_configuration));
1237 fprintf(stderr, "%s: out of memory\n", progname);
1242 mc = &mcs[MI_SCREEN(mi)];
1244 if ((mc->glx_context = init_GL(mi)) != NULL) {
1246 reshape_molecule (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1252 wire = MI_IS_WIREFRAME(mi);
1254 mc->rotx = frand(1.0) * RANDSIGN();
1255 mc->roty = frand(1.0) * RANDSIGN();
1256 mc->rotz = frand(1.0) * RANDSIGN();
1258 /* bell curve from 0-6 degrees, avg 3 */
1259 mc->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
1260 mc->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
1261 mc->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
1263 mc->d_max = mc->dx * 2;
1265 mc->ddx = 0.00006 + frand(0.00003);
1266 mc->ddy = 0.00006 + frand(0.00003);
1267 mc->ddz = 0.00006 + frand(0.00003);
1273 if (*s == 'x' || *s == 'X') mc->spin_x = 1;
1274 else if (*s == 'y' || *s == 'Y') mc->spin_y = 1;
1275 else if (*s == 'z' || *s == 'Z') mc->spin_z = 1;
1279 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
1287 mc->molecule_dlist = glGenLists(1);
1289 load_molecules (mi);
1290 mc->which = random() % mc->nmolecules;
1292 mc->no_label_threshold = get_float_resource ("noLabelThreshold",
1293 "NoLabelThreshold");
1294 mc->wireframe_threshold = get_float_resource ("wireframeThreshold",
1295 "WireframeThreshold");
1303 draw_molecule (ModeInfo *mi)
1305 static time_t last = 0;
1306 time_t now = time ((time_t *) 0);
1308 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1309 Display *dpy = MI_DISPLAY(mi);
1310 Window window = MI_WINDOW(mi);
1312 if (!mc->glx_context)
1315 if (last + timeout <= now) /* randomize molecules every -timeout seconds */
1317 if (mc->nmolecules == 1)
1319 if (last != 0) goto SKIP;
1324 mc->which = random() % mc->nmolecules;
1329 while (n == mc->which)
1330 n = random() % mc->nmolecules;
1337 glNewList (mc->molecule_dlist, GL_COMPILE);
1338 ensure_bounding_box_visible (mi);
1340 do_labels = orig_do_labels;
1341 do_bonds = orig_do_bonds;
1342 MI_IS_WIREFRAME(mi) = orig_wire;
1344 if (mc->molecule_size > mc->no_label_threshold)
1346 if (mc->molecule_size > mc->wireframe_threshold)
1347 MI_IS_WIREFRAME(mi) = 1;
1349 if (MI_IS_WIREFRAME(mi))
1352 build_molecule (mi);
1358 glScalef(1.1, 1.1, 1.1);
1365 static int frame = 0;
1367 # define SINOID(SCALE,SIZE) \
1368 ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
1370 x = SINOID(0.031, 9.0);
1371 y = SINOID(0.023, 9.0);
1372 z = SINOID(0.017, 9.0);
1374 glTranslatef(x, y, z);
1377 if (mc->spin_x || mc->spin_y || mc->spin_z)
1382 if (x < 0) x = 1 - (x + 1);
1383 if (y < 0) y = 1 - (y + 1);
1384 if (z < 0) z = 1 - (z + 1);
1386 if (mc->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0);
1387 if (mc->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0);
1388 if (mc->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0);
1390 rotate(&mc->rotx, &mc->dx, &mc->ddx, mc->d_max);
1391 rotate(&mc->roty, &mc->dy, &mc->ddy, mc->d_max);
1392 rotate(&mc->rotz, &mc->dz, &mc->ddz, mc->d_max);
1396 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1397 glCallList (mc->molecule_dlist);
1400 if (mi->fps_p) do_fps (mi);
1403 glXSwapBuffers(dpy, window);