/* -*- Mode: C; tab-width: 4 -*- */ /* molecule --- 3D molecules */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)molecule.c 5.01 2001/04/12 xlockmore"; #endif /* molecule, Copyright (c) 2001 Jamie Zawinski * Draws molecules, based on coordinates from PDB (Protein Data Base) files. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * * Revision History: * 16-May-01: tschmidt@micron.com * Fix remaining memory leaks. Fix occasional SEGV. * 19-Apr-01: tschmidt@micron.com * Fixed most memory leaks * 19-Apr-01: rolf@groppe.de fixed some allocation problems * Startup message now only at first call * 12-Apr-01: rolf@groppe.de Ported this mode from xscreensaver to xlock. * Use of parameter -cycles instead of timeout, * when used as xlock module. * Some minor changes for better display in * iconified state. */ /* Documentation on the PDB file format: http://www.rcsb.org/pdb/docs/format/pdbguide2.2/guide2.2_frame.html Good source of PDB files: http://www.sci.ouc.bc.ca/chem/molecule/molecule.html TO DO: - I'm not sure the text labels are being done in the best way; they are sometimes, but not always, occluded by spheres that pass in front of them. */ #ifdef VMS #include "vms_x_fix.h" #include #endif #define DEF_TIMEOUT "20" #define DEF_SPIN "XYZ" #define DEF_WANDER "False" #define DEF_LABELS "True" #define DEF_TITLES "True" #define DEF_ATOMS "True" #define DEF_BONDS "True" #define DEF_BBOX "False" #define DEF_MOLECULE "(default)" #ifdef STANDALONE #define MODE_molecule #define PROGCLASS "Molecule" #define HACK_INIT init_molecule #define HACK_DRAW draw_molecule #define HACK_RESHAPE reshape_molecule #define molecule_opts xlockmore_opts #define DEFAULTS "*delay: 10000 \n" \ "*timeout: " DEF_TIMEOUT "\n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ "*molecule: " DEF_MOLECULE "\n" \ "*spin: " DEF_SPIN "\n" \ "*wander: " DEF_WANDER "\n" \ "*labels: " DEF_LABELS "\n" \ "*atoms: " DEF_ATOMS "\n" \ "*bonds: " DEF_BONDS "\n" \ "*bbox: " DEF_BBOX "\n" \ "*atomFont: -*-times-bold-r-normal-*-240-*\n" \ "*titleFont: -*-times-bold-r-normal-*-180-*\n" \ "*noLabelThreshold: 30 \n" \ "*wireframeThreshold: 150 \n" \ #include "xlockmore.h" #include "colors.h" #else /* !STANDALONE */ # include "xlock.h" /* from the xlockmore distribution */ # include "visgl.h" #endif /* !STANDALONE */ #include "sphere.h" #include "tube.h" #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #ifdef MODE_molecule /* whole file */ #include #include #include #define SPHERE_SLICES 16 /* how densely to render spheres */ #define SPHERE_STACKS 10 #define SMOOTH_TUBE /* whether to have smooth or faceted tubes */ #ifdef SMOOTH_TUBE # define TUBE_FACES 12 /* how densely to render tubes */ #else # define TUBE_FACES 8 #endif #define FLOATRAND(a) (((double)LRAND() / (double)MAXRAND) * a) static int scale_down; #define SPHERE_SLICES_2 7 #define SPHERE_STACKS_2 4 #define TUBE_FACES_2 3 static const char * const builtin_pdb_data[] = { # include "molecules.h" }; typedef struct { const char *name; GLfloat size, size2; const char *color; const char *text_color; GLfloat gl_color[8]; } atom_data; /* These are the traditional colors used to render these atoms, and their approximate size in angstroms. */ static atom_data all_atom_data[] = { { "H", 1.17, 0, "White", "Grey60", { 0, }}, { "C", 1.75, 0, "Grey50", "White", { 0, }}, { "N", 1.55, 0, "RoyalBlue1", "NavyBlue", { 0, }}, { "O", 1.40, 0, "Red", "LightPink", { 0, }}, { "P", 1.28, 0, "MediumPurple", "PaleVioletRed", { 0, }}, { "S", 1.80, 0, "Yellow4", "Yellow1", { 0, }}, { "bond", 0, 0, "Grey70", "Yellow1", { 0, }}, { "*", 1.40, 0, "Green4", "PaleGreen2", { 0, }} }; typedef struct { int id; /* sequence number in the PDB file */ const char *label; /* The atom name */ GLfloat x, y, z; /* position in 3-space (angstroms) */ atom_data *data; /* computed: which style of atom this is */ } molecule_atom; typedef struct { int from, to; /* atom sequence numbers */ int strength; /* how many bonds are between these two atoms */ } molecule_bond; typedef struct { const char *label; /* description of this compound */ int natoms, atoms_size; int nbonds, bonds_size; molecule_atom *atoms; molecule_bond *bonds; } molecule; typedef struct { GLXContext *glx_context; GLfloat rotx, roty, rotz; /* current object rotation */ GLfloat dx, dy, dz; /* current rotational velocity */ GLfloat ddx, ddy, ddz; /* current rotational acceleration */ GLfloat d_max; /* max velocity */ Bool spin_x, spin_y, spin_z; GLfloat molecule_size; /* max dimension of molecule bounding box */ GLfloat no_label_threshold; /* Things happen when molecules are huge */ GLfloat wireframe_threshold; int which; /* which of the molecules is being shown */ int nmolecules; molecule *molecules; GLuint molecule_dlist; XFontStruct *xfont1, *xfont2; GLuint font1_dlist, font2_dlist; } molecule_configuration; static molecule_configuration *mcs = (molecule_configuration *) NULL; static molecule *mols = (molecule *) NULL; static int timeout; static char *molecule_str; static char *do_spin; static Bool do_wander; static Bool do_titles; static Bool do_labels; static Bool do_atoms; static Bool do_bonds; static Bool do_bbox; static Bool orig_do_labels, orig_do_titles, orig_do_bonds, cur_wire, orig_wire; /* saved to reset */ static Bool not_first_gl = False; static Bool firstcall = True; static XrmOptionDescRec opts[] = { {(char *) "-molecule", (char *) ".molecule.molecule", XrmoptionSepArg, 0 }, #ifdef STANDALONE {(char *) "-timeout", (char *) ".molecule.timeout",XrmoptionSepArg, 0 }, #endif {(char *) "-spin", (char *) ".molecule.spin", XrmoptionSepArg, 0 }, {(char *) "+spin", (char *) ".molecule.spin", XrmoptionNoArg, (caddr_t) NULL}, {(char *) "-wander", (char *) ".molecule.wander", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+wander", (char *) ".molecule.wander", XrmoptionNoArg, (caddr_t) "off" }, {(char *) "-labels", (char *) ".molecule.labels", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+labels", (char *) ".molecule.labels", XrmoptionNoArg, (caddr_t) "off" }, {(char *) "-titles", (char *) ".molecule.titles", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+titles", (char *) ".molecule.titles", XrmoptionNoArg, (caddr_t) "off" }, {(char *) "-atoms", (char *) ".molecule.atoms", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+atoms", (char *) ".molecule.atoms", XrmoptionNoArg, (caddr_t) "off" }, {(char *) "-bonds", (char *) ".molecule.bonds", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+bonds", (char *) ".molecule.bonds", XrmoptionNoArg, (caddr_t) "off" }, {(char *) "-bbox", (char *) ".molecule.bbox", XrmoptionNoArg, (caddr_t) "on" }, {(char *) "+bbox", (char *) ".molecule.bbox", XrmoptionNoArg, (caddr_t) "off" }, }; static argtype vars[] = { {(void *) &molecule_str, (char *) "molecule", (char *) "Molecule", (char *) DEF_MOLECULE,t_String}, #ifdef STANDALONE {(void *) &timeout, (char *) "timeout",(char *) "Seconds", (char *) DEF_TIMEOUT,t_Int}, #endif {(void *) &do_spin, (char *) "spin", (char *) "Spin", (char *) DEF_SPIN, t_String}, {(void *) &do_wander, (char *) "wander", (char *) "Wander", (char *) DEF_WANDER, t_Bool}, {(void *) &do_labels, (char *) "labels", (char *) "Labels", (char *) DEF_LABELS, t_Bool}, {(void *) &do_titles, (char *) "titles", (char *) "Titles", (char *) DEF_TITLES, t_Bool}, {(void *) &do_atoms, (char *) "atoms", (char *) "Atoms", (char *) DEF_ATOMS, t_Bool}, {(void *) &do_bonds, (char *) "bonds", (char *) "Bonds", (char *) DEF_BONDS, t_Bool}, {(void *) &do_bbox, (char *) "bbox", (char *) "BBox", (char *) DEF_BBOX, t_Bool}, }; static OptionStruct desc[] = { {(char *) "-molecule", (char *) "set molecule"}, #ifdef STANDALONE {(char *) "-timeout", (char *) "set timeout"}, #endif {(char *) "-/+spin", (char *) "turn on/off spin"}, {(char *) "-/+wander", (char *) "turn on/off wander"}, {(char *) "-/+labels", (char *) "turn on/off labels"}, {(char *) "-/+titles", (char *) "turn on/off titles"}, {(char *) "-/+atoms", (char *) "turn on/off atoms"}, {(char *) "-/+bonds", (char *) "turn on/off bonds"}, {(char *) "-/+bbox", (char *) "turn on/off bbox"}, }; ModeSpecOpt molecule_opts = {countof(opts), opts, countof(vars), vars, desc}; #ifdef USE_MODULES ModStruct molecule_description = {"molecule", "init_molecule", "draw_molecule", "release_molecule", "draw_molecule", "init_molecule", (char *) NULL, &molecule_opts, 50000, 1, 20, 1, 64, 1.0, "", "Draws molecules", 0, NULL}; #endif static time_t last = 0; /* shapes */ static void sphere (GLfloat x, GLfloat y, GLfloat z, GLfloat diameter, Bool wire) { int stacks = (scale_down ? SPHERE_STACKS_2 : SPHERE_STACKS); int slices = (scale_down ? SPHERE_SLICES_2 : SPHERE_SLICES); glPushMatrix (); glTranslatef (x, y, z); glScalef (diameter, diameter, diameter); unit_sphere (stacks, slices, wire); glPopMatrix (); } static Bool load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP) { #ifdef STANDALONE const char *font = get_string_resource (res, "Font"); #else const char *font = res; #endif XFontStruct *f; Font id; int first, last; if (!font) font = "-*-times-bold-r-normal-*-180-*"; f = XLoadQueryFont(MI_DISPLAY(mi), font); if (!f) f = XLoadQueryFont(MI_DISPLAY(mi), "fixed"); id = f->fid; first = f->min_char_or_byte2; last = f->max_char_or_byte2; clear_gl_error (); *dlistP = glGenLists ((GLuint) last+1); if (check_gl_error ("glGenLists")) return False; /*- * PURIFY reports a cumulative memory leak on the next line with Mesa 3.4.1 * on Solaris 2.X and SunOS 4.1.X. This can be fixed with a patch to Mesa * 3.4.1. OpenGL on Solaris does not have the memory leak. */ glXUseXFont(id, first, last-first+1, *dlistP + first); if (check_gl_error ("glXUseXFont")) return False; *fontP = f; return True; } static Bool load_fonts (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; #ifdef STANDALONE if (!load_font (mi, (char *) "atomFont", &mc->xfont1, &mc->font1_dlist) return False; if (!load_font (mi, (char *) "titleFont", &mc->xfont2, &mc->font2_dlist) return False; #else if (!load_font (mi, (char *) "-*-times-bold-r-normal-*-240-*", &mc->xfont1, &mc->font1_dlist)) return False; if (!load_font (mi, (char *) "-*-times-bold-r-normal-*-180-*", &mc->xfont2, &mc->font2_dlist)) return False; #endif return True; } static void free_fonts (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; clear_gl_error (); if (glIsList(mc->molecule_dlist)) { glDeleteLists(mc->molecule_dlist, 1); (void) check_gl_error ("glDeleteLists"); mc->molecule_dlist = 0; } if (mc->xfont1) { int last; last = mc->xfont1->max_char_or_byte2; clear_gl_error (); if (glIsList(mc->font1_dlist)) { glDeleteLists (mc->font1_dlist,(GLuint) last+1); (void) check_gl_error ("glDeleteLists"); mc->font1_dlist = 0; } XFreeFont(MI_DISPLAY(mi),mc->xfont1); mc->xfont1 = (XFontStruct *) NULL; } if (mc->xfont2) { int last; last = mc->xfont2->max_char_or_byte2; clear_gl_error (); if (glIsList(mc->font2_dlist)) { glDeleteLists (mc->font2_dlist,(GLuint) last+1); (void) check_gl_error ("glDeleteLists"); mc->font2_dlist = 0; } XFreeFont(MI_DISPLAY(mi),mc->xfont2); mc->xfont2 = (XFontStruct *) NULL; } } static int string_width (XFontStruct *f, const char *c) { int w = 0; while (*c) { int cc = *((unsigned char *) c); w += (f->per_char ? f->per_char[cc-f->min_char_or_byte2].rbearing : f->min_bounds.rbearing); c++; } return w; } static atom_data * get_atom_data (const char *atom_name) { int i; atom_data *d = 0; char *n = (char *) strdup (atom_name); char *n2 = n; int L; while (!isalpha((int) *n)) n++; L = strlen(n); while (L > 0 && !isalpha((int) n[L-1])) n[--L] = 0; for (i = 0; i < (int) countof(all_atom_data); i++) { d = &all_atom_data[i]; if (!strcmp (n, all_atom_data[i].name)) break; } free(n2); return d; } static void set_atom_color (ModeInfo *mi, molecule_atom *a, Bool font_p) { atom_data *d; GLfloat *gl_color; if (a) d = a->data; else { static atom_data *def_data = 0; if (!def_data) def_data = get_atom_data ("bond"); d = def_data; } gl_color = (!font_p ? d->gl_color : (d->gl_color + 4)); if (gl_color[3] == 0) { const char *string = !font_p ? d->color : d->text_color; XColor xcolor; if (!XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), string, &xcolor)) { (void) fprintf (stderr, "molecule: unparsable color in %s: %s\n", (a ? a->label : d->name), string); /* exit (1); */ } gl_color[0] = xcolor.red / 65536.0; gl_color[1] = xcolor.green / 65536.0; gl_color[2] = xcolor.blue / 65536.0; gl_color[3] = 1.0; } if (font_p) glColor3f (gl_color[0], gl_color[1], gl_color[2]); else glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gl_color); } static GLfloat atom_size (molecule_atom *a) { if (do_bonds) { if (a->data->size2 == 0) { /* let the molecules have the same relative sizes, but scale them to a smaller range, so that the bond-tubes are actually visible... */ GLfloat bot = 0.4; GLfloat top = 0.6; GLfloat min = 1.17; GLfloat max = 1.80; GLfloat ratio = (a->data->size - min) / (max - min); a->data->size2 = bot + (ratio * (top - bot)); } return a->data->size2; } else return a->data->size; } static molecule_atom * get_atom (molecule_atom *atoms, int natoms, int id, Bool verbose) { int i; /* quick short-circuit */ if (id < natoms) { if (atoms[id].id == id) return &atoms[id]; if (id > 0 && atoms[id-1].id == id) return &atoms[id-1]; if (id < natoms-1 && atoms[id+1].id == id) return &atoms[id+1]; } for (i = 0; i < natoms; i++) if (id == atoms[i].id) return &atoms[i]; if (verbose) { (void) fprintf (stderr, "molecule: no atom %d\n", id); } return (molecule_atom *) NULL; } static void molecule_bounding_box (ModeInfo *mi, GLfloat *x1, GLfloat *y1, GLfloat *z1, GLfloat *x2, GLfloat *y2, GLfloat *z2) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; molecule *m = &mc->molecules[mc->which]; int i; if (m->natoms == 0) { *x1 = *y1 = *z1 = *x2 = *y2 = *z2 = 0; } else { *x1 = *x2 = m->atoms[0].x; *y1 = *y2 = m->atoms[0].y; *z1 = *z2 = m->atoms[0].z; } for (i = 1; i < m->natoms; i++) { if (m->atoms[i].x < *x1) *x1 = m->atoms[i].x; if (m->atoms[i].y < *y1) *y1 = m->atoms[i].y; if (m->atoms[i].z < *z1) *z1 = m->atoms[i].z; if (m->atoms[i].x > *x2) *x2 = m->atoms[i].x; if (m->atoms[i].y > *y2) *y2 = m->atoms[i].y; if (m->atoms[i].z > *z2) *z2 = m->atoms[i].z; } *x1 -= 1; *y1 -= 1; *z1 -= 1; *x2 += 1; *y2 += 1; *z2 += 1; } static void draw_bounding_box (ModeInfo *mi) { static GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 }; static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 }; int wire = cur_wire; GLfloat x1, y1, z1, x2, y2, z2; molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2); glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1); glFrontFace(GL_CCW); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(0, 1, 0); glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2); glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1); glEnd(); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(0, -1, 0); glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2); glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1); glEnd(); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(0, 0, 1); glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1); glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1); glEnd(); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(0, 0, -1); glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2); glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2); glEnd(); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(1, 0, 0); glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2); glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1); glEnd(); glBegin(wire ? GL_LINE_LOOP : GL_QUADS); glNormal3f(-1, 0, 0); glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2); glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1); glEnd(); glPushAttrib (GL_LIGHTING_BIT); glDisable (GL_LIGHTING); glColor3f (c2[0], c2[1], c2[2]); glBegin(GL_LINES); if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0; if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0; if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0; glVertex3f(x1, 0, 0); glVertex3f(x2, 0, 0); glVertex3f(0 , y1, 0); glVertex3f(0, y2, 0); glVertex3f(0, 0, z1); glVertex3f(0, 0, z2); glEnd(); glPopAttrib(); } /* Since PDB files don't always have the molecule centered around the origin, and since some molecules are pretty large, scale and/or translate so that the whole molecule is visible in the window. */ static void ensure_bounding_box_visible (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; GLfloat x1, y1, z1, x2, y2, z2; GLfloat w, h, d; GLfloat size; GLfloat max_size = 10; /* don't bother scaling down if the molecule is already smaller than this */ molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2); w = x2-x1; h = y2-y1; d = z2-z1; size = (w > h ? w : h); size = (size > d ? size : d); mc->molecule_size = size; scale_down = 0; if (size > max_size) { GLfloat scale = max_size / size; glScalef (scale, scale, scale); scale_down = scale < 0.3; } glTranslatef (-(x1 + w/2), -(y1 + h/2), -(z1 + d/2)); } static void print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y, GLfloat line_height) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; y -= line_height; glPushAttrib (GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT); glDisable (GL_LIGHTING); glDisable (GL_DEPTH_TEST); { glMatrixMode(GL_PROJECTION); glPushMatrix(); { glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); { int i; glLoadIdentity(); gluOrtho2D (0, MI_WIDTH(mi), 0, MI_HEIGHT(mi)); set_atom_color (mi, 0, True); glRasterPos2f (x, y); for (i = 0; i < (int) strlen(string); i++) { char c = string[i]; if (c == '\n') glRasterPos2f (x, (y -= line_height)); else glCallList (mc->font2_dlist + (int)(c)); } } glPopMatrix(); } glMatrixMode(GL_PROJECTION); glPopMatrix(); } glPopAttrib(); glMatrixMode(GL_MODELVIEW); } /* Constructs the GL shapes of the current molecule */ static void build_molecule (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; int wire = cur_wire; int i; molecule *m = &mc->molecules[mc->which]; if (wire) { glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); glDisable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_CULL_FACE); } if (!wire) set_atom_color (mi, 0, False); if (do_bonds) for (i = 0; i < m->nbonds; i++) { molecule_bond *b = &m->bonds[i]; molecule_atom *from = get_atom(m->atoms, m->natoms, b->from, MI_IS_VERBOSE(mi)); molecule_atom *to = get_atom(m->atoms, m->natoms, b->to, MI_IS_VERBOSE(mi)); if (wire) { glBegin(GL_LINES); glVertex3f(from->x, from->y, from->z); glVertex3f(to->x, to->y, to->z); glEnd(); } else { int faces = (scale_down ? TUBE_FACES_2 : TUBE_FACES); # ifdef SMOOTH_TUBE int smooth = True; # else int smooth = False; # endif GLfloat thickness = 0.07 * b->strength; GLfloat cap_size = 0.03; if (thickness > 0.3) thickness = 0.3; tube (from->x, from->y, from->z, to->x, to->y, to->z, thickness, cap_size, faces, smooth, wire); } } for (i = 0; i < m->natoms; i++) { molecule_atom *a = &m->atoms[i]; int i; if (!wire && do_atoms) { GLfloat size = atom_size (a); set_atom_color (mi, a, False); sphere (a->x, a->y, a->z, size, wire); } if (do_labels) { glPushAttrib (GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT); glDisable (GL_LIGHTING); glDisable (GL_DEPTH_TEST); if (!wire) set_atom_color (mi, a, True); glRasterPos3f (a->x, a->y, a->z); { GLdouble mm[17], pm[17]; GLint vp[5]; GLdouble wx=-1, wy=-1, wz=-1; glGetDoublev (GL_MODELVIEW_MATRIX, mm); glGetDoublev (GL_PROJECTION_MATRIX, pm); glGetIntegerv (GL_VIEWPORT, vp); /* Convert 3D coordinates to window coordinates */ gluProject (a->x, a->y, a->z, mm, pm, vp, &wx, &wy, &wz); /* Fudge the window coordinates to center the string */ wx -= string_width (mc->xfont1, a->label) / 2; wy -= mc->xfont1->descent; /* Convert new window coordinates back to 3D coordinates */ gluUnProject (wx, wy, wz, mm, pm, vp, &wx, &wy, &wz); glRasterPos3f (wx, wy, wz); } for (i = 0; i < (int) strlen(a->label); i++) glCallList (mc->font1_dlist + (int)(a->label[i])); glPopAttrib(); } } if (do_bbox) draw_bounding_box (mi); if (do_titles && m->label && *m->label) print_title_string (mi, m->label, 10, MI_HEIGHT(mi) - 10, mc->xfont2->ascent + mc->xfont2->descent); } /* loading */ static void push_atom (molecule *m, int id, const char *label, GLfloat x, GLfloat y, GLfloat z) { m->natoms++; if (m->atoms_size < m->natoms) { m->atoms_size += 20; m->atoms = (molecule_atom *) realloc (m->atoms, m->atoms_size * sizeof(*m->atoms)); } m->atoms[m->natoms-1].id = id; m->atoms[m->natoms-1].label = label; m->atoms[m->natoms-1].x = x; m->atoms[m->natoms-1].y = y; m->atoms[m->natoms-1].z = z; m->atoms[m->natoms-1].data = get_atom_data (label); } static void push_bond (molecule *m, int from, int to) { int i; for (i = 0; i < m->nbonds; i++) if ((m->bonds[i].from == from && m->bonds[i].to == to) || (m->bonds[i].to == from && m->bonds[i].from == to)) { m->bonds[i].strength++; return; } m->nbonds++; if (m->bonds_size < m->nbonds) { m->bonds_size += 20; m->bonds = (molecule_bond *) realloc (m->bonds, m->bonds_size * sizeof(*m->bonds)); } m->bonds[m->nbonds-1].from = from; m->bonds[m->nbonds-1].to = to; m->bonds[m->nbonds-1].strength = 1; } /* This function is crap. */ static void parse_pdb_data (molecule *m, const char *data, const char *filename, int line) { const char *s = data; char *ss; while (*s) { if ((!m->label || !*m->label) && (!strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6))) { char *name = (char *) calloc (1, 100); char *n2 = name; int L = strlen(s); if (L > 99) L = 99; (void) strncpy(n2, s, L); n2 += 7; while (isspace((int) *n2)) n2++; ss = strchr (n2, '\n'); if (ss) *ss = 0; ss = strchr (n2, '\r'); if (ss) *ss = 0; ss = n2 + strlen(n2)-1; while (isspace((int) *ss) && ss > n2) *ss-- = 0; if (strlen(n2) > 4 && !strcmp (n2 + strlen(n2) - 4, ".pdb")) n2[strlen(n2)-4] = 0; if (m->label) free((char *) m->label); m->label = (char *) strdup (n2); free(name); } else if (!strncmp (s, "TITLE ", 6) || !strncmp (s, "HEADER", 6) || !strncmp (s, "COMPND", 6) || !strncmp (s, "AUTHOR", 6) || !strncmp (s, "REVDAT", 6) || !strncmp (s, "SOURCE", 6) || !strncmp (s, "EXPDTA", 6) || !strncmp (s, "JRNL ", 6) || !strncmp (s, "REMARK", 6) || !strncmp (s, "SEQRES", 6) || !strncmp (s, "HET ", 6) || !strncmp (s, "FORMUL", 6) || !strncmp (s, "CRYST1", 6) || !strncmp (s, "ORIGX1", 6) || !strncmp (s, "ORIGX2", 6) || !strncmp (s, "ORIGX3", 6) || !strncmp (s, "SCALE1", 6) || !strncmp (s, "SCALE2", 6) || !strncmp (s, "SCALE3", 6) || !strncmp (s, "MASTER", 6) || !strncmp (s, "KEYWDS", 6) || !strncmp (s, "DBREF ", 6) || !strncmp (s, "HETNAM", 6) || !strncmp (s, "HETSYN", 6) || !strncmp (s, "HELIX ", 6) || !strncmp (s, "LINK ", 6) || !strncmp (s, "MTRIX1", 6) || !strncmp (s, "MTRIX2", 6) || !strncmp (s, "MTRIX3", 6) || !strncmp (s, "SHEET ", 6) || !strncmp (s, "CISPEP", 6) || !strncmp (s, "GENERATED BY", 12) || !strncmp (s, "TER ", 4) || !strncmp (s, "END ", 4) || !strncmp (s, "TER\n", 4) || !strncmp (s, "END\n", 4) || !strncmp (s, "\n", 1)) /* ignored. */ ; else if (!strncmp (s, "ATOM ", 7)) { int id; /* PURIFY reports a cumulative potential memory leak on the next line */ char *name = (char *) calloc (1, 4); GLfloat x = -999, y = -999, z = -999; (void) sscanf (s+7, " %d ", &id); (void) strncpy (name, s+12, 3); while (isspace((int) *name)) name++; ss = name + strlen(name)-1; while (isspace((int) *ss) && ss > name) *ss-- = 0; (void) sscanf (s + 32, " %f %f %f ", &x, &y, &z); /* (void) fprintf (stderr, "molecule: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n", filename, line, id, name, x, y, z); */ push_atom (m, id, name, x, y, z); } else if (!strncmp (s, "HETATM ", 7)) { int id; /* PURIFY reports a cumulative potential memory leak on the next line */ char *name = (char *) calloc (1, 4); GLfloat x = -999, y = -999, z = -999; (void) sscanf (s+7, " %d ", &id); (void) strncpy (name, s+12, 3); while (isspace((int) *name)) name++; ss = name + strlen(name)-1; while (isspace((int) *ss) && ss > name) *ss-- = 0; (void) sscanf (s + 30, " %f %f %f ", &x, &y, &z); /* (void) fprintf (stderr, "molecule: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n", filename, line, id, name, x, y, z); */ push_atom (m, id, name, x, y, z); } else if (!strncmp (s, "CONECT ", 7)) { int atoms[11]; int i = sscanf (s + 8, " %d %d %d %d %d %d %d %d %d %d %d %d ", &atoms[0], &atoms[1], &atoms[2], &atoms[3], &atoms[4], &atoms[5], &atoms[6], &atoms[7], &atoms[8], &atoms[9], &atoms[10], &atoms[11]); int j; for (j = 1; j < i; j++) if (atoms[j] > 0) { /* (void) fprintf (stderr, "molecule: %s: %d: bond: %d %d\n", filename, line, atoms[0], atoms[j]); */ push_bond (m, atoms[0], atoms[j]); } } else { char *s1 = (char *) strdup (s); for (ss = s1; *ss && *ss != '\n'; ss++) ; *ss = 0; (void) fprintf (stderr, "molecule: %s: %d: unrecognised line: %s\n", filename, line, s1); } while (*s && *s != '\n') s++; if (*s == '\n') s++; line++; } } static void parse_pdb_file (molecule *m, const char *name) { FILE *in; int buf_size = 40960; char *buf; int line = 1; in = fopen(name, "r"); if (!in) { char *buf = (char *) malloc(1024 + strlen(name)); (void) sprintf(buf, "molecule: error reading \"%s\"", name); perror(buf); /* exit (1); */ } buf = (char *) malloc (buf_size); while (fgets (buf, buf_size-1, in)) { char *s; for (s = buf; *s; s++) if (*s == '\r') *s = '\n'; parse_pdb_data (m, buf, name, line++); } free (buf); (void) fclose (in); if (!m->natoms) { (void) fprintf (stderr, "molecule: file %s contains no atomic coordinates!\n", name); /* exit (1); */ } if (!m->nbonds && do_bonds) { (void) fprintf (stderr, "molecule: warning: file %s contains no atomic bond info.\n", name); do_bonds = 0; } } static void generate_molecule_formula (molecule *m) { char *buf = (char *) malloc (m->natoms * 10); char *s = buf; int i; struct { char *atom; int count; } counts[200]; memset (counts, 0, sizeof(counts)); *s = 0; for (i = 0; i < m->natoms; i++) { int j = 0; char *a = (char *) m->atoms[i].label; char *e; while (!isalpha((int) *a)) a++; a = (char *) strdup (a); for (e = a; isalpha((int) *e); e++); *e = 0; while (counts[j].atom && !!strcmp(a, counts[j].atom)) j++; if (counts[j].atom) free(a); else counts[j].atom = a; counts[j].count++; } i = 0; while (counts[i].atom) { (void) strcat (s, counts[i].atom); free(counts[i].atom); s += strlen (s); if (counts[i].count > 1) (void) sprintf (s, "(%d)", counts[i].count); s += strlen (s); i++; } if (!m->label) m->label = (char *) strdup(""); s = (char *) malloc (strlen (m->label) + strlen (buf) + 2); (void) strcpy (s, m->label); (void) strcat (s, "\n"); (void) strcat (s, buf); free((char*) m->label); free (buf); m->label = s; } static void insert_vertical_whitespace (char *string) { while (*string) { if ((string[0] == ',' || string[0] == ';' || string[0] == ':') && string[1] == ' ') string[0] = ' ', string[1] = '\n'; string++; } } /* Construct the molecule data from either: the builtins; or from the (one) .pdb file specified with -molecule. */ static void load_molecules (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; int wire = cur_wire; if (!molecule_str || !*molecule_str || !strcmp(molecule_str, "(default)")) /* do the builtins */ { int i; mc->nmolecules = countof(builtin_pdb_data); if (!mols) { mols = (molecule *) calloc (sizeof (molecule), mc->nmolecules); for (i = 0; i < mc->nmolecules; i++) { char name[100]; (void) sprintf (name, "", i); parse_pdb_data (&mols[i], builtin_pdb_data[i], name, 1); generate_molecule_formula (&mols[i]); insert_vertical_whitespace ((char *) mols[i].label); } } mc->molecules = mols; } else /* Load a file */ { int i = 0; mc->nmolecules = 1; if (!mols) { mols = (molecule *) calloc (sizeof (molecule), mc->nmolecules); parse_pdb_file (&mols[i], molecule_str); generate_molecule_formula (&mols[i]); insert_vertical_whitespace ((char *) mols[i].label); if ((wire || !do_atoms) && !do_labels && mols[i].nbonds == 0) { /* If we're not drawing atoms (e.g., wireframe mode), and there is no bond info, then make sure labels are turned on, or we'll be looking at a black screen... */ (void) fprintf (stderr, "molecule: no bonds: turning -label on.\n"); do_labels = 1; } } mc->molecules = mols; } } /* Window management, etc */ void reshape_molecule (ModeInfo *mi, int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; glViewport (0, 0, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective( 30.0, 1/h, 1.0, 100.0 ); gluLookAt( 0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -15.0); glClear(GL_COLOR_BUFFER_BIT); } static void gl_init() { static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0}; glLightfv(GL_LIGHT0, GL_POSITION, pos); if (!not_first_gl) { orig_do_labels = do_labels; orig_do_bonds = do_bonds; orig_wire = cur_wire; orig_do_titles = do_titles; not_first_gl = True; } } /* lifted from lament.c */ #define RAND(n) ((long) ((LRAND() & 0x7fffffff) % ((long) (n)))) #define RANDSIGN() ((LRAND() & 1) ? 1 : -1) static void rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v, Bool verbose) { double ppos = *pos; /* tick position */ if (ppos < 0) ppos = -(ppos + *v); else ppos += *v; if (ppos > 1.0) ppos -= 1.0; else if (ppos < 0) ppos += 1.0; if ((ppos < 0.0) || (ppos > 1.0)) { if (verbose) { (void) fprintf(stderr, "Weirdness in rotate()\n"); (void) fprintf(stderr, "ppos = %g\n", ppos); } return; } *pos = (*pos > 0 ? ppos : -ppos); /* accelerate */ *v += *dv; /* clamp velocity */ if (*v > max_v || *v < -max_v) { *dv = -*dv; } /* If it stops, start it going in the other direction. */ else if (*v < 0) { if (random() % 4) { *v = 0; /* keep going in the same direction */ if (random() % 2) *dv = 0; else if (*dv < 0) *dv = -*dv; } else { /* reverse gears */ *v = -*v; *dv = -*dv; *pos = -*pos; } } /* Alter direction of rotational acceleration randomly. */ if (! (random() % 120)) *dv = -*dv; /* Change acceleration very occasionally. */ if (! (random() % 200)) { if (*dv == 0) *dv = 0.00001; else if (random() & 1) *dv *= 1.2; else *dv *= 0.8; } } static void startup_blurb (ModeInfo *mi) { molecule_configuration *mc = &mcs[MI_SCREEN(mi)]; const char *s = "Constructing molecules..."; print_title_string (mi, s, MI_WIDTH(mi) - (string_width (mc->xfont2, s) + 40), 10 + mc->xfont2->ascent + mc->xfont2->descent, mc->xfont2->ascent + mc->xfont2->descent); glFinish(); glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi)); firstcall = False; } void init_molecule (ModeInfo *mi) { molecule_configuration *mc; int wire; #ifndef STANDALONE timeout = MI_CYCLES(mi); #endif if (!mcs) { mcs = (molecule_configuration *) calloc (MI_NUM_SCREENS(mi), sizeof (molecule_configuration)); if (!mcs) { return; } } mc = &mcs[MI_SCREEN(mi)]; if (mc->glx_context) { /* Free font stuff */ free_fonts (mi); } if ((mc->glx_context = init_GL(mi)) != NULL) { glDrawBuffer(GL_BACK); gl_init(); last = 0; reshape_molecule (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } if (!load_fonts (mi)) { release_molecule(mi); return; } if (firstcall) startup_blurb (mi); cur_wire = MI_IS_WIREFRAME(mi); wire = cur_wire; mc->rotx = FLOATRAND(1.0) * RANDSIGN(); mc->roty = FLOATRAND(1.0) * RANDSIGN(); mc->rotz = FLOATRAND(1.0) * RANDSIGN(); /* bell curve from 0-6 degrees, avg 3 */ mc->dx = (FLOATRAND(0.1) + FLOATRAND(0.1) + FLOATRAND(0.1)) / (360/2); mc->dy = (FLOATRAND(0.1) + FLOATRAND(0.1) + FLOATRAND(0.1)) / (360/2); mc->dz = (FLOATRAND(0.1) + FLOATRAND(0.1) + FLOATRAND(0.1)) / (360/2); mc->d_max = mc->dx * 8; mc->ddx = 0.00006 + FLOATRAND(0.00003); mc->ddy = 0.00006 + FLOATRAND(0.00003); mc->ddz = 0.00006 + FLOATRAND(0.00003); { char *s = do_spin; while (*s) { if (*s == 'x' || *s == 'X') mc->spin_x = 1; else if (*s == 'y' || *s == 'Y') mc->spin_y = 1; else if (*s == 'z' || *s == 'Z') mc->spin_z = 1; else { (void) fprintf (stderr, "molecule: spin must contain only the characters X, Y, or Z (not \"%s\")\n", do_spin); /* exit (1); */ } s++; } } mc->molecule_dlist = glGenLists(1); load_molecules (mi); mc->which = NRAND(mc->nmolecules); #ifdef STANDALONE mc->no_label_threshold = get_float_resource ("noLabelThreshold", "NoLabelThreshold"); mc->wireframe_threshold = get_float_resource ("wireframeThreshold", "WireframeThreshold"); #else mc->no_label_threshold = 30; mc->wireframe_threshold = 150; #endif if (wire) do_bonds = 1; } void draw_molecule (ModeInfo *mi) { /* static time_t last = 0; */ time_t now = time ((time_t *) 0); molecule_configuration *mc; Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); if (mcs == NULL) return; mc = &mcs[MI_SCREEN(mi)]; if (!mc->glx_context) return; MI_IS_DRAWN(mi) = True; if (last + timeout <= now) /* randomize molecules every -timeout seconds */ { if (mc->nmolecules == 1) { if (last != 0) goto SKIP; mc->which = 0; } else if (last == 0) { mc->which = NRAND(mc->nmolecules); } else { int n = mc->which; while (n == mc->which) n = NRAND(mc->nmolecules); mc->which = n; } last = now; glNewList (mc->molecule_dlist, GL_COMPILE); ensure_bounding_box_visible (mi); if (MI_IS_ICONIC(mi)) {do_labels = False; do_bonds = True; do_titles = False; } else { do_labels = orig_do_labels; do_bonds = orig_do_bonds; do_titles = orig_do_titles; } cur_wire = orig_wire; if (mc->molecule_size > mc->no_label_threshold) do_labels = 0; if (mc->molecule_size > mc->wireframe_threshold) cur_wire = 1; if (cur_wire) do_bonds = 1; build_molecule (mi); glEndList(); } SKIP: glPushMatrix (); glScalef(1.1, 1.1, 1.1); { GLfloat x, y, z; if (do_wander) { static int frame = 0; # define SINOID(SCALE,SIZE) \ ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2) x = SINOID(0.031, 9.0); y = SINOID(0.023, 9.0); z = SINOID(0.017, 9.0); frame++; glTranslatef(x, y, z); } if (mc->spin_x || mc->spin_y || mc->spin_z) { x = mc->rotx; y = mc->roty; z = mc->rotz; if (x < 0) x = 1 - (x + 1); if (y < 0) y = 1 - (y + 1); if (z < 0) z = 1 - (z + 1); if (mc->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0); if (mc->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0); if (mc->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0); rotate(&mc->rotx, &mc->dx, &mc->ddx, mc->d_max, MI_IS_VERBOSE(mi)); rotate(&mc->roty, &mc->dy, &mc->ddy, mc->d_max, MI_IS_VERBOSE(mi)); rotate(&mc->rotz, &mc->dz, &mc->ddz, mc->d_max, MI_IS_VERBOSE(mi)); } } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCallList (mc->molecule_dlist); glPopMatrix (); if (MI_IS_FPS(mi)) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } void release_molecule(ModeInfo * mi) { if (mcs != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { molecule_configuration *mc = &mcs[screen]; #if 0 int i; for (i = 0; i < mc->nmolecules; i++) { molecule *m = &mc->molecules[i]; if (m->atoms) { free(m->atoms); m->atoms = (molecule_atom *) NULL; } if (m->bonds) { free(m->bonds); m->bonds = (molecule_bond *) NULL; } if (m->label) { free(m->label); m->label = (char *) NULL; } } if (mc->molecules) { free(mc->molecules); mc->molecules = (molecule *) NULL; } #endif if (mc->glx_context) { /* Display lists MUST be freed while their glXContext is current. */ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mc->glx_context)); /* Free font stuff */ free_fonts (mi); } } free(mcs); mcs = (molecule_configuration *) NULL; } FreeAllGL(mi); } #endif /* USE_GL */