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_LIGHTING | GL_DEPTH_TEST);
556 glDisable (GL_LIGHTING);
557 glDisable (GL_DEPTH_TEST);
559 glMatrixMode(GL_PROJECTION);
564 glMatrixMode(GL_MODELVIEW);
570 gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
572 set_atom_color (mi, 0, True);
574 glRasterPos2f (x, y);
575 for (i = 0; i < strlen(string); i++)
579 glRasterPos2f (x, (y -= line_height));
581 glCallList (mc->font2_dlist + (int)(c));
586 glMatrixMode(GL_PROJECTION);
591 glMatrixMode(GL_MODELVIEW);
595 /* Constructs the GL shapes of the current molecule
598 build_molecule (ModeInfo *mi)
600 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
601 int wire = MI_IS_WIREFRAME(mi);
604 molecule *m = &mc->molecules[mc->which];
608 glDisable(GL_CULL_FACE);
609 glDisable(GL_LIGHTING);
610 glDisable(GL_LIGHT0);
611 glDisable(GL_DEPTH_TEST);
612 glDisable(GL_NORMALIZE);
613 glDisable(GL_CULL_FACE);
617 glEnable(GL_CULL_FACE);
618 glEnable(GL_LIGHTING);
620 glEnable(GL_DEPTH_TEST);
621 glEnable(GL_NORMALIZE);
622 glEnable(GL_CULL_FACE);
626 set_atom_color (mi, 0, False);
629 for (i = 0; i < m->nbonds; i++)
631 molecule_bond *b = &m->bonds[i];
632 molecule_atom *from = get_atom (m->atoms, m->natoms, b->from);
633 molecule_atom *to = get_atom (m->atoms, m->natoms, b->to);
638 glVertex3f(from->x, from->y, from->z);
639 glVertex3f(to->x, to->y, to->z);
644 int faces = (scale_down ? TUBE_FACES_2 : TUBE_FACES);
650 GLfloat thickness = 0.07 * b->strength;
651 GLfloat cap_size = 0.03;
655 tube (from->x, from->y, from->z,
658 faces, smooth, wire);
662 for (i = 0; i < m->natoms; i++)
664 molecule_atom *a = &m->atoms[i];
667 if (!wire && do_atoms)
669 GLfloat size = atom_size (a);
670 set_atom_color (mi, a, False);
671 sphere (a->x, a->y, a->z, size, wire);
676 glPushAttrib (GL_LIGHTING | GL_DEPTH_TEST);
677 glDisable (GL_LIGHTING);
678 glDisable (GL_DEPTH_TEST);
681 set_atom_color (mi, a, True);
683 glRasterPos3f (a->x, a->y, a->z);
686 GLdouble mm[17], pm[17];
688 GLdouble wx=-1, wy=-1, wz=-1;
689 glGetDoublev (GL_MODELVIEW_MATRIX, mm);
690 glGetDoublev (GL_PROJECTION_MATRIX, pm);
691 glGetIntegerv (GL_VIEWPORT, vp);
693 /* Convert 3D coordinates to window coordinates */
694 gluProject (a->x, a->y, a->z, mm, pm, vp, &wx, &wy, &wz);
696 /* Fudge the window coordinates to center the string */
697 wx -= string_width (mc->xfont1, a->label) / 2;
698 wy -= mc->xfont1->descent;
700 /* Convert new window coordinates back to 3D coordinates */
701 gluUnProject (wx, wy, wz, mm, pm, vp, &wx, &wy, &wz);
702 glRasterPos3f (wx, wy, wz);
705 for (i = 0; i < strlen(a->label); i++)
706 glCallList (mc->font1_dlist + (int)(a->label[i]));
713 draw_bounding_box (mi);
715 if (do_titles && m->label && *m->label)
716 print_title_string (mi, m->label,
717 10, mi->xgwa.height - 10,
718 mc->xfont2->ascent + mc->xfont2->descent);
726 push_atom (molecule *m,
727 int id, const char *label,
728 GLfloat x, GLfloat y, GLfloat z)
731 if (m->atoms_size < m->natoms)
734 m->atoms = (molecule_atom *) realloc (m->atoms,
735 m->atoms_size * sizeof(*m->atoms));
737 m->atoms[m->natoms-1].id = id;
738 m->atoms[m->natoms-1].label = label;
739 m->atoms[m->natoms-1].x = x;
740 m->atoms[m->natoms-1].y = y;
741 m->atoms[m->natoms-1].z = z;
742 m->atoms[m->natoms-1].data = get_atom_data (label);
747 push_bond (molecule *m, int from, int to)
751 for (i = 0; i < m->nbonds; i++)
752 if ((m->bonds[i].from == from && m->bonds[i].to == to) ||
753 (m->bonds[i].to == from && m->bonds[i].from == to))
755 m->bonds[i].strength++;
760 if (m->bonds_size < m->nbonds)
763 m->bonds = (molecule_bond *) realloc (m->bonds,
764 m->bonds_size * sizeof(*m->bonds));
766 m->bonds[m->nbonds-1].from = from;
767 m->bonds[m->nbonds-1].to = to;
768 m->bonds[m->nbonds-1].strength = 1;
773 /* This function is crap.
776 parse_pdb_data (molecule *m, const char *data, const char *filename, int line)
778 const char *s = data;
782 if ((!m->label || !*m->label) &&
783 (!strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6)))
785 char *name = calloc (1, 100);
792 while (isspace(*n2)) n2++;
794 ss = strchr (n2, '\n');
796 ss = strchr (n2, '\r');
799 ss = n2+strlen(n2)-1;
800 while (isspace(*ss) && ss > n2)
803 if (strlen (n2) > 4 &&
804 !strcmp (n2 + strlen(n2) - 4, ".pdb"))
805 n2[strlen(n2)-4] = 0;
807 if (m->label) free ((char *) m->label);
808 m->label = strdup (n2);
811 else if (!strncmp (s, "TITLE ", 6) ||
812 !strncmp (s, "HEADER", 6) ||
813 !strncmp (s, "COMPND", 6) ||
814 !strncmp (s, "AUTHOR", 6) ||
815 !strncmp (s, "REVDAT", 6) ||
816 !strncmp (s, "SOURCE", 6) ||
817 !strncmp (s, "EXPDTA", 6) ||
818 !strncmp (s, "JRNL ", 6) ||
819 !strncmp (s, "REMARK", 6) ||
820 !strncmp (s, "SEQRES", 6) ||
821 !strncmp (s, "HET ", 6) ||
822 !strncmp (s, "FORMUL", 6) ||
823 !strncmp (s, "CRYST1", 6) ||
824 !strncmp (s, "ORIGX1", 6) ||
825 !strncmp (s, "ORIGX2", 6) ||
826 !strncmp (s, "ORIGX3", 6) ||
827 !strncmp (s, "SCALE1", 6) ||
828 !strncmp (s, "SCALE2", 6) ||
829 !strncmp (s, "SCALE3", 6) ||
830 !strncmp (s, "MASTER", 6) ||
831 !strncmp (s, "KEYWDS", 6) ||
832 !strncmp (s, "DBREF ", 6) ||
833 !strncmp (s, "HETNAM", 6) ||
834 !strncmp (s, "HETSYN", 6) ||
835 !strncmp (s, "HELIX ", 6) ||
836 !strncmp (s, "LINK ", 6) ||
837 !strncmp (s, "MTRIX1", 6) ||
838 !strncmp (s, "MTRIX2", 6) ||
839 !strncmp (s, "MTRIX3", 6) ||
840 !strncmp (s, "SHEET ", 6) ||
841 !strncmp (s, "CISPEP", 6) ||
842 !strncmp (s, "GENERATED BY", 12) ||
843 !strncmp (s, "TER ", 4) ||
844 !strncmp (s, "END ", 4) ||
845 !strncmp (s, "TER\n", 4) ||
846 !strncmp (s, "END\n", 4) ||
847 !strncmp (s, "\n", 1))
850 else if (!strncmp (s, "ATOM ", 7))
853 char *name = (char *) calloc (1, 4);
854 GLfloat x = -999, y = -999, z = -999;
856 sscanf (s+7, " %d ", &id);
858 strncpy (name, s+12, 3);
859 while (isspace(*name)) name++;
860 ss = name + strlen(name)-1;
861 while (isspace(*ss) && ss > name)
863 sscanf (s + 32, " %f %f %f ", &x, &y, &z);
865 fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
866 progname, filename, line,
869 push_atom (m, id, name, x, y, z);
871 else if (!strncmp (s, "HETATM ", 7))
874 char *name = (char *) calloc (1, 4);
875 GLfloat x = -999, y = -999, z = -999;
877 sscanf (s+7, " %d ", &id);
879 strncpy (name, s+12, 3);
880 while (isspace(*name)) name++;
881 ss = name + strlen(name)-1;
882 while (isspace(*ss) && ss > name)
884 sscanf (s + 30, " %f %f %f ", &x, &y, &z);
886 fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
887 progname, filename, line,
890 push_atom (m, id, name, x, y, z);
892 else if (!strncmp (s, "CONECT ", 7))
895 int i = sscanf (s + 8, " %d %d %d %d %d %d %d %d %d %d %d ",
896 &atoms[0], &atoms[1], &atoms[2], &atoms[3],
897 &atoms[4], &atoms[5], &atoms[6], &atoms[7],
898 &atoms[8], &atoms[9], &atoms[10], &atoms[11]);
900 for (j = 1; j < i; j++)
904 fprintf (stderr, "%s: %s: %d: bond: %d %d\n",
905 progname, filename, line, atoms[0], atoms[j]);
907 push_bond (m, atoms[0], atoms[j]);
912 char *s1 = strdup (s);
913 for (ss = s1; *ss && *ss != '\n'; ss++)
916 fprintf (stderr, "%s: %s: %d: unrecognised line: %s\n",
917 progname, filename, line, s1);
920 while (*s && *s != '\n')
930 parse_pdb_file (molecule *m, const char *name)
933 int buf_size = 40960;
937 in = fopen(name, "r");
940 char *buf = (char *) malloc(1024 + strlen(name));
941 sprintf(buf, "%s: error reading \"%s\"", progname, name);
946 buf = (char *) malloc (buf_size);
948 while (fgets (buf, buf_size-1, in))
951 for (s = buf; *s; s++)
952 if (*s == '\r') *s = '\n';
953 parse_pdb_data (m, buf, name, line++);
961 fprintf (stderr, "%s: file %s contains no atomic coordinates!\n",
966 if (!m->nbonds && do_bonds)
968 fprintf (stderr, "%s: warning: file %s contains no atomic bond info.\n",
976 generate_molecule_formula (molecule *m)
978 char *buf = (char *) malloc (m->natoms * 10);
981 struct { char *atom; int count; } counts[200];
982 memset (counts, 0, sizeof(counts));
984 for (i = 0; i < m->natoms; i++)
987 char *a = (char *) m->atoms[i].label;
989 while (!isalpha(*a)) a++;
991 for (e = a; isalpha(*e); e++);
993 while (counts[j].atom && !!strcmp(a, counts[j].atom))
1003 while (counts[i].atom)
1005 strcat (s, counts[i].atom);
1006 free (counts[i].atom);
1008 if (counts[i].count > 1)
1009 sprintf (s, "(%d)", counts[i].count);
1014 if (!m->label) m->label = strdup("");
1015 s = (char *) malloc (strlen (m->label) + strlen (buf) + 2);
1016 strcpy (s, m->label);
1019 free ((char *) m->label);
1025 insert_vertical_whitespace (char *string)
1029 if ((string[0] == ',' ||
1031 string[0] == ':') &&
1033 string[0] = ' ', string[1] = '\n';
1039 /* Construct the molecule data from either: the builtins; or from
1040 the (one) .pdb file specified with -molecule.
1043 load_molecules (ModeInfo *mi)
1045 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1046 int wire = MI_IS_WIREFRAME(mi);
1048 if (!molecule_str || !*molecule_str ||
1049 !strcmp(molecule_str, "(default)")) /* do the builtins */
1052 mc->nmolecules = countof(builtin_pdb_data);
1053 mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1054 for (i = 0; i < mc->nmolecules; i++)
1057 sprintf (name, "<builtin-%d>", i);
1058 parse_pdb_data (&mc->molecules[i], builtin_pdb_data[i], name, 1);
1059 generate_molecule_formula (&mc->molecules[i]);
1060 insert_vertical_whitespace ((char *) mc->molecules[i].label);
1063 else /* Load a file */
1067 mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
1068 parse_pdb_file (&mc->molecules[i], molecule_str);
1069 generate_molecule_formula (&mc->molecules[i]);
1070 insert_vertical_whitespace ((char *) mc->molecules[i].label);
1072 if ((wire || !do_atoms) &&
1074 mc->molecules[i].nbonds == 0)
1076 /* If we're not drawing atoms (e.g., wireframe mode), and
1077 there is no bond info, then make sure labels are turned on,
1078 or we'll be looking at a black screen... */
1079 fprintf (stderr, "%s: no bonds: turning -label on.\n", progname);
1087 /* Window management, etc
1090 reshape_molecule (ModeInfo *mi, int width, int height)
1092 GLfloat h = (GLfloat) height / (GLfloat) width;
1094 glViewport (0, 0, (GLint) width, (GLint) height);
1096 glMatrixMode(GL_PROJECTION);
1099 gluPerspective( 30.0, 1/h, 1.0, 100.0 );
1100 gluLookAt( 0.0, 0.0, 15.0,
1103 glMatrixMode(GL_MODELVIEW);
1105 glTranslatef(0.0, 0.0, -15.0);
1107 glClear(GL_COLOR_BUFFER_BIT);
1112 gl_init (ModeInfo *mi)
1114 static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
1115 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1117 orig_do_labels = do_labels;
1118 orig_do_bonds = do_bonds;
1119 orig_wire = MI_IS_WIREFRAME(mi);
1123 /* lifted from lament.c */
1124 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
1125 #define RANDSIGN() ((random() & 1) ? 1 : -1)
1128 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
1134 ppos = -(ppos + *v);
1143 if (ppos < 0) abort();
1144 if (ppos > 1.0) abort();
1145 *pos = (*pos > 0 ? ppos : -ppos);
1150 /* clamp velocity */
1151 if (*v > max_v || *v < -max_v)
1155 /* If it stops, start it going in the other direction. */
1162 /* keep going in the same direction */
1177 /* Alter direction of rotational acceleration randomly. */
1178 if (! (random() % 120))
1181 /* Change acceleration very occasionally. */
1182 if (! (random() % 200))
1186 else if (random() & 1)
1195 startup_blurb (ModeInfo *mi)
1197 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1198 const char *s = "Constructing molecules...";
1199 print_title_string (mi, s,
1200 mi->xgwa.width - (string_width (mc->xfont2, s) + 40),
1201 10 + mc->xfont2->ascent + mc->xfont2->descent,
1202 mc->xfont2->ascent + mc->xfont2->descent);
1204 glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1208 init_molecule (ModeInfo *mi)
1210 molecule_configuration *mc;
1214 mcs = (molecule_configuration *)
1215 calloc (MI_NUM_SCREENS(mi), sizeof (molecule_configuration));
1217 fprintf(stderr, "%s: out of memory\n", progname);
1222 mc = &mcs[MI_SCREEN(mi)];
1224 if ((mc->glx_context = init_GL(mi)) != NULL) {
1226 reshape_molecule (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1232 wire = MI_IS_WIREFRAME(mi);
1234 mc->rotx = frand(1.0) * RANDSIGN();
1235 mc->roty = frand(1.0) * RANDSIGN();
1236 mc->rotz = frand(1.0) * RANDSIGN();
1238 /* bell curve from 0-6 degrees, avg 3 */
1239 mc->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
1240 mc->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
1241 mc->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
1243 mc->d_max = mc->dx * 2;
1245 mc->ddx = 0.00006 + frand(0.00003);
1246 mc->ddy = 0.00006 + frand(0.00003);
1247 mc->ddz = 0.00006 + frand(0.00003);
1253 if (*s == 'x' || *s == 'X') mc->spin_x = 1;
1254 else if (*s == 'y' || *s == 'Y') mc->spin_y = 1;
1255 else if (*s == 'z' || *s == 'Z') mc->spin_z = 1;
1259 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
1267 mc->molecule_dlist = glGenLists(1);
1269 load_molecules (mi);
1270 mc->which = random() % mc->nmolecules;
1272 mc->no_label_threshold = get_float_resource ("noLabelThreshold",
1273 "NoLabelThreshold");
1274 mc->wireframe_threshold = get_float_resource ("wireframeThreshold",
1275 "WireframeThreshold");
1283 draw_molecule (ModeInfo *mi)
1285 static time_t last = 0;
1286 time_t now = time ((time_t *) 0);
1288 molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
1289 Display *dpy = MI_DISPLAY(mi);
1290 Window window = MI_WINDOW(mi);
1292 if (!mc->glx_context)
1295 if (last + timeout <= now) /* randomize molecules every -timeout seconds */
1297 if (mc->nmolecules == 1)
1299 if (last != 0) goto SKIP;
1304 mc->which = random() % mc->nmolecules;
1309 while (n == mc->which)
1310 n = random() % mc->nmolecules;
1317 glNewList (mc->molecule_dlist, GL_COMPILE);
1318 ensure_bounding_box_visible (mi);
1320 do_labels = orig_do_labels;
1321 do_bonds = orig_do_bonds;
1322 MI_IS_WIREFRAME(mi) = orig_wire;
1324 if (mc->molecule_size > mc->no_label_threshold)
1326 if (mc->molecule_size > mc->wireframe_threshold)
1327 MI_IS_WIREFRAME(mi) = 1;
1329 if (MI_IS_WIREFRAME(mi))
1332 build_molecule (mi);
1338 glScalef(1.1, 1.1, 1.1);
1345 static int frame = 0;
1347 # define SINOID(SCALE,SIZE) \
1348 ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
1350 x = SINOID(0.031, 9.0);
1351 y = SINOID(0.023, 9.0);
1352 z = SINOID(0.017, 9.0);
1354 glTranslatef(x, y, z);
1357 if (mc->spin_x || mc->spin_y || mc->spin_z)
1362 if (x < 0) x = 1 - (x + 1);
1363 if (y < 0) y = 1 - (y + 1);
1364 if (z < 0) z = 1 - (z + 1);
1366 if (mc->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0);
1367 if (mc->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0);
1368 if (mc->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0);
1370 rotate(&mc->rotx, &mc->dx, &mc->ddx, mc->d_max);
1371 rotate(&mc->roty, &mc->dy, &mc->ddy, mc->d_max);
1372 rotate(&mc->rotz, &mc->dz, &mc->ddz, mc->d_max);
1376 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1377 glCallList (mc->molecule_dlist);
1380 if (mi->fps_p) do_fps (mi);
1383 glXSwapBuffers(dpy, window);