-/* molecule, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
+/* molecule, Copyright (c) 2001-2014 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 on the PDB file format:
- http://www.rcsb.org/pdb/docs/format/pdbguide2.2/guide2.2_frame.html
+ https://en.wikipedia.org/wiki/Protein_Data_Bank_%28file_format%29
+ http://www.wwpdb.org/docs.html
+ http://www.wwpdb.org/documentation/format32/v3.2.html
+ http://www.wwpdb.org/documentation/format32/sect9.html
+ http://www.rcsb.org/pdb/file_formats/pdb/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.
-
- GENERAL OPENGL NAIVETY:
-
- I don't understand the *right* way to place text in front of the
- atoms. What I'm doing now is close, but has glitches. I think I
- understand glPolygonOffset(), but I think it doesn't help me.
-
- Here's how I'd phrase the problem I'm trying to solve:
-
- - I have a bunch of spherical objects of various sizes
- - I want a piece of text in the scene, between each object
- and the observer
- - the position of this text should be apparently tangential
- to the surface of the sphere, so that:
- - it is never inside the sphere;
- - but can be occluded by other objects in the scene.
-
- So I was trying to use glPolygonOffset() to say "pretend all
- polygons are N units deeper than they actually are" where N was
- somewhere around the maximal radius of the objects. Which wasn't a
- perfect solution, but was close. But it turns out that can't work,
- because the second arg to glPolygonOffset() is multiplied by some
- minimal depth quantum which is not revealed, so I can't pass it an
- offset in scene units -- only in multiples of the quantum. So I
- don't know how many quanta in radius my spheres are.
-
- I think I need to position and render the text with glRasterPos3f()
- so that the text is influenced by the depth buffer. If I used 2f,
- or an explicit constant Z value, then the text would always be in
- front of each sphere, and text would be visible for spheres that
- were fully occluded, which isn't what I want.
-
- So my only guess at this point is that I need to position the text
- exactly where I want it, tangential to the spheres -- but that
- means I need to be able to compute that XYZ position, which is
- dependent on the position of the observer! Which means two things:
- first, while generating my scene, I need to take into account the
- position of the observer, and I don't have a clue how to do that;
- and second, it means I can't put my whole molecule in a display
- list, because the XYZ position of the text in the scene changes at
- every frame, as the molecule rotates.
-
- This just *can't* be as hard as it seems!
+ http://www.umass.edu/microbio/rasmol/whereget.htm
+ http://www.wwpdb.org/docs.html
*/
-#include <X11/Intrinsic.h>
-
-#define PROGCLASS "Molecule"
-#define HACK_INIT init_molecule
-#define HACK_DRAW draw_molecule
-#define HACK_RESHAPE reshape_molecule
-#define molecule_opts xlockmore_opts
-
-#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)"
-
#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" \
+ "*atomFont: -*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*\n" \
+ "*titleFont: -*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*\n" \
+ "*noLabelThreshold: 150 \n" \
"*wireframeThreshold: 150 \n" \
-
+# define refresh_molecule 0
+# define release_molecule 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
#include "colors.h"
#include "sphere.h"
#include "tube.h"
+#include "texfont.h"
+#include "rotator.h"
+#include "gltrackball.h"
#ifdef USE_GL /* whole file */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <ctype.h>
-#include <GL/glu.h>
-#define SPHERE_SLICES 16 /* how densely to render spheres */
-#define SPHERE_STACKS 10
+#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_ESHELLS "True"
+#define DEF_BBOX "False"
+#define DEF_SHELL_ALPHA "0.3"
+#define DEF_MOLECULE "(default)"
+#define DEF_VERBOSE "False"
+
+#define SPHERE_SLICES 48 /* how densely to render spheres */
+#define SPHERE_STACKS 24
#define SMOOTH_TUBE /* whether to have smooth or faceted tubes */
# define TUBE_FACES 8
#endif
-static int scale_down;
-#define SPHERE_SLICES_2 7
-#define SPHERE_STACKS_2 4
-#define TUBE_FACES_2 3
+#define SPHERE_SLICES_2 14
+#define SPHERE_STACKS_2 8
+#define TUBE_FACES_2 6
-const char * const builtin_pdb_data[] = {
+# ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than the length
+ ISO C89 compilers are required to support" when includng
+ the following data file... */
+# endif
+static const char * const builtin_pdb_data[] = {
# include "molecules.h"
};
+#ifndef USE_IPHONE
+# define LOAD_FILES
+#endif
+
+
typedef struct {
const char *name;
GLfloat size, size2;
/* 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", "Grey70", { 0, }},
- { "C", 1.75, 0, "Grey60", "White", { 0, }},
- { "N", 1.55, 0, "LightSteelBlue3", "SlateBlue1", { 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", "LightGreen", { 0, }}
+static const atom_data all_atom_data[] = {
+ { "H", 1.17, 0.40, "#FFFFFF", "#000000", { 0, }},
+ { "C", 1.75, 0.58, "#999999", "#FFFFFF", { 0, }},
+ { "CA", 1.80, 0.60, "#0000FF", "#ADD8E6", { 0, }},
+ { "N", 1.55, 0.52, "#A2B5CD", "#EE99FF", { 0, }},
+ { "O", 1.40, 0.47, "#FF0000", "#FFB6C1", { 0, }},
+ { "P", 1.28, 0.43, "#9370DB", "#DB7093", { 0, }},
+ { "S", 1.80, 0.60, "#8B8B00", "#FFFF00", { 0, }},
+ { "bond", 0, 0, "#B3B3B3", "#FFFF00", { 0, }},
+ { "*", 1.40, 0.47, "#008B00", "#90EE90", { 0, }}
};
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 */
+ const atom_data *data; /* computed: which style of atom this is */
} molecule_atom;
typedef struct {
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;
+ rotator *rot;
+ trackball_state *trackball;
+ Bool button_down_p;
GLfloat molecule_size; /* max dimension of molecule bounding box */
int nmolecules;
molecule *molecules;
+ int mode; /* 0 = normal, 1 = out, 2 = in */
+ int mode_tick;
+ int next; /* 0 = random, -1 = back, 1 = forward */
+
GLuint molecule_dlist;
+ GLuint shell_dlist;
+
+ texture_font_data *atom_font, *title_font;
+
+ int polygon_count;
- XFontStruct *xfont1, *xfont2;
- GLuint font1_dlist, font2_dlist;
+ time_t draw_time;
+ int draw_tick;
+
+ GLfloat overall_scale;
+ int low_rez_p;
} molecule_configuration;
static Bool do_labels;
static Bool do_atoms;
static Bool do_bonds;
+static Bool do_shells;
static Bool do_bbox;
+static Bool verbose_p;
+static GLfloat shell_alpha;
-static Bool orig_do_labels, orig_do_bonds, orig_wire; /* saved to reset */
+/* saved to reset */
+static Bool orig_do_labels, orig_do_atoms, orig_do_bonds, orig_do_shells,
+ orig_wire;
static XrmOptionDescRec opts[] = {
- { "-molecule", ".molecule", XrmoptionSepArg, 0 },
- { "-timeout",".timeout",XrmoptionSepArg, 0 },
- { "-spin", ".spin", XrmoptionSepArg, 0 },
- { "+spin", ".spin", XrmoptionNoArg, "False" },
- { "-wander", ".wander", XrmoptionNoArg, "True" },
- { "+wander", ".wander", XrmoptionNoArg, "False" },
- { "-labels", ".labels", XrmoptionNoArg, "True" },
- { "+labels", ".labels", XrmoptionNoArg, "False" },
- { "-titles", ".titles", XrmoptionNoArg, "True" },
- { "+titles", ".titles", XrmoptionNoArg, "False" },
- { "-atoms", ".atoms", XrmoptionNoArg, "True" },
- { "+atoms", ".atoms", XrmoptionNoArg, "False" },
- { "-bonds", ".bonds", XrmoptionNoArg, "True" },
- { "+bonds", ".bonds", XrmoptionNoArg, "False" },
- { "-bbox", ".bbox", XrmoptionNoArg, "True" },
- { "+bbox", ".bbox", XrmoptionNoArg, "False" },
+ { "-molecule", ".molecule", XrmoptionSepArg, 0 },
+ { "-timeout", ".timeout", XrmoptionSepArg, 0 },
+ { "-spin", ".spin", XrmoptionSepArg, 0 },
+ { "+spin", ".spin", XrmoptionNoArg, "" },
+ { "-wander", ".wander", XrmoptionNoArg, "True" },
+ { "+wander", ".wander", XrmoptionNoArg, "False" },
+ { "-labels", ".labels", XrmoptionNoArg, "True" },
+ { "+labels", ".labels", XrmoptionNoArg, "False" },
+ { "-titles", ".titles", XrmoptionNoArg, "True" },
+ { "+titles", ".titles", XrmoptionNoArg, "False" },
+ { "-atoms", ".atoms", XrmoptionNoArg, "True" },
+ { "+atoms", ".atoms", XrmoptionNoArg, "False" },
+ { "-bonds", ".bonds", XrmoptionNoArg, "True" },
+ { "+bonds", ".bonds", XrmoptionNoArg, "False" },
+ { "-shells", ".eshells", XrmoptionNoArg, "True" },
+ { "+shells", ".eshells", XrmoptionNoArg, "False" },
+ { "-shell-alpha", ".shellAlpha", XrmoptionSepArg, 0 },
+ { "-bbox", ".bbox", XrmoptionNoArg, "True" },
+ { "+bbox", ".bbox", XrmoptionNoArg, "False" },
+ { "-verbose", ".verbose", XrmoptionNoArg, "True" },
};
static argtype vars[] = {
- {(caddr_t *) &molecule_str, "molecule", "Molecule", DEF_MOLECULE,t_String},
- {(caddr_t *) &timeout, "timeout","Seconds",DEF_TIMEOUT,t_Int},
- {(caddr_t *) &do_spin, "spin", "Spin", DEF_SPIN, t_String},
- {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
- {(caddr_t *) &do_labels, "labels", "Labels", DEF_LABELS, t_Bool},
- {(caddr_t *) &do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
- {(caddr_t *) &do_atoms, "atoms", "Atoms", DEF_ATOMS, t_Bool},
- {(caddr_t *) &do_bonds, "bonds", "Bonds", DEF_BONDS, t_Bool},
- {(caddr_t *) &do_bbox, "bbox", "BBox", DEF_BBOX, t_Bool},
+ {&molecule_str, "molecule", "Molecule", DEF_MOLECULE, t_String},
+ {&timeout, "timeout", "Seconds", DEF_TIMEOUT, t_Int},
+ {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
+ {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
+ {&do_atoms, "atoms", "Atoms", DEF_ATOMS, t_Bool},
+ {&do_bonds, "bonds", "Bonds", DEF_BONDS, t_Bool},
+ {&do_shells, "eshells", "EShells", DEF_ESHELLS, t_Bool},
+ {&do_labels, "labels", "Labels", DEF_LABELS, t_Bool},
+ {&do_titles, "titles", "Titles", DEF_TITLES, t_Bool},
+ {&do_bbox, "bbox", "BBox", DEF_BBOX, t_Bool},
+ {&shell_alpha, "shellAlpha", "ShellAlpha", DEF_SHELL_ALPHA, t_Float},
+ {&verbose_p, "verbose", "Verbose", DEF_VERBOSE, t_Bool},
};
-ModeSpecOpt molecule_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt molecule_opts = {countof(opts), opts, countof(vars), vars, NULL};
\f
/* shapes */
-static void
-sphere (GLfloat x, GLfloat y, GLfloat z, GLfloat diameter, Bool wire)
+static int
+sphere (molecule_configuration *mc,
+ 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);
+ int stacks = (mc->low_rez_p ? SPHERE_STACKS_2 : SPHERE_STACKS);
+ int slices = (mc->low_rez_p ? SPHERE_SLICES_2 : SPHERE_SLICES);
glPushMatrix ();
glTranslatef (x, y, z);
glScalef (diameter, diameter, diameter);
unit_sphere (stacks, slices, wire);
glPopMatrix ();
-}
-
-
-static void
-load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
-{
- const char *font = get_string_resource (res, "Font");
- XFontStruct *f;
- Font id;
- int first, last;
-
- if (!font) font = "-*-times-bold-r-normal-*-180-*";
-
- f = XLoadQueryFont(mi->dpy, font);
- if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
- id = f->fid;
- first = f->min_char_or_byte2;
- last = f->max_char_or_byte2;
-
- clear_gl_error ();
- *dlistP = glGenLists ((GLuint) last+1);
- check_gl_error ("glGenLists");
- glXUseXFont(id, first, last-first+1, *dlistP + first);
- check_gl_error ("glXUseXFont");
-
- *fontP = f;
+ return stacks * slices;
}
load_fonts (ModeInfo *mi)
{
molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
- load_font (mi, "atomFont", &mc->xfont1, &mc->font1_dlist);
- load_font (mi, "titleFont", &mc->xfont2, &mc->font2_dlist);
+ mc->atom_font = load_texture_font (mi->dpy, "atomFont");
+ mc->title_font = load_texture_font (mi->dpy, "titleFont");
}
-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 *
+static const atom_data *
get_atom_data (const char *atom_name)
{
int i;
- atom_data *d = 0;
+ const atom_data *d = 0;
char *n = strdup (atom_name);
char *n2 = n;
int L;
for (i = 0; i < countof(all_atom_data); i++)
{
d = &all_atom_data[i];
- if (!strcmp (n, all_atom_data[i].name))
+ if (!strcasecmp (n, all_atom_data[i].name))
break;
}
static void
-set_atom_color (ModeInfo *mi, molecule_atom *a, Bool font_p)
+set_atom_color (ModeInfo *mi, const molecule_atom *a,
+ Bool font_p, GLfloat alpha)
{
- atom_data *d;
- GLfloat *gl_color;
+ const atom_data *d;
+ GLfloat gl_color[4];
if (a)
d = a->data;
else
+ d = get_atom_data ("bond");
+
+ if (font_p)
{
- static atom_data *def_data = 0;
- if (!def_data) def_data = get_atom_data ("bond");
- d = def_data;
+ gl_color[0] = d->gl_color[4];
+ gl_color[1] = d->gl_color[5];
+ gl_color[2] = d->gl_color[6];
+ gl_color[3] = d->gl_color[7];
+ }
+ else
+ {
+ gl_color[0] = d->gl_color[0];
+ gl_color[1] = d->gl_color[1];
+ gl_color[2] = d->gl_color[2];
+ gl_color[3] = d->gl_color[3];
}
-
- gl_color = (!font_p ? d->gl_color : (d->gl_color + 4));
if (gl_color[3] == 0)
{
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;
}
+ gl_color[3] = alpha;
+
+ /* If we're not drawing atoms, and the color is black, use white instead.
+ This is a kludge so that H can have black text over its white ball,
+ but the text still shows up if balls are off.
+ */
+ if (font_p && !do_atoms &&
+ gl_color[0] == 0 && gl_color[1] == 0 && gl_color[2] == 0)
+ {
+ gl_color[0] = gl_color[1] = gl_color[2] = 1;
+ }
+
if (font_p)
- glColor3f (gl_color[0], gl_color[1], gl_color[2]);
+ glColor4f (gl_color[0], gl_color[1], gl_color[2], gl_color[3]);
else
glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gl_color);
}
static GLfloat
-atom_size (molecule_atom *a)
+atom_size (const 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;
- }
+ return a->data->size2;
else
return a->data->size;
}
if (m->atoms[i].z > *z2) *z2 = m->atoms[i].z;
}
- *x1 -= 1;
- *y1 -= 1;
- *z1 -= 1;
- *x2 += 1;
- *y2 += 1;
- *z2 += 1;
+ *x1 -= 1.5;
+ *y1 -= 1.5;
+ *z1 -= 1.5;
+ *x2 += 1.5;
+ *y2 += 1.5;
+ *z2 += 1.5;
}
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 };
+ static const GLfloat c1[4] = { 0.2, 0.2, 0.4, 1.0 };
+ static const GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
int wire = MI_IS_WIREFRAME(mi);
GLfloat x1, y1, z1, x2, y2, z2;
molecule_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
glEnd();
- glPushAttrib (GL_LIGHTING);
glDisable (GL_LIGHTING);
glColor3f (c2[0], c2[1], c2[2]);
glVertex3f(0, 0, z1); glVertex3f(0, 0, z2);
glEnd();
- glPopAttrib();
+ if (!wire)
+ glEnable (GL_LIGHTING);
}
mc->molecule_size = size;
- scale_down = 0;
+ mc->low_rez_p = 0;
+ mc->overall_scale = 1;
if (size > max_size)
{
- GLfloat scale = max_size / size;
- glScalef (scale, scale, scale);
+ mc->overall_scale = max_size / size;
+ glScalef (mc->overall_scale, mc->overall_scale, mc->overall_scale);
- scale_down = scale < 0.3;
+ mc->low_rez_p = mc->overall_scale < 0.3;
}
glTranslatef (-(x1 + w/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_TRANSFORM_BIT | /* for matrix contents */
- GL_ENABLE_BIT); /* for various glDisable calls */
- glDisable (GL_LIGHTING);
- glDisable (GL_DEPTH_TEST);
- {
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- {
- glLoadIdentity();
-
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- {
- int i;
- glLoadIdentity();
-
- gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
-
- set_atom_color (mi, 0, True);
-
- glRasterPos2f (x, y);
- for (i = 0; i < 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)
+build_molecule (ModeInfo *mi, Bool transparent_p)
{
molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
int wire = MI_IS_WIREFRAME(mi);
int i;
+ GLfloat alpha = transparent_p ? shell_alpha : 1.0;
+ int polys = 0;
molecule *m = &mc->molecules[mc->which];
glEnable(GL_CULL_FACE);
}
-#if 0
- if (do_labels && !wire)
- {
- /* This is so all polygons are drawn slightly farther back in the depth
- buffer, so that when we render text directly on top of the spheres,
- it still shows up. */
- glEnable (GL_POLYGON_OFFSET_FILL);
- glPolygonOffset (1.0, (do_bonds ? 10.0 : 35.0));
- }
- else
- {
- glDisable (GL_POLYGON_OFFSET_FILL);
- }
-#endif
-
if (!wire)
- set_atom_color (mi, 0, False);
+ set_atom_color (mi, 0, False, alpha);
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);
- molecule_atom *to = get_atom (m->atoms, m->natoms, b->to);
+ const molecule_bond *b = &m->bonds[i];
+ const molecule_atom *from = get_atom (m->atoms, m->natoms, b->from);
+ const molecule_atom *to = get_atom (m->atoms, m->natoms, b->to);
if (wire)
{
glVertex3f(from->x, from->y, from->z);
glVertex3f(to->x, to->y, to->z);
glEnd();
+ polys++;
}
else
{
- int faces = (scale_down ? TUBE_FACES_2 : TUBE_FACES);
+ int faces = (mc->low_rez_p ? 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;
+ Bool cap_p = (!do_atoms || do_shells);
+ GLfloat base = 0.07;
+ GLfloat thickness = base * b->strength;
+ GLfloat cap_size = (cap_p ? base / 2 : 0);
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);
+ polys += tube (from->x, from->y, from->z,
+ to->x, to->y, to->z,
+ thickness, cap_size,
+ faces, smooth, cap_p, wire);
}
}
if (!wire && do_atoms)
for (i = 0; i < m->natoms; i++)
{
- molecule_atom *a = &m->atoms[i];
+ const molecule_atom *a = &m->atoms[i];
GLfloat size = atom_size (a);
- set_atom_color (mi, a, False);
- sphere (a->x, a->y, a->z, size, wire);
+ set_atom_color (mi, a, False, alpha);
+ polys += sphere (mc, a->x, a->y, a->z, size, wire);
}
- /* Second pass to draw labels, after all atoms and bonds are in place
- */
- if (do_labels)
- for (i = 0; i < m->natoms; i++)
- {
- molecule_atom *a = &m->atoms[i];
- int j;
-
- if (!wire)
- {
- glDisable (GL_LIGHTING);
-#if 1
- glDisable (GL_DEPTH_TEST);
-#endif
- }
-
- if (!wire)
- set_atom_color (mi, a, True);
-
- glRasterPos3f (a->x, a->y, a->z);
-
- /* Before drawing the string, shift the origin to center
- the text over the origin of the sphere. */
- glBitmap (0, 0, 0, 0,
- -string_width (mc->xfont1, a->label) / 2,
- -mc->xfont1->descent,
- NULL);
-
- for (j = 0; j < strlen(a->label); j++)
- glCallList (mc->font1_dlist + (int)(a->label[j]));
-
- /* More efficient to always call glEnable() with correct values
- than to call glPushAttrib()/glPopAttrib(), since reading
- attributes from GL does a round-trip and stalls the pipeline.
- */
- if (!wire)
- {
- glEnable(GL_LIGHTING);
-#if 1
- glEnable(GL_DEPTH_TEST);
-#endif
- }
- }
-
- if (do_bbox)
- draw_bounding_box (mi);
+ if (do_bbox && !transparent_p)
+ {
+ draw_bounding_box (mi);
+ polys += 4;
+ }
- if (do_titles && m->label && *m->label)
- print_title_string (mi, m->label,
- 10, mi->xgwa.height - 10,
- mc->xfont2->ascent + mc->xfont2->descent);
+ mc->polygon_count += polys;
}
}
+static void
+parse_error (const char *file, int lineno, const char *line)
+{
+ fprintf (stderr, "%s: %s: parse error, line %d: %s\n",
+ progname, file, lineno, line);
+ exit (1);
+}
+
/* This function is crap.
*/
!strncmp (s, "MTRIX3", 6) ||
!strncmp (s, "SHEET ", 6) ||
!strncmp (s, "CISPEP", 6) ||
+/*
+ !strncmp (s, "SEQADV", 6) ||
+ !strncmp (s, "SITE ", 5) ||
+ !strncmp (s, "FTNOTE", 6) ||
+ !strncmp (s, "MODEL ", 5) ||
+ !strncmp (s, "ENDMDL", 6) ||
+ !strncmp (s, "SPRSDE", 6) ||
+ !strncmp (s, "MODRES", 6) ||
+ */
!strncmp (s, "GENERATED BY", 12) ||
!strncmp (s, "TER ", 4) ||
!strncmp (s, "END ", 4) ||
else if (!strncmp (s, "ATOM ", 7))
{
int id;
+ const char *end = strchr (s, '\n');
+ int L = end - s;
char *name = (char *) calloc (1, 4);
GLfloat x = -999, y = -999, z = -999;
- sscanf (s+7, " %d ", &id);
+ if (1 != sscanf (s+7, " %d ", &id))
+ parse_error (filename, line, s);
+ /* Use the "atom name" field if that is all that is available. */
strncpy (name, s+12, 3);
+
+ /* But prefer the "element" field. */
+ if (L > 77 && !isspace(s[77])) {
+ /* fprintf(stderr, " \"%s\" -> ", name); */
+ name[0] = s[76];
+ name[1] = s[77];
+ name[2] = 0;
+ /* fprintf(stderr, "\"%s\"\n", name); */
+ }
+
while (isspace(*name)) name++;
ss = name + strlen(name)-1;
while (isspace(*ss) && ss > name)
*ss-- = 0;
- sscanf (s + 32, " %f %f %f ", &x, &y, &z);
+ ss = name + 1;
+ while(*ss)
+ {
+ *ss = tolower(*ss);
+ ss++;
+ }
+ if (3 != sscanf (s + 32, " %f %f %f ", &x, &y, &z))
+ parse_error (filename, line, s);
+
/*
fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
progname, filename, line,
char *name = (char *) calloc (1, 4);
GLfloat x = -999, y = -999, z = -999;
- sscanf (s+7, " %d ", &id);
+ if (1 != sscanf (s+7, " %d ", &id))
+ parse_error (filename, line, s);
strncpy (name, s+12, 3);
while (isspace(*name)) name++;
ss = name + strlen(name)-1;
while (isspace(*ss) && ss > name)
*ss-- = 0;
- sscanf (s + 30, " %f %f %f ", &x, &y, &z);
+ if (3 != sscanf (s + 30, " %f %f %f ", &x, &y, &z))
+ parse_error (filename, line, s);
/*
fprintf (stderr, "%s: %s: %d: atom: %d \"%s\" %9.4f %9.4f %9.4f\n",
progname, filename, line,
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 ",
+ 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]);
}
-static void
+#ifdef LOAD_FILES
+static int
parse_pdb_file (molecule *m, const char *name)
{
FILE *in;
char *buf = (char *) malloc(1024 + strlen(name));
sprintf(buf, "%s: error reading \"%s\"", progname, name);
perror(buf);
- exit (1);
+ return -1;
}
buf = (char *) malloc (buf_size);
{
fprintf (stderr, "%s: file %s contains no atomic coordinates!\n",
progname, name);
- exit (1);
+ return -1;
}
if (!m->nbonds && do_bonds)
progname, name);
do_bonds = 0;
}
+
+ return 0;
}
+#endif /* LOAD_FILES */
+
+typedef struct { char *atom; int count; } atom_and_count;
+
+/* When listing the components of a molecule, the convention is to put the
+ carbon atoms first, the hydrogen atoms second, and the other atom types
+ sorted alphabetically after that (although for some molecules, the usual
+ order is different: we special-case a few of those.)
+ */
+static int
+cmp_atoms (const void *aa, const void *bb)
+{
+ const atom_and_count *a = (atom_and_count *) aa;
+ const atom_and_count *b = (atom_and_count *) bb;
+ if (!a->atom) return 1;
+ if (!b->atom) return -1;
+ if (!strcmp(a->atom, "C")) return -1;
+ if (!strcmp(b->atom, "C")) return 1;
+ if (!strcmp(a->atom, "H")) return -1;
+ if (!strcmp(b->atom, "H")) return 1;
+ return strcmp (a->atom, b->atom);
+}
+
+static void special_case_formula (char *f);
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];
+ atom_and_count counts[200];
memset (counts, 0, sizeof(counts));
*s = 0;
for (i = 0; i < m->natoms; i++)
counts[j].count++;
}
+ i = 0;
+ while (counts[i].atom) i++;
+ qsort (counts, i, sizeof(*counts), cmp_atoms);
+
i = 0;
while (counts[i].atom)
{
free (counts[i].atom);
s += strlen (s);
if (counts[i].count > 1)
- sprintf (s, "(%d)", counts[i].count);
+ sprintf (s, "[%d]", counts[i].count); /* use [] to get subscripts */
s += strlen (s);
i++;
}
+ special_case_formula (buf);
+
if (!m->label) m->label = strdup("");
s = (char *) malloc (strlen (m->label) + strlen (buf) + 2);
strcpy (s, m->label);
m->label = s;
}
+/* thanks to Rene Uittenbogaard <ruittenb@wish.nl> */
+static void
+special_case_formula (char *f)
+{
+ if (!strcmp(f, "H[2]Be")) strcpy(f, "BeH[2]");
+ else if (!strcmp(f, "H[3]B")) strcpy(f, "BH[3]");
+ else if (!strcmp(f, "H[3]N")) strcpy(f, "NH[3]");
+ else if (!strcmp(f, "CHN")) strcpy(f, "HCN");
+ else if (!strcmp(f, "CKN")) strcpy(f, "KCN");
+ else if (!strcmp(f, "H[4]N[2]")) strcpy(f, "N[2]H[4]");
+ else if (!strcmp(f, "Cl[3]P")) strcpy(f, "PCl[3]");
+ else if (!strcmp(f, "Cl[5]P")) strcpy(f, "PCl[5]");
+}
+
+
static void
insert_vertical_whitespace (char *string)
{
load_molecules (ModeInfo *mi)
{
molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
+ int i;
- if (!molecule_str || !*molecule_str ||
- !strcmp(molecule_str, "(default)")) /* do the builtins */
+ mc->nmolecules = 0;
+# ifdef LOAD_FILES
+ if (molecule_str && *molecule_str &&
+ strcmp(molecule_str, "(default)")) /* try external PDB files */
+ {
+ /* The -molecule option can point to a .pdb file, or to
+ a directory of them.
+ */
+ int wire = MI_IS_WIREFRAME(mi);
+ struct stat st;
+ int nfiles = 0;
+ int list_size = 0;
+ char **files = 0;
+ int molecule_ctr;
+
+ if (!stat (molecule_str, &st) &&
+ S_ISDIR (st.st_mode))
+ {
+ char buf [255];
+ DIR *pdb_dir;
+ struct dirent *dentry;
+
+ pdb_dir = opendir (molecule_str);
+ if (! pdb_dir)
+ {
+ sprintf (buf, "%.100s: %.100s", progname, molecule_str);
+ perror (buf);
+ exit (1);
+ }
+
+ if (verbose_p)
+ fprintf (stderr, "%s: directory %s\n", progname, molecule_str);
+
+ nfiles = 0;
+ list_size = 100;
+ files = (char **) calloc (sizeof(*files), list_size);
+
+ while ((dentry = readdir (pdb_dir)))
+ {
+ int L = strlen (dentry->d_name);
+ if (L > 4 && !strcasecmp (dentry->d_name + L - 4, ".pdb"))
+ {
+ char *fn;
+ if (nfiles >= list_size-1)
+ {
+ list_size = (list_size + 10) * 1.2;
+ files = (char **)
+ realloc (files, list_size * sizeof(*files));
+ if (!files)
+ {
+ OOM:
+ fprintf (stderr, "%s: out of memory (%d files)\n",
+ progname, nfiles);
+ exit (1);
+ }
+ }
+
+ fn = (char *) malloc (strlen (molecule_str) + L + 10);
+ if (!fn) goto OOM;
+ strcpy (fn, molecule_str);
+ if (fn[strlen(fn)-1] != '/') strcat (fn, "/");
+ strcat (fn, dentry->d_name);
+ files[nfiles++] = fn;
+ if (verbose_p)
+ fprintf (stderr, "%s: file %s\n", progname, fn);
+ }
+ }
+ closedir (pdb_dir);
+
+ if (nfiles == 0)
+ fprintf (stderr, "%s: no .pdb files in directory %s\n",
+ progname, molecule_str);
+ }
+ else
+ {
+ files = (char **) malloc (sizeof (*files));
+ nfiles = 1;
+ files[0] = strdup (molecule_str);
+ if (verbose_p)
+ fprintf (stderr, "%s: file %s\n", progname, molecule_str);
+ }
+
+ mc->nmolecules = nfiles;
+ mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
+ molecule_ctr = 0;
+ for (i = 0; i < mc->nmolecules; i++)
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: reading %s\n", progname, files[i]);
+ if (!parse_pdb_file (&mc->molecules[molecule_ctr], files[i]))
+ {
+ if ((wire || !do_atoms) &&
+ !do_labels &&
+ mc->molecules[molecule_ctr].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... */
+ fprintf (stderr, "%s: %s: no bonds: turning -label on.\n",
+ progname, files[i]);
+ do_labels = 1;
+ }
+ free (files[i]);
+ files[i] = 0;
+ molecule_ctr++;
+ }
+ }
+
+ free (files);
+ files = 0;
+ mc->nmolecules = molecule_ctr;
+ }
+# endif /* LOAD_FILES */
+
+ if (mc->nmolecules == 0) /* do the builtins if no files */
{
- int i;
mc->nmolecules = countof(builtin_pdb_data);
mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
for (i = 0; i < mc->nmolecules; i++)
{
char name[100];
sprintf (name, "<builtin-%d>", i);
+ if (verbose_p) fprintf (stderr, "%s: reading %s\n", progname, name);
parse_pdb_data (&mc->molecules[i], builtin_pdb_data[i], name, 1);
- generate_molecule_formula (&mc->molecules[i]);
- insert_vertical_whitespace ((char *) mc->molecules[i].label);
}
}
- else /* Load a file */
+
+ for (i = 0; i < mc->nmolecules; i++)
{
- int i = 0;
- mc->nmolecules = 1;
- mc->molecules = (molecule *) calloc (sizeof (molecule), mc->nmolecules);
- parse_pdb_file (&mc->molecules[i], molecule_str);
generate_molecule_formula (&mc->molecules[i]);
insert_vertical_whitespace ((char *) mc->molecules[i].label);
-
- if ((wire || !do_atoms) &&
- !do_labels &&
- mc->molecules[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... */
- fprintf (stderr, "%s: no bonds: turning -label on.\n", progname);
- do_labels = 1;
- }
}
}
\f
/* Window management, etc
*/
-void
+ENTRYPOINT void
reshape_molecule (ModeInfo *mi, int width, int height)
{
GLfloat h = (GLfloat) height / (GLfloat) width;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
-
- gluPerspective( 30.0, 1/h, 20.0, 40.0 );
- gluLookAt( 0.0, 0.0, 15.0,
- 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0);
+ gluPerspective (30.0, 1/h, 20.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
- glTranslatef(0.0, 0.0, -15.0);
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
static void
gl_init (ModeInfo *mi)
{
- static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
+ static const GLfloat pos[4] = {1.0, 0.4, 0.9, 0.0};
+ static const GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
+ static const GLfloat dif[4] = {0.8, 0.8, 0.8, 1.0};
+ static const GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
-
- orig_do_labels = do_labels;
- orig_do_bonds = do_bonds;
- orig_wire = MI_IS_WIREFRAME(mi);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
}
-/* lifted from lament.c */
-#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
-#define RANDSIGN() ((random() & 1) ? 1 : -1)
-
static void
-rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
+startup_blurb (ModeInfo *mi)
{
- 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) abort();
- if (ppos > 1.0) abort();
- *pos = (*pos > 0 ? ppos : -ppos);
+ molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
+ const char *s = "Constructing molecules...";
+ print_texture_label (mi->dpy, mc->title_font,
+ mi->xgwa.width, mi->xgwa.height,
+ 0, s);
+ glFinish();
+ glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
+}
- /* accelerate */
- *v += *dv;
+ENTRYPOINT Bool
+molecule_handle_event (ModeInfo *mi, XEvent *event)
+{
+ molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
- /* 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 (gltrackball_event_handler (event, mc->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &mc->button_down_p))
+ return True;
+ else
{
- 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;
+ if (event->xany.type == KeyPress)
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == '<' || c == ',' || c == '-' || c == '_' ||
+ keysym == XK_Left || keysym == XK_Up || keysym == XK_Prior)
+ {
+ mc->next = -1;
+ goto SWITCH;
+ }
+ else if (c == '>' || c == '.' || c == '=' || c == '+' ||
+ keysym == XK_Right || keysym == XK_Down ||
+ keysym == XK_Next)
+ {
+ mc->next = 1;
+ goto SWITCH;
+ }
+ }
- /* Change acceleration very occasionally. */
- if (! (random() % 200))
- {
- if (*dv == 0)
- *dv = 0.00001;
- else if (random() & 1)
- *dv *= 1.2;
- else
- *dv *= 0.8;
+ if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
+ {
+ SWITCH:
+ mc->mode = 1;
+ mc->mode_tick = 4;
+ return True;
+ }
}
-}
-
-static void
-startup_blurb (ModeInfo *mi)
-{
- molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
- const char *s = "Constructing molecules...";
- print_title_string (mi, s,
- mi->xgwa.width - (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));
+ return False;
}
-void
+
+ENTRYPOINT void
init_molecule (ModeInfo *mi)
{
molecule_configuration *mc;
wire = MI_IS_WIREFRAME(mi);
- mc->rotx = frand(1.0) * RANDSIGN();
- mc->roty = frand(1.0) * RANDSIGN();
- mc->rotz = frand(1.0) * RANDSIGN();
-
- /* bell curve from 0-6 degrees, avg 3 */
- mc->dx = (frand(1) + frand(1) + frand(1)) / (360/2);
- mc->dy = (frand(1) + frand(1) + frand(1)) / (360/2);
- mc->dz = (frand(1) + frand(1) + frand(1)) / (360/2);
-
- mc->d_max = mc->dx * 2;
-
- mc->ddx = 0.00006 + frand(0.00003);
- mc->ddy = 0.00006 + frand(0.00003);
- mc->ddz = 0.00006 + frand(0.00003);
-
{
+ Bool spinx=False, spiny=False, spinz=False;
+ double spin_speed = 0.5;
+ double spin_accel = 0.3;
+ double wander_speed = 0.01;
+
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;
+ if (*s == 'x' || *s == 'X') spinx = True;
+ else if (*s == 'y' || *s == 'Y') spiny = True;
+ else if (*s == 'z' || *s == 'Z') spinz = True;
+ else if (*s == '0') ;
else
{
fprintf (stderr,
}
s++;
}
+
+ mc->rot = make_rotator (spinx ? spin_speed : 0,
+ spiny ? spin_speed : 0,
+ spinz ? spin_speed : 0,
+ spin_accel,
+ do_wander ? wander_speed : 0,
+ (spinx && spiny && spinz));
+ mc->trackball = gltrackball_init (True);
}
+ orig_do_labels = do_labels;
+ orig_do_atoms = do_atoms;
+ orig_do_bonds = do_bonds;
+ orig_do_shells = do_shells;
+ orig_wire = MI_IS_WIREFRAME(mi);
+
mc->molecule_dlist = glGenLists(1);
+ if (do_shells)
+ mc->shell_dlist = glGenLists(1);
load_molecules (mi);
mc->which = random() % mc->nmolecules;
- mc->no_label_threshold = get_float_resource ("noLabelThreshold",
+ mc->no_label_threshold = get_float_resource (mi->dpy, "noLabelThreshold",
"NoLabelThreshold");
- mc->wireframe_threshold = get_float_resource ("wireframeThreshold",
+ mc->wireframe_threshold = get_float_resource (mi->dpy, "wireframeThreshold",
"WireframeThreshold");
+ mc->mode = 0;
if (wire)
do_bonds = 1;
}
-void
+/* Put the labels on the atoms.
+ This can't be a part of the display list because of the games
+ we play with the translation matrix.
+ */
+static void
+draw_labels (ModeInfo *mi)
+{
+ molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ molecule *m = &mc->molecules[mc->which];
+ int i;
+
+ if (!do_labels)
+ return;
+
+ for (i = 0; i < m->natoms; i++)
+ {
+ molecule_atom *a = &m->atoms[i];
+ GLfloat size = atom_size (a);
+ GLfloat m[4][4];
+
+ glPushMatrix();
+
+ if (!wire)
+ set_atom_color (mi, a, True, 1);
+
+ /* First, we translate the origin to the center of the atom.
+
+ Then we retrieve the prevailing modelview matrix, which
+ includes any rotation, wandering, and user-trackball-rolling
+ of the scene.
+
+ We set the top 3x3 cells of that matrix to be the identity
+ matrix. This removes all rotation from the matrix, while
+ leaving the translation alone. This has the effect of
+ leaving the prevailing coordinate system perpendicular to
+ the camera view: were we to draw a square face, it would
+ be in the plane of the screen.
+
+ Now we translate by `size' toward the viewer -- so that the
+ origin is *just in front* of the ball.
+
+ Then we draw the label text, allowing the depth buffer to
+ do its work: that way, labels on atoms will be occluded
+ properly when other atoms move in front of them.
+
+ This technique (of neutralizing rotation relative to the
+ observer, after both rotations and translations have been
+ applied) is known as "billboarding".
+ */
+
+ glTranslatef(a->x, a->y, a->z); /* get matrix */
+ glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
+ m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
+ m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
+ m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
+ glLoadIdentity(); /* reset modelview */
+ glMultMatrixf (&m[0][0]); /* replace with ours */
+
+ glTranslatef (0, 0, (size * 1.1)); /* move toward camera */
+
+ glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
+
+ {
+ XCharStruct e;
+ int w, h;
+ texture_string_metrics (mc->atom_font, a->label, &e, 0, 0);
+ w = e.width;
+ h = e.ascent + e.descent;
+
+ GLfloat s = 1.0 / h; /* Scale to unit */
+ s *= mc->overall_scale; /* Scale to size of atom */
+ s *= 0.8; /* Shrink a bit */
+ glScalef (s, s, 1);
+ glTranslatef (-w/2, -h/2, 0);
+ print_texture_string (mc->atom_font, a->label);
+ }
+
+ glPopMatrix();
+ }
+}
+
+
+static void
+pick_new_molecule (ModeInfo *mi, time_t last)
+{
+ molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
+
+ if (mc->nmolecules == 1)
+ {
+ if (last != 0) return;
+ mc->which = 0;
+ }
+ else if (last == 0)
+ {
+ mc->which = random() % mc->nmolecules;
+ }
+ else if (mc->next < 0)
+ {
+ mc->which--;
+ if (mc->which < 0) mc->which = mc->nmolecules-1;
+ mc->next = 0;
+ }
+ else if (mc->next > 0)
+ {
+ mc->which++;
+ if (mc->which >= mc->nmolecules) mc->which = 0;
+ mc->next = 0;
+ }
+ else
+ {
+ int n = mc->which;
+ while (n == mc->which)
+ n = random() % mc->nmolecules;
+ mc->which = n;
+ }
+
+ if (verbose_p)
+ {
+ char *name = strdup (mc->molecules[mc->which].label);
+ char *s = strpbrk (name, "\r\n");
+ if (s) *s = 0;
+ fprintf (stderr, "%s: drawing %s (%d)\n", progname, name, mc->which);
+ free (name);
+ }
+
+ mc->polygon_count = 0;
+
+ glNewList (mc->molecule_dlist, GL_COMPILE);
+ ensure_bounding_box_visible (mi);
+
+ do_labels = orig_do_labels;
+ do_atoms = orig_do_atoms;
+ do_bonds = orig_do_bonds;
+ do_shells = orig_do_shells;
+ MI_IS_WIREFRAME(mi) = orig_wire;
+
+ if (mc->molecule_size > mc->no_label_threshold)
+ do_labels = 0;
+ if (mc->molecule_size > mc->wireframe_threshold)
+ MI_IS_WIREFRAME(mi) = 1;
+
+ if (MI_IS_WIREFRAME(mi))
+ do_bonds = 1, do_shells = 0;
+
+ if (!do_bonds)
+ do_shells = 0;
+
+ if (! (do_bonds || do_atoms || do_labels))
+ {
+ /* Make sure *something* shows up! */
+ MI_IS_WIREFRAME(mi) = 1;
+ do_bonds = 1;
+ }
+
+ build_molecule (mi, False);
+ glEndList();
+
+ if (do_shells)
+ {
+ glNewList (mc->shell_dlist, GL_COMPILE);
+ ensure_bounding_box_visible (mi);
+
+ do_labels = 0;
+ do_atoms = 1;
+ do_bonds = 0;
+
+ build_molecule (mi, True);
+
+ glEndList();
+ do_bonds = orig_do_bonds;
+ do_atoms = orig_do_atoms;
+ do_labels = orig_do_labels;
+ }
+}
+
+
+ENTRYPOINT void
draw_molecule (ModeInfo *mi)
{
- static time_t last = 0;
time_t now = time ((time_t *) 0);
+ GLfloat speed = 4.0; /* speed at which the zoom out/in happens */
molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
Display *dpy = MI_DISPLAY(mi);
if (!mc->glx_context)
return;
- if (last + timeout <= now) /* randomize molecules every -timeout seconds */
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mc->glx_context));
+
+ if (mc->draw_time == 0)
{
- if (mc->nmolecules == 1)
- {
- if (last != 0) goto SKIP;
- mc->which = 0;
- }
- else if (last == 0)
+ pick_new_molecule (mi, mc->draw_time);
+ mc->draw_time = now;
+ }
+ else if (mc->mode == 0)
+ {
+ if (mc->draw_tick++ > 10)
{
- mc->which = random() % mc->nmolecules;
+ time_t now = time((time_t *) 0);
+ if (mc->draw_time == 0) mc->draw_time = now;
+ mc->draw_tick = 0;
+
+ if (!mc->button_down_p &&
+ mc->nmolecules > 1 &&
+ mc->draw_time + timeout <= now)
+ {
+ /* randomize molecules every -timeout seconds */
+ mc->mode = 1; /* go out */
+ mc->mode_tick = 80 / speed;
+ mc->draw_time = now;
+ }
}
- else
+ }
+ else if (mc->mode == 1) /* out */
+ {
+ if (--mc->mode_tick <= 0)
{
- int n = mc->which;
- while (n == mc->which)
- n = random() % mc->nmolecules;
- mc->which = n;
+ mc->mode_tick = 80 / speed;
+ mc->mode = 2; /* go in */
+ pick_new_molecule (mi, mc->draw_time);
}
+ }
+ else if (mc->mode == 2) /* in */
+ {
+ if (--mc->mode_tick <= 0)
+ mc->mode = 0; /* normal */
+ }
+ else
+ abort();
- last = now;
-
-
- glNewList (mc->molecule_dlist, GL_COMPILE);
- ensure_bounding_box_visible (mi);
-
- do_labels = orig_do_labels;
- do_bonds = orig_do_bonds;
- MI_IS_WIREFRAME(mi) = orig_wire;
+ glPushMatrix ();
+ glScalef(1.1, 1.1, 1.1);
- if (mc->molecule_size > mc->no_label_threshold)
- do_labels = 0;
- if (mc->molecule_size > mc->wireframe_threshold)
- MI_IS_WIREFRAME(mi) = 1;
+ {
+ double x, y, z;
+ get_position (mc->rot, &x, &y, &z, !mc->button_down_p);
+ glTranslatef((x - 0.5) * 9,
+ (y - 0.5) * 9,
+ (z - 0.5) * 9);
+
+ gltrackball_rotate (mc->trackball);
+
+ get_rotation (mc->rot, &x, &y, &z, !mc->button_down_p);
+ glRotatef (x * 360, 1.0, 0.0, 0.0);
+ glRotatef (y * 360, 0.0, 1.0, 0.0);
+ glRotatef (z * 360, 0.0, 0.0, 1.0);
+ }
- if (MI_IS_WIREFRAME(mi))
- do_bonds = 1;
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- build_molecule (mi);
- glEndList();
+ if (mc->mode != 0)
+ {
+ GLfloat s = (mc->mode == 1
+ ? mc->mode_tick / (80 / speed)
+ : ((80 / speed) - mc->mode_tick + 1) / (80 / speed));
+ glScalef (s, s, s);
}
- SKIP:
- glPushMatrix ();
- glScalef(1.1, 1.1, 1.1);
+ glPushMatrix();
+ glCallList (mc->molecule_dlist);
- {
- GLfloat x, y, z;
+ if (mc->mode == 0)
+ {
+ molecule *m = &mc->molecules[mc->which];
- if (do_wander)
- {
- static int frame = 0;
+ draw_labels (mi);
-# define SINOID(SCALE,SIZE) \
- ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
+ /* This can't go in the display list, or the characters are spaced
+ wrongly when the window is resized. */
+ if (do_titles && m->label && *m->label)
+ {
+ set_atom_color (mi, 0, True, 1);
+ print_texture_label (mi->dpy, mc->title_font,
+ mi->xgwa.width, mi->xgwa.height,
+ 1, m->label);
+ }
+ }
+ glPopMatrix();
- x = SINOID(0.031, 9.0);
- y = SINOID(0.023, 9.0);
- z = SINOID(0.017, 9.0);
- frame++;
- glTranslatef(x, y, z);
- }
+ if (do_shells)
+ {
+ glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glPushMatrix();
+ glCallList (mc->shell_dlist);
+ glPopMatrix();
+ glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- 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);
- rotate(&mc->roty, &mc->dy, &mc->ddy, mc->d_max);
- rotate(&mc->rotz, &mc->dz, &mc->ddz, mc->d_max);
- }
- }
+ glDepthFunc (GL_EQUAL);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glPushMatrix();
+ glCallList (mc->shell_dlist);
+ glPopMatrix();
+ glDepthFunc (GL_LESS);
+ glDisable (GL_BLEND);
+ }
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glCallList (mc->molecule_dlist);
glPopMatrix ();
+ mi->polygon_count = mc->polygon_count;
+
if (mi->fps_p) do_fps (mi);
glFinish();
glXSwapBuffers(dpy, window);
}
+XSCREENSAVER_MODULE ("Molecule", molecule)
+
#endif /* USE_GL */