From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / molecule.c
index 62929647ad7511b34f07e81c983a576b93c41978..b3998f2d8d285e909142b4dbd9f396ad5c5c3410 100644 (file)
@@ -1,4 +1,4 @@
-/* molecule, Copyright (c) 2001-2006 Jamie Zawinski <jwz@jwz.org>
+/* molecule, Copyright (c) 2001-2012 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://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
+   http://www.umass.edu/microbio/rasmol/whereget.htm
+   http://www.wwpdb.org/docs.html
  */
 
+#ifdef HAVE_GLBITMAP
+# define ATOM_FONT "-*-helvetica-medium-r-normal-*-180-*"
+#else
+# define ATOM_FONT "-*-helvetica-medium-r-normal-*-240-*"
+#endif
+
 #define DEFAULTS       "*delay:        10000         \n" \
                        "*showFPS:      False         \n" \
                        "*wireframe:    False         \n" \
-                       "*atomFont:   -*-times-bold-r-normal-*-240-*\n" \
-                       "*titleFont:  -*-times-bold-r-normal-*-180-*\n" \
+                       "*atomFont:   " ATOM_FONT "\n" \
+                       "*atomFont2:  -*-helvetica-bold-r-normal-*-80-*\n" \
+                       "*titleFont:  -*-helvetica-medium-r-normal-*-180-*\n" \
                        "*noLabelThreshold:    30     \n" \
                        "*wireframeThreshold:  150    \n" \
 
 #define DEF_TITLES      "True"
 #define DEF_ATOMS       "True"
 #define DEF_BONDS       "True"
-#define DEF_SHELLS      "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 24  /* how densely to render spheres */
-#define SPHERE_STACKS 12
+#define SPHERE_SLICES 48  /* how densely to render spheres */
+#define SPHERE_STACKS 24
 
 #define SMOOTH_TUBE       /* whether to have smooth or faceted tubes */
 
@@ -70,9 +83,9 @@
 # define TUBE_FACES  8
 #endif
 
-#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
 
 
 # ifdef __GNUC__
@@ -98,10 +111,10 @@ typedef struct {
    and their approximate size in angstroms.
  */
 static const atom_data all_atom_data[] = {
-  { "H",    1.17,  0.40, "#FFFFFF", "#B3B3B3", { 0, }},
+  { "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", "#836FFF", { 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, }},
@@ -153,8 +166,14 @@ typedef struct {
   GLuint molecule_dlist;
   GLuint shell_dlist;
 
-  XFontStruct *xfont1, *xfont2;
-  GLuint font1_dlist, font2_dlist;
+# ifdef HAVE_GLBITMAP
+  XFontStruct *xfont1, *xfont2, *xfont3;
+  GLuint font1_dlist, font2_dlist, font3_dlist;
+# else
+  texture_font_data *font1_data, *font2_data, *font3_data;
+# endif
+
+
   int polygon_count;
 
   time_t draw_time;
@@ -215,7 +234,7 @@ static argtype vars[] = {
   {&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_SHELLS,   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},
@@ -251,8 +270,15 @@ static void
 load_fonts (ModeInfo *mi)
 {
   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
+# ifdef HAVE_GLBITMAP
   load_font (mi->dpy, "atomFont",  &mc->xfont1, &mc->font1_dlist);
-  load_font (mi->dpy, "titleFont", &mc->xfont2, &mc->font2_dlist);
+  load_font (mi->dpy, "atomFont2", &mc->xfont2, &mc->font2_dlist);
+  load_font (mi->dpy, "titleFont", &mc->xfont3, &mc->font3_dlist);
+# else
+  mc->font1_data = load_texture_font (mi->dpy, "atomFont");
+  mc->font2_data = load_texture_font (mi->dpy, "atomFont2");
+  mc->font3_data = load_texture_font (mi->dpy, "titleFont");
+# endif
 }
 
 
@@ -327,6 +353,16 @@ set_atom_color (ModeInfo *mi, const molecule_atom *a,
   
   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)
     glColor4f (gl_color[0], gl_color[1], gl_color[2], gl_color[3]);
   else
@@ -452,7 +488,6 @@ draw_bounding_box (ModeInfo *mi)
   glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
   glEnd();
 
-  glPushAttrib (GL_LIGHTING);
   glDisable (GL_LIGHTING);
 
   glColor3f (c2[0], c2[1], c2[2]);
@@ -465,7 +500,8 @@ draw_bounding_box (ModeInfo *mi)
   glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
   glEnd();
 
-  glPopAttrib();
+  if (!wire)
+    glEnable (GL_LIGHTING);
 }
 
 
@@ -574,11 +610,10 @@ build_molecule (ModeInfo *mi, Bool transparent_p)
             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, (!do_atoms || do_shells), wire);
-            polys += faces;
+            polys += tube (from->x, from->y, from->z,
+                           to->x,   to->y,   to->z,
+                           thickness, cap_size,
+                           faces, smooth, (!do_atoms || do_shells), wire);
           }
       }
 
