1609 lines
42 KiB
C
1609 lines
42 KiB
C
/* -*- 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 <jwz@jwz.org>
|
|
* 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 <X11/Intrinsic.h>
|
|
#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 <ctype.h>
|
|
#include <GL/glu.h>
|
|
#include <time.h>
|
|
|
|
#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, "<builtin-%d>", 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 */
|