@@ -749,13 +784,26 @@ parse_pdb_data (molecule *m, const char *data, const char *filename, int line)
       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;
 
           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)
@@ -1177,10 +1225,15 @@ startup_blurb (ModeInfo *mi)
 {
   molecule_configuration *mc = &mcs[MI_SCREEN(mi)];
   const char *s = "Constructing molecules...";
-  print_gl_string (mi->dpy, mc->xfont2, mc->font2_dlist,
+  print_gl_string (mi->dpy,
+# ifdef HAVE_GLBITMAP
+                   mc->xfont3, mc->font3_dlist,
+# else
+                   mc->font3_data,
+# endif
                    mi->xgwa.width, mi->xgwa.height,
                    10, mi->xgwa.height - 10,
-                   s);
+                   s, False);
   glFinish();
   glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
 }
@@ -1207,7 +1260,9 @@ molecule_handle_event (ModeInfo *mi, XEvent *event)
     }
   else if (event->xany.type == ButtonPress &&
            (event->xbutton.button == Button4 ||
-            event->xbutton.button == Button5))
+            event->xbutton.button == Button5 ||
+            event->xbutton.button == Button6 ||
+            event->xbutton.button == Button7))
     {
       gltrackball_mousewheel (mc->trackball, event->xbutton.button, 10,
                               !!event->xbutton.state);
@@ -1333,7 +1388,13 @@ 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, j;
+# ifdef HAVE_GLBITMAP
+  XFontStruct *xfont = (mc->scale_down ? mc->xfont2 : mc->xfont1);
+  GLuint font_dlist  = (mc->scale_down ? mc->font2_dlist : mc->font1_dlist);
+# else
+  texture_font_data *font_data = mc->font1_data;  /* don't scale down */
+# endif
+  int i;
 
   if (!do_labels)
     return;
@@ -1387,18 +1448,39 @@ draw_labels (ModeInfo *mi)
 
       glTranslatef (0, 0, (size * 1.1));           /* move toward camera */
 
+# ifdef HAVE_GLBITMAP
       glRasterPos3f (0, 0, 0);                     /* draw text here */
 
       /* 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,
+                -string_width (xfont, a->label, 0) / 2,
+                -xfont->descent,
                 NULL);
-
-      for (j = 0; j < strlen(a->label); j++)
-
-        glCallList (mc->font1_dlist + (int)(a->label[j]));
+      {
+        int j;
+        for (j = 0; j < strlen(a->label); j++)
+          glCallList (font_dlist + (int)(a->label[j]));
+      }
+# else
+      {
+        int h;
+        int w = texture_string_width (font_data, a->label, &h);
+        GLfloat s = 1.0 / h;
+        GLfloat max = 18;   /* max point size to avoid pixellated text */
+        if (h > max) s *= max/h;
+        glScalef (s, s, 1);
+        glTranslatef (-w/2, h*2/3, 0);
+        print_gl_string (mi->dpy,
+# ifdef HAVE_GLBITMAP
+                         xfont, font_dlist,
+# else
+                         font_data,
+# endif
+                         0, 0, 0, 0,
+                         a->label, False);
+      }
+# endif
 
       glPopMatrix();
     }
@@ -1593,10 +1675,15 @@ draw_molecule (ModeInfo *mi)
       if (do_titles && m->label && *m->label)
         {
           set_atom_color (mi, 0, True, 1);
-          print_gl_string (mi->dpy, mc->xfont2, mc->font2_dlist,
+          print_gl_string (mi->dpy,
+# ifdef HAVE_GLBITMAP
+                           mc->xfont3, mc->font3_dlist,
+# else
+                           mc->font3_data,
+# endif
                            mi->xgwa.width, mi->xgwa.height,
                            10, mi->xgwa.height - 10,
-                           m->label);
+                           m->label, False);
         }
     }
   glPopMatrix();