/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                                 D R A W . C                                  *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: draw.c,v 1.7 2004/08/29 14:49:57 jrh Exp $
* $Log: draw.c,v $
* Revision 1.7  2004/08/29 14:49:57  jrh
* Release 2.4.1
*
* Revision 1.6  2003/11/07 10:59:34  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:04:57  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:25:10  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:36:48  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:27  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:40:22  jrh
* Initial revision
*
*/
#include<limits.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include<X11/Xlib.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/keysym.h>
#include<Xm/Xm.h>
#include<GL/gl.h>
#include<GL/glu.h>
#include "viewmol.h"

int renderMolecule(struct MOLECULE *, int, int);
void wireModel(struct MOLECULE *, Dimension, int);
void stickModel(struct MOLECULE *, int);
void balls(struct MOLECULE *, double, int);
void setAtomProperties(struct ELEMENT *, int);
void drawIntern(struct MOLECULE *);
void drawUnitCell(struct MOLECULE *, Dimension, int);
void drawMillerPlane(struct MOLECULE *, int);
void labelAtoms(struct MOLECULE *, int);
void drawAnnotation(void);
void drawForces(struct MOLECULE *, int);
void drawNormalMode(struct MOLECULE *, int);
void drawArrow(double, double, double, double, double, double);
void drawGround(void);
void setupProjection(int);
void setupModelview(int);
void shadow(GLfloat v0[3], GLfloat v1[3], GLfloat v2[3], GLfloat vector[4]);
void shadowColor(void);

extern void setWindowColor(int, Pixel, const float *);
extern int StringWidth(XFontStruct *, char *);
extern int StringHeight(XFontStruct *);
extern void cone(double, double, double, double, double, double, double, int);
extern double dist(double, double, double, double, double, double);
extern void pixelToWorld(int, double *, double *);
extern void (*drawBegin)(GLenum), (*drawEnd)(void);
extern void (*drawVertex3d)(double, double, double);
extern void (*drawNormal3d)(double, double, double);
extern void (*drawColor4fv)(const GLfloat *);
extern void (*drawSphere)(GLUquadricObj *, GLdouble, GLint, GLint);
extern void (*drawString)(char *, double, double, double, double, GLuint, char);
extern void (*drawLineStipple)(GLint, GLushort);
extern void (*drawLineWidth)(GLfloat);
extern void (*drawDisable)(GLenum);
extern void raytracerBegin(GLenum);
extern void manual(Widget, caddr_t, caddr_t);
extern char *getStringResource(Widget, char *);
extern void printDialog(Widget, caddr_t, XmAnyCallbackStruct *);
extern void marchingCube(struct GRIDOBJECT *, double, int, void (*)(GLenum),
                         void (*)(void), void (*)(double, double, double),
                         void (*)(double, double, double), int, int);
extern void ellipse(double, double, double, double, double, double, double,
                    double, double, int);
extern void cylinder(double, double, double, double, double, double, double,
                     int, GLenum);
extern void raytracerMaterial(struct ELEMENT *);
extern void transformCoordinates(int, float input[4], float output[4]);
extern void redraw(int);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern clock_t getCPUTime(void);
extern void scaleAnnotation(float);
extern void moveAnnotation(int, double, double);
extern void selectMolecule(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void quaternionToMatrix(int, float *);
extern void incrementQuaternion(double, double, double, float *);
extern void deleteSelection(void);
extern void getScreenCoordinates(double, double, double, double *, double *,
                                 double *);

extern struct MOLECULE *molecules;
extern struct ELEMENT *elements;
extern struct WINDOW windows[];
extern struct ANNOTATION *annotation;
extern Pixel stdcol[9];
extern Widget topShell;
extern GLfloat light0p[], light1p[];
extern int ne, nmolecule;
extern int label, showForces, animate, showUnitCell, showInertia;
extern int primitive, bfatom, lights, moveItem, projectionMode, showHBond;
extern int shadows, ground, drawIcon;
extern double amplitude, forceScale, wnScale;
extern double gridres, sphereres, lineWidth, drawingSpeed;
extern float *transObject, *rotObject;
extern int rgbMode, picking, swapBuffers, selectAtom;
extern int iwavef, interp, debug, setins, nAnnotations;
extern double *grid, level, weight;
extern char *formBondLength, *formBondAngle, *formTorsionAngle;

void drawBackground(int window, Pixel color, double depthBufferClear)
{
  float background_rgb[4] = {0.0, 0.0, 1.0, 0.0};
  int clear=GL_COLOR_BUFFER_BIT;

  if (windows[window].smooth)
  {
    background_rgb[0]=windows[window].background_rgb[0];
    background_rgb[1]=windows[window].background_rgb[1];
    background_rgb[2]=windows[window].background_rgb[2];

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
    glShadeModel(GL_SMOOTH);
    glBegin(GL_POLYGON);
    if (windows[window].smooth & 1) background_rgb[0]=0.7;
    if (windows[window].smooth & 2) background_rgb[1]=0.7;
    if (windows[window].smooth & 4) background_rgb[2]=0.7;
    setWindowColor(FOREGROUND, color, background_rgb);
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
    if (windows[window].smooth & 1) background_rgb[0]=0.3;
    if (windows[window].smooth & 2) background_rgb[1]=0.3;
    if (windows[window].smooth & 4) background_rgb[2]=0.3;
    setWindowColor(FOREGROUND, color, background_rgb);
    glVertex3f(1.0, 1.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);
    glEnd();
    glShadeModel(GL_FLAT);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    clear=GL_DEPTH_BUFFER_BIT;
  }
  else
  {
    setWindowColor(BACKGROUND, color, windows[window].background_rgb);
    if (depthBufferClear != 0.0) clear|=GL_DEPTH_BUFFER_BIT;
  }
  if (depthBufferClear != 0.0)
    glClearDepth(depthBufferClear);
  if (shadows) clear|=GL_STENCIL_BUFFER_BIT;
  glClear(clear);
}

void drawMolecule(Widget w, caddr_t client_data, XmDrawingAreaCallbackStruct *data)
{
  struct MOLECULE *mol;
  double clockTick;
  GLfloat light1t[4];
  GLfloat light0c[4] = {1.0, 1.0, 1.0, 1.0};
  GLfloat light1c[4] = {1.0, 1.0, 1.0, 1.0};
  GLfloat ground0[3] = {0.0, 0.0, 0.0};
  GLfloat ground1[3] = {1.0, 0.0, 0.0};
  GLfloat ground2[3] = {0.0, 0.0, 1.0};
  clock_t startTime=0;
  Dimension width, height;
  int renderMode;
  register int found, i;

  XtRemoveAllCallbacks(windows[VIEWER].widget, XmNexposeCallback);
  XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
  startTime=getCPUTime();
  glGetIntegerv(GL_RENDER_MODE, &renderMode);
  if (renderMode == GL_RENDER && !drawIcon)
    glXMakeCurrent(XtDisplay(windows[VIEWER].widget), XtWindow(windows[VIEWER].widget),
                   windows[VIEWER].context);
  if (!picking)
  {
    drawBackground(VIEWER, windows[VIEWER].background, windows[VIEWER].far);
  }
  if (nAnnotations > 0) drawAnnotation();
  setupProjection(picking);
  if (shadows) glPushMatrix();
  if (primitive == GLU_FILL && (windows[VIEWER].mode != WIREMODEL || iwavef != ALL_OFF))
  {
    if (lights & 0x1)
    {
      glPushMatrix();
      quaternionToMatrix(GL_MODELVIEW, &rotObject[4*LIGHTNO0]);
      glLightfv(GL_LIGHT0, GL_POSITION, light0p);
      glLightfv(GL_LIGHT0, GL_DIFFUSE,  light0c);
      glPopMatrix();
    }
    if (lights & 0x2)
    {
      glPushMatrix();
      quaternionToMatrix(GL_MODELVIEW, &rotObject[4*LIGHTNO1]);
      glLightfv(GL_LIGHT1, GL_POSITION, light1p);
      glLightfv(GL_LIGHT1, GL_DIFFUSE,  light1c);
      glPopMatrix();
    }
  }
  if (projectionMode == PERSPECTIVE && ground)
  {
    drawGround();
  }
  glTranslatef(transObject[3*WORLD], transObject[3*WORLD+1], transObject[3*WORLD+2]);
  quaternionToMatrix(GL_MODELVIEW, &rotObject[4*WORLD]);
  for (i=0; i<nmolecule; i++)
  {
    mol=&molecules[i];
    glPushMatrix();
    setupModelview(i);
    glPushName(i);
    if (projectionMode == PERSPECTIVE && ground) glEnable(GL_CLIP_PLANE0);
    found=renderMolecule(mol, (width+height)/2, TRUE);
    if (projectionMode == PERSPECTIVE && ground) glDisable(GL_CLIP_PLANE0);
    if (label) labelAtoms(mol, LABEL);
    if (setins) labelAtoms(mol, INS);

    if (shadows && (lights & 0x2))
    {
      glStencilFunc(GL_LESS, 2, 0xffffffff);
      glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glColor4f(0.0, 0.0, 0.0, 0.5);
      glPassThrough((GLfloat)(ADD_DEPTH+(int)(2.0*windows[VIEWER].far)));
      glPopMatrix();
      ground0[1]=windows[VIEWER].bottom;
      ground1[1]=windows[VIEWER].bottom;
      ground2[1]=windows[VIEWER].bottom;
      transformCoordinates(LIGHTNO1, light1p, light1t);
      shadow(ground0, ground1, ground2, light1t);
      setupModelview(i);
      found=renderMolecule(mol, (width+height)/2, FALSE);
      glDisable(GL_BLEND);
      glDisable(GL_STENCIL_TEST);
      glPassThrough((GLfloat)(ADD_DEPTH));
    }

    glPopName();
    glPopMatrix();
  }
  if (swapBuffers) glXSwapBuffers(XtDisplay(windows[VIEWER].widget), XtWindow(windows[VIEWER].widget));

  startTime=getCPUTime()-startTime;
#if defined LINUX || defined DARWIN
  clockTick=(double)sysconf(_SC_CLK_TCK);
#else
  clockTick=(double)CLK_TCK;
#endif
  if (startTime != (clock_t)0)
    drawingSpeed=clockTick/startTime;
  else
    drawingSpeed=clockTick;
  if (debug)
  {
    if (startTime != (clock_t)0)
      printf("%.1f fps (%.2f ms per frame)\n", drawingSpeed,
               1000.*startTime/clockTick);
    else
      printf("> %.1f fps (< %.2f ms per frame)\n", drawingSpeed, 1000./clockTick);
  }
  XtAddCallback(windows[VIEWER].widget, XmNexposeCallback, (XtCallbackProc)drawMolecule, NULL);
}

int renderMolecule(struct MOLECULE *mol, int width, int lighting)
{
  double boxf;
  const float black[4] = {0.0, 0.0, 0.0, 1.0};
  int prim=GL_LINE_LOOP;
  register int i, found=0;

  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LESS);
  if (windows[VIEWER].mode == WIREMODEL || primitive == GLU_POINT)
  {
    glShadeModel(GL_FLAT);
    wireModel(mol, width, lighting);
  }
  if (primitive != GLU_FILL || windows[VIEWER].mode == WIREMODEL)
    glShadeModel(GL_FLAT);
  else
  {
    glShadeModel(GL_SMOOTH);
    glCullFace(GL_BACK);
    glEnable(GL_CULL_FACE);
    if (lighting)
    {
      glEnable(GL_LIGHTING);
      if (lights & 0x1) glEnable(GL_LIGHT0);
      if (lights & 0x2) glEnable(GL_LIGHT1);
    }
  }
  if (windows[VIEWER].mode == STICKMODEL || windows[VIEWER].mode == BALLMODEL)
    stickModel(mol, lighting);
  if (windows[VIEWER].mode == STICKMODEL)
  {
    if (lighting) glPassThrough((GLfloat)(ADD_DEPTH+(int)(windows[VIEWER].far)));
    balls(mol, -(mol->bondRadius), lighting);
    if (lighting) glPassThrough((GLfloat)(ADD_DEPTH));
  }
  if (windows[VIEWER].mode == BALLMODEL)
    balls(mol, 0.5, lighting);
  if (windows[VIEWER].mode == CUPMODEL)
  {
    balls(mol, 1.3, lighting);
    stickModel(mol, lighting);
  }
  if (mol->unitcell && showUnitCell)
    drawUnitCell(mol, width, lighting);
  if (primitive == GLU_FILL) glDisable(GL_CULL_FACE);
  if ((mol->imo != -1 || mol->ibasfu != -1 || iwavef != ALL_OFF) && !selectAtom)
  {
    if (iwavef > DENSITY && iwavef-DENSITY <= mol->ngridobjects)
      found=iwavef-DENSITY;
    else
    {
      for (i=0; i<mol->ngridobjects; i++)
      {
        if ((mol->gridObjects[i].type & ~GRIDREAD) == iwavef)
        {
          if ((iwavef == MOLECULAR_ORBITAL && mol->gridObjects[i].mo == mol->imo) ||
              (iwavef == BASIS_IN_MO && mol->gridObjects[i].mo == mol->imo &&
               mol->gridObjects[i].basisfunction == mol->ibasfu) ||
              (iwavef == BASIS_FUNCTION && mol->gridObjects[i].basisfunction == mol->ibasfu) ||
              iwavef >= DENSITY)
          {
            found=i+1;
            if (debug) printf("Found MO/basis function: %d\n", found);
            break;
          }
        }
      }
    }
    if (found)
    {
      boxf=windows[VIEWER].far;
      gridres=mol->gridObjects[found-1].resolution;
      (*drawLineWidth)((GLfloat)1.);
      switch (primitive)
      {
        case GLU_POINT: prim=GL_POINTS;
                        break;
        case GLU_LINE:  prim=GL_LINE_LOOP;
                        break;
        case GLU_FILL:  prim=GL_TRIANGLE_STRIP;
                        break;
      }
      if (primitive == GLU_FILL)
      {
        glShadeModel(GL_SMOOTH);
        if (lighting)
        {
          glEnable(GL_LIGHTING);
          if (lights & 0x1) glEnable(GL_LIGHT0);
          if (lights & 0x2) glEnable(GL_LIGHT1);
        }
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable(GL_BLEND);
      }
      for (i=0; i<ne; i++)
        if (!strcmp(elements[i].symbol, "Ps")) break;
      setAtomProperties(&elements[i], lighting);
      glPassThrough((GLfloat)VISIBILITY);
      glPushMatrix();
      if (mol->unitcell != NULL)
        glTranslatef(mol->unitcell->corners[0].x, mol->unitcell->corners[0].y,
	             mol->unitcell->corners[0].z);
      else
        glTranslatef(mol->transx, mol->transy, mol->transz);
      marchingCube(&(mol->gridObjects[found-1]), level, prim, drawBegin,
                   drawEnd, drawVertex3d, drawNormal3d, interp, debug);
      if (iwavef != DENSITY)
      {
        for (i=0; i<ne; i++)
          if (!strcmp(elements[i].symbol, "Ms")) break;
        setAtomProperties(&elements[i], lighting);
        level=(-level);
        marchingCube(&(mol->gridObjects[found-1]), level, prim, drawBegin,
                     drawEnd, drawVertex3d, drawNormal3d, interp, debug);
        level=(-level);
      }
      glPopMatrix();
      glPassThrough((GLfloat)VISIBILITY);
    }
  }
  if (mol->unitcell && mol->unitcell->showMiller)
  {
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    drawMillerPlane(mol, lighting);
    glDisable(GL_BLEND);
  }

  if (showForces && !picking) drawForces(mol, lighting);
  if (mol->mode != -1 && animate == ARROWS && mol->cnm != NULL)
    drawNormalMode(mol, lighting);

  glDisable(GL_DEPTH_TEST);
  if (primitive == GLU_FILL)
  {
    glShadeModel(GL_FLAT);
    if (lighting)
    {
      glDisable(GL_LIGHTING);
      if (lights & 0x1) glDisable(GL_LIGHT0);
      if (lights & 0x2) glDisable(GL_LIGHT1);
    }
    glDisable(GL_BLEND);
  }
  if (showInertia)
  {
    (*drawLineWidth)((GLfloat)1.);
    if (lighting)
      setWindowColor(FOREGROUND, stdcol[BLACK], black);
    else
      shadowColor();
    ellipse(0.0, 0.0, 0.0, mol->tinert[0][0],
            mol->tinert[0][1], mol->tinert[0][2],
            mol->tinert[1][0], mol->tinert[1][1],
            mol->tinert[1][2], 40);
    ellipse(0.0, 0.0, 0.0, mol->tinert[1][0],
            mol->tinert[1][1], mol->tinert[1][2],
            mol->tinert[2][0], mol->tinert[2][1],
            mol->tinert[2][2], 40);
    ellipse(0.0, 0.0, 0.0, mol->tinert[2][0],
            mol->tinert[2][1], mol->tinert[2][2],
            mol->tinert[0][0], mol->tinert[0][1],
            mol->tinert[0][2], 40);
  }
  if (mol->ninternal && lighting) drawIntern(mol);
  return(found);
}

void wireModel(struct MOLECULE *mol, Dimension width, int lighting)
{ 
  double x, y, z, nline, sline;
  int first, second;
  register int i;

  if (lineWidth == 0.0)
    nline=width*0.005;
  else
    nline=lineWidth;
  sline=2.0*nline;
  (*drawLineWidth)((GLfloat)nline);
  for (i=0; i<mol->nb; i++)
  {
    first=mol->bonds[i].first;
    second=mol->bonds[i].second;
    x=mol->atoms[first].x+mol->bonds[i].frac
     *(mol->atoms[second].x-mol->atoms[first].x);
    y=mol->atoms[first].y+mol->bonds[i].frac
     *(mol->atoms[second].y-mol->atoms[first].y);
    z=mol->atoms[first].z+mol->bonds[i].frac
     *(mol->atoms[second].z-mol->atoms[first].z);
    if (abs(mol->bonds[i].order) != 2)
    {
      if (mol->bonds[i].order == (-1))             /* Hydrogen bond */
      {
        if (!showHBond) continue;
        glEnable(GL_LINE_STIPPLE);                 /* Dashed lines */
        (*drawLineStipple)(1, 0xf0f0);
      }
      if (picking || mol->atoms[first].flags & SELECTED)
      {
        glPushName(first+1);
        (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
      }
      else
        (*drawBegin)(GL_LINE_STRIP);
      if (mol->bonds[i].order == (-1))
        setAtomProperties(mol->bondStyle, lighting);
      else
        setAtomProperties(mol->atoms[first].element, lighting);
      (*drawVertex3d)(mol->atoms[first].x, mol->atoms[first].y,
                      mol->atoms[first].z);
      (*drawVertex3d)(x, y, z);
      if (picking || mol->atoms[first].flags & SELECTED ||
          mol->atoms[second].flags & SELECTED)
      {
        (*drawEnd)();
        glPopName();
        if (mol->atoms[first].flags & SELECTED)
          (*drawLineWidth)((GLfloat)nline);
        glPushName(second+1);
        if (mol->atoms[second].flags & SELECTED)
          (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
        (*drawVertex3d)(x, y, z);
      }
      if (mol->bonds[i].order != (-1))
        setAtomProperties(mol->atoms[second].element, lighting);
      (*drawVertex3d)(mol->atoms[second].x, mol->atoms[second].y,
                      mol->atoms[second].z);
      (*drawEnd)();
      if (mol->atoms[second].flags & SELECTED)
        (*drawLineWidth)((GLfloat)nline);
      if (picking) glPopName();
      if (mol->bonds[i].order == (-1)) (*drawDisable)(GL_LINE_STIPPLE);
    }
    if (abs(mol->bonds[i].order) > 1)
    {
      if (picking || mol->atoms[first].flags & SELECTED)
      {
        glPushName(first+1); 
        (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
      }
      else
        (*drawBegin)(GL_LINE_STRIP);
      setAtomProperties(mol->atoms[first].element, lighting);
      (*drawVertex3d)(mol->atoms[first].x-mol->bonds[i].x,
                      mol->atoms[first].y-mol->bonds[i].y,
                      mol->atoms[first].z-mol->bonds[i].z);
      (*drawVertex3d)(x-mol->bonds[i].x, y-mol->bonds[i].y,
                      z-mol->bonds[i].z);
      if (picking || mol->atoms[first].flags & SELECTED ||
          mol->atoms[second].flags & SELECTED)
      {
        (*drawEnd)();
        glPopName();
        if (mol->atoms[first].flags & SELECTED)
          (*drawLineWidth)((GLfloat)nline);
        glPushName(second+1);
        if (mol->atoms[second].flags & SELECTED)
          (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
        (*drawVertex3d)(x-mol->bonds[i].x, y-mol->bonds[i].y,
                        z-mol->bonds[i].z);
      }
      setAtomProperties(mol->atoms[second].element, lighting);
      (*drawVertex3d)(mol->atoms[second].x-mol->bonds[i].x,
                      mol->atoms[second].y-mol->bonds[i].y,
                      mol->atoms[second].z-mol->bonds[i].z);
      (*drawEnd)();
      if (mol->atoms[second].flags & SELECTED)
        (*drawLineWidth)((GLfloat)nline);
      if (picking) glPopName();
      if (mol->bonds[i].order == (-2))
      {
        glEnable(GL_LINE_STIPPLE);            /* Dashed lines */
        (*drawLineStipple)(1, 0xf0f0);
      }
      if (picking || mol->atoms[first].flags & SELECTED)
      {
        glPushName(first+1);
        (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
      }
      else
        (*drawBegin)(GL_LINE_STRIP);
      setAtomProperties(mol->atoms[first].element, lighting);
      (*drawVertex3d)(mol->atoms[first].x+mol->bonds[i].x,
                      mol->atoms[first].y+mol->bonds[i].y,
                      mol->atoms[first].z+mol->bonds[i].z);
      (*drawVertex3d)(x+mol->bonds[i].x, y+mol->bonds[i].y,
                      z+mol->bonds[i].z);
      if (picking || mol->atoms[first].flags & SELECTED ||
          mol->atoms[second].flags & SELECTED)
      {
        (*drawEnd)();
        glPopName();
        if (mol->atoms[first].flags & SELECTED)
          (*drawLineWidth)((GLfloat)nline);
        glPushName(second+1);
        if (mol->atoms[second].flags & SELECTED)
          (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
        (*drawVertex3d)(x+mol->bonds[i].x, y+mol->bonds[i].y,
                        z+mol->bonds[i].z);
      }
      setAtomProperties(mol->atoms[second].element, lighting);
      (*drawVertex3d)(mol->atoms[second].x+mol->bonds[i].x,
                      mol->atoms[second].y+mol->bonds[i].y,
                      mol->atoms[second].z+mol->bonds[i].z);
      (*drawEnd)();
      if (mol->atoms[second].flags & SELECTED)
        (*drawLineWidth)((GLfloat)nline);
      if (picking) glPopName();
      if (mol->bonds[i].order == (-2))
        (*drawDisable)(GL_LINE_STIPPLE);
    }
  }
  x=0.01*windows[VIEWER].far;
  for (i=0; i<mol->na; i++)
  {
    if (mol->atoms[i].nbonds == 0)
    {
      if (picking) glPushName(i+1);
      setAtomProperties(mol->atoms[i].element, lighting);
      if (mol->atoms[i].flags & SELECTED)
        (*drawLineWidth)((GLfloat)sline);  
      (*drawBegin)(GL_LINES);
      (*drawVertex3d)(mol->atoms[i].x-x, mol->atoms[i].y,
                      mol->atoms[i].z);
      (*drawVertex3d)(mol->atoms[i].x+x, mol->atoms[i].y,
                      mol->atoms[i].z);
      (*drawVertex3d)(mol->atoms[i].x, mol->atoms[i].y-x,
                      mol->atoms[i].z);
      (*drawVertex3d)(mol->atoms[i].x, mol->atoms[i].y+x,
                      mol->atoms[i].z);
      (*drawVertex3d)(mol->atoms[i].x, mol->atoms[i].y,
                      mol->atoms[i].z-x);
      (*drawVertex3d)(mol->atoms[i].x, mol->atoms[i].y,
                      mol->atoms[i].z+x);
      (*drawEnd)();
      if (mol->atoms[i].flags & SELECTED)
        (*drawLineWidth)((GLfloat)nline);  
      if (picking) glPopName();
    }
  }
}

void stickModel(struct MOLECULE *mol, int lighting)
{
  double x, y, z, f=0.1;
  double xold, yold, zold;
  int first, second;
  register int i, j;

  (*drawLineWidth)(1.);
  glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  for (i=0; i<mol->nb; i++)
  {
    first=mol->bonds[i].first;
    second=mol->bonds[i].second;
    if (mol->bonds[i].order == (-1))
    {
      if (!showHBond) continue;
      glDisable(GL_CULL_FACE);
      setAtomProperties(mol->bondStyle, lighting);
      xold=mol->atoms[first].x;
      yold=mol->atoms[first].y;
      zold=mol->atoms[first].z;
      for (j=1; j<10; j+=2)
      {
        x=xold+f*(mol->atoms[second].x-mol->atoms[first].x);
        y=yold+f*(mol->atoms[second].y-mol->atoms[first].y);
        z=zold+f*(mol->atoms[second].z-mol->atoms[first].z);
        cylinder(xold, yold, zold, x, y, z, mol->bondRadius, (int)sphereres,
                 (GLenum)primitive);
        xold=x+f*(mol->atoms[second].x-mol->atoms[first].x);
        yold=y+f*(mol->atoms[second].y-mol->atoms[first].y);
        zold=z+f*(mol->atoms[second].z-mol->atoms[first].z);
      }
      glEnable(GL_CULL_FACE);
    }
    else
    {
      x=mol->atoms[first].x+mol->bonds[i].frac
       *(mol->atoms[second].x-mol->atoms[first].x);
      y=mol->atoms[first].y+mol->bonds[i].frac
       *(mol->atoms[second].y-mol->atoms[first].y);
      z=mol->atoms[first].z+mol->bonds[i].frac
       *(mol->atoms[second].z-mol->atoms[first].z);
      setAtomProperties(mol->atoms[first].element, lighting);
      glPushName(first+1);
      if (mol->atoms[first].flags & SELECTED)
        glEnable(GL_TEXTURE_2D);
      cylinder(mol->atoms[first].x, mol->atoms[first].y,
               mol->atoms[first].z, x, y, z,
               mol->bondRadius, (int)sphereres, (GLenum)primitive);
      if (mol->atoms[first].flags & SELECTED)
        glDisable(GL_TEXTURE_2D);
      glPopName();
      setAtomProperties(mol->atoms[second].element, lighting);
      glPushName(second+1);
      if (mol->atoms[second].flags & SELECTED)
        glEnable(GL_TEXTURE_2D);
      cylinder(mol->atoms[second].x, mol->atoms[second].y,
               mol->atoms[second].z, x, y, z,
                 mol->bondRadius, (int)sphereres, (GLenum)primitive);
      if (mol->atoms[second].flags & SELECTED)
        glDisable(GL_TEXTURE_2D);
      glPopName();
    }
  }
}

void balls(struct MOLECULE *mol, double fac, int lighting)
{
  GLUquadricObj *obj;
  register double rad;
  register int i;

  (*drawLineWidth)(1.);
  obj=gluNewQuadric();
  gluQuadricDrawStyle(obj, (GLenum)primitive);
  gluQuadricTexture(obj, GL_TRUE);
  if (primitive == GLU_FILL) gluQuadricNormals(obj, (GLenum)GLU_SMOOTH);
  else                       gluQuadricNormals(obj, (GLenum)GLU_NONE);
  glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  for (i=0; i<mol->na; i++)
  {
    if (fac < 0.0)
      rad=-fac;
    else
      rad=fac*mol->atoms[i].rad*mol->atoms[i].radScale;
    glPushMatrix();
    glTranslated(mol->atoms[i].x, mol->atoms[i].y,
                 mol->atoms[i].z);
    setAtomProperties(mol->atoms[i].element, lighting);
    glPushName(i+1);
    if (mol->atoms[i].flags & SELECTED)
      glEnable(GL_TEXTURE_2D);
    (*drawSphere)(obj, rad, (int)sphereres, (int)sphereres);
    if (mol->atoms[i].flags & SELECTED)
      glDisable(GL_TEXTURE_2D);
    glPopName();
    glPopMatrix();
  }
}

void drawIntern(struct MOLECULE *mol)
{
  const float black[4] = {0.0, 0.0, 0.0, 0.0};
  const float white[4] = {1.0, 1.0, 1.0, 0.0};
  Dimension width, height;
  char line[24], *form;
  double xpix, ypix;
  double wx1, wy1, wz, wx2, wy2, matrix[16];
  double h;
  register int i, j, k;

  if ((rgbMode && windows[VIEWER].background_rgb[0] == 0.0 &&
       windows[VIEWER].background_rgb[1] == 0.0 &&
       windows[VIEWER].background_rgb[2] == 0.0) ||
       (!rgbMode && windows[VIEWER].background == stdcol[BLACK]))
    setWindowColor(FOREGROUND, stdcol[WHITE], white);
  else
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
  pixelToWorld(VIEWER, &xpix, &ypix);
  h=(double)StringHeight(windows[VIEWER].font)*ypix;
  if (!picking)
  {
    glEnable(GL_LINE_STIPPLE);
    (*drawLineStipple)(1, 0xf0f0);
    (*drawLineWidth)((GLfloat)1.);
  }
  for (i=0; i<mol->ninternal; i++)
  {
    switch (abs(mol->internals[i].type))
    {
      case BONDAVERAGE: form=formBondLength;
                        break;
      case BONDLENGTH:  j=mol->internals[i].atoms[0];
                        k=mol->internals[i].atoms[1];
                        form=formBondLength;
                        (*drawBegin)(GL_LINES);
                        if (j & 0x20000000 && k & 0x20000000)
                        {
                          j&=0x1fffffff;
                          k&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                          (*drawVertex3d)(mol->unitcell->corners[k].x, mol->unitcell->corners[k].y,
                                          mol->unitcell->corners[k].z);
                        }
                        else
                        {
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                          (*drawVertex3d)(mol->atoms[k].x, mol->atoms[k].y,
                                          mol->atoms[k].z);
                        }
                        (*drawEnd)();
                        break;
      case ANGLE:    /* j=mol->internals[i].atoms[1]; */
                        form=formBondAngle;
                        (*drawBegin)(GL_LINE_STRIP);
                        j=mol->internals[i].atoms[0];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                        mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        j=mol->internals[i].atoms[1];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        j=mol->internals[i].atoms[2];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        (*drawEnd)();
                        break;
      case TORSION:  /* j=mol->internals[i].atoms[1];
                        k=mol->internals[i].atoms[2]; */
                        form=formTorsionAngle;
                        (*drawBegin)(GL_LINE_STRIP);
                        j=mol->internals[i].atoms[0];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        j=mol->internals[i].atoms[1];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        j=mol->internals[i].atoms[2];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        j=mol->internals[i].atoms[3];
			if (j & 0x20000000)
			{
			  j&=0x1fffffff;
                          (*drawVertex3d)(mol->unitcell->corners[j].x, mol->unitcell->corners[j].y,
                                          mol->unitcell->corners[j].z);
                        }
			else
                          (*drawVertex3d)(mol->atoms[j].x, mol->atoms[j].y,
                                          mol->atoms[j].z);
                        (*drawEnd)();
                        break;
      default:          (*drawDisable)(GL_LINE_STIPPLE);
                        return;
    }
    if (mol->internals[i].type > 0)
    {
      sprintf(line, form, mol->internals[i].value);
      if (picking)
      {
         glGetDoublev(GL_MODELVIEW_MATRIX, matrix);
         wx1=matrix[0]*mol->internals[i].x
            +matrix[4]*mol->internals[i].y
            +matrix[8]*mol->internals[i].z+matrix[12];
         wy1=matrix[1]*mol->internals[i].x
            +matrix[5]*mol->internals[i].y
            +matrix[9]*mol->internals[i].z+matrix[13]
            -ypix*windows[VIEWER].font->descent;
         wz=matrix[2]*mol->internals[i].x
           +matrix[6]*mol->internals[i].y
           +matrix[10]*mol->internals[i].z+matrix[14];
        wx2=wx1+(double)StringWidth(windows[VIEWER].font, line)*xpix;
        wy2=wy1+h;
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();
        glPushName((GLuint)((i+1) | 0x40000000));
        glBegin(GL_POLYGON);
        glVertex3d(wx1, wy1, wz);
        glVertex3d(wx2, wy1, wz);
        glVertex3d(wx2, wy2, wz);
        glVertex3d(wx1, wy2, wz);
        glEnd();
        glPopMatrix();
        glPopName();
      }
      else
      {
        (*drawString)(line, mol->internals[i].x, mol->internals[i].y,
                      mol->internals[i].z, 0.0, windows[VIEWER].GLfontId,
                      'l');
      }
    }
    else
    {
      XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
      for (j=0; j<nAnnotations; j++)
      {
        if (annotation[j].userdata == i)
	{
          getScreenCoordinates(mol->internals[i].x, mol->internals[i].y, mol->internals[i].z,
	                       &wx1, &wy1, &wz);
          moveAnnotation(j, 2.0*wx1/(double)width-1.0, 2.0*wy1/(double)height-1.0);
/*        printf("%f  %f\n", 2.0*wx1/(double)width-1.0, 2.0*wy1/(double)height-1.0);*/
        }
      }
    }
  }
  (*drawDisable)(GL_LINE_STIPPLE);
}

void drawUnitCell(struct MOLECULE *mol, Dimension width, int lighting)
{
  GLUquadricObj *obj;
  double nline, sline;
  double x, y, z;
  int first, second;
  register int i;
/*char name[4];*/

  nline=1.0;
  sline=2.0;
  if (windows[VIEWER].mode == WIREMODEL)
  {
    if (lineWidth == 0.0)
      nline=width*0.01;
    else
      nline=lineWidth;
    sline=2.0*nline;
    (*drawLineWidth)((GLfloat)nline);
  }
  else
  {
    (*drawLineWidth)(1.);
    glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    obj=gluNewQuadric();
    gluQuadricDrawStyle(obj, (GLenum)primitive);
    gluQuadricTexture(obj, GL_TRUE);
    if (primitive == GLU_FILL) gluQuadricNormals(obj, (GLenum)GLU_SMOOTH);
    else                       gluQuadricNormals(obj, (GLenum)GLU_NONE);
    glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    for (i=0; i<mol->unitcell->nc; i++)
    {
      glPushMatrix();
      glTranslated(mol->unitcell->corners[i].x, mol->unitcell->corners[i].y,
                   mol->unitcell->corners[i].z);
      setAtomProperties(mol->unitcell->corners[i].element, lighting);
      (*drawSphere)(obj, mol->bondRadius, (int)sphereres, (int)sphereres);
      glPopMatrix();
/*    sprintf(name, "%d", i);
      (*drawString)(name, mol->unitcell->corners[i].x+0.05, mol->unitcell->corners[i].y+0.05,
                    mol->unitcell->corners[i].z+0.05, 0.0, windows[VIEWER].GLfontId, 'l');
*/  }
  }

  for (i=0; i<mol->unitcell->ne; i++)
  {
    first=mol->unitcell->edges[i].first;
    second=mol->unitcell->edges[i].second;
    x=mol->unitcell->corners[first].x+0.5*(mol->unitcell->corners[second].x
     -mol->unitcell->corners[first].x);
    y=mol->unitcell->corners[first].y+0.5*(mol->unitcell->corners[second].y
     -mol->unitcell->corners[first].y);
    z=mol->unitcell->corners[first].z+0.5*(mol->unitcell->corners[second].z
     -mol->unitcell->corners[first].z);
    setAtomProperties(mol->unitcell->corners[first].element, lighting);
    if (picking) glPushName((GLuint)((first+1) | 0x20000000));
    if (windows[VIEWER].mode == WIREMODEL)
    {
      if (mol->unitcell->corners[first].flags & SELECTED)
      {
        (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
      }
      else
        (*drawBegin)(GL_LINE_STRIP);
      (*drawVertex3d)(mol->unitcell->corners[first].x,
                      mol->unitcell->corners[first].y,
                      mol->unitcell->corners[first].z);
      (*drawVertex3d)(x, y, z);
      if (picking || mol->unitcell->corners[first].flags & SELECTED ||
          mol->unitcell->corners[second].flags & SELECTED)
      {
        (*drawEnd)();
        glPopName();
        if (mol->unitcell->corners[first].flags & SELECTED)
          (*drawLineWidth)((GLfloat)nline);
        glPushName((GLuint)((second+1) | 0x20000000));
        if (mol->unitcell->corners[second].flags & SELECTED)
          (*drawLineWidth)((GLfloat)sline);
        (*drawBegin)(GL_LINES);
        (*drawVertex3d)(x, y, z);
      }
    }
    else
    {
      if (mol->unitcell->corners[first].flags & SELECTED) glEnable(GL_TEXTURE_2D);
      cylinder(mol->unitcell->corners[first].x, mol->unitcell->corners[first].y,
               mol->unitcell->corners[first].z, x, y, z,
               mol->bondRadius, (int)sphereres, (GLenum)primitive);
      if (mol->unitcell->corners[first].flags & SELECTED) glDisable(GL_TEXTURE_2D);
    }
    setAtomProperties(mol->unitcell->corners[second].element, lighting);
    if (windows[VIEWER].mode == WIREMODEL)
    {
      (*drawVertex3d)(mol->unitcell->corners[second].x,
                      mol->unitcell->corners[second].y,
                      mol->unitcell->corners[second].z);
      (*drawEnd)();
      if (mol->unitcell->corners[second].flags & SELECTED)
        (*drawLineWidth)((GLfloat)nline);
    }
    else
    {
      if (picking)
      {
        glPopName();
        glPushName((GLuint)((second+1) | 0x20000000));
      }
      if (mol->unitcell->corners[second].flags & SELECTED) glEnable(GL_TEXTURE_2D);
      cylinder(mol->unitcell->corners[second].x, mol->unitcell->corners[second].y,
               mol->unitcell->corners[second].z, x, y, z,
               mol->bondRadius, (int)sphereres, (GLenum)primitive);
      if (mol->unitcell->corners[second].flags & SELECTED) glDisable(GL_TEXTURE_2D);
    }
    if (picking) glPopName();
  }
}

void drawMillerPlane(struct MOLECULE *mol, int lighting)
{
  int step;
  register int i, j;

  setAtomProperties(mol->unitcell->millerPlanes[0].element, lighting);
  step=mol->unitcell->nmiller % 3 ? 4 : 3;

  (*drawBegin)(GL_POLYGON);
  for (i=0; i<mol->unitcell->nmiller; i+=step)
  {
    for (j=0; j<step; j++)
      (*drawVertex3d)(mol->unitcell->millerPlanes[i+j].x, mol->unitcell->millerPlanes[i+j].y,
                      mol->unitcell->millerPlanes[i+j].z);
  }
  (*drawEnd)();
}

void labelAtoms(struct MOLECULE *mol, int what)
{
  const float black[4] = {0.0, 0.0, 0.0, 0.0};
  const float white[4] = {1.0, 1.0, 1.0, 0.0};
  char name[MAXLENLINE];
  register int i;

  if ((rgbMode && windows[VIEWER].background_rgb[0] == 0.0 &&
       windows[VIEWER].background_rgb[1] == 0.0 &&
       windows[VIEWER].background_rgb[2] == 0.0) ||
       (!rgbMode && windows[VIEWER].background == stdcol[BLACK]))
    setWindowColor(FOREGROUND, stdcol[WHITE], white);
  else
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
  for (i=0; i<mol->na; i++)
  {
    if (strncmp(mol->atoms[i].name, "Uc", 2) && strncmp(mol->atoms[i].name, "Mp", 2))
    {
      switch(what)
      {
        case LABEL: sprintf(name, "%s%d", mol->atoms[i].name, i+1);
                    break;
        case INS:   sprintf(name, "%f", mol->atoms[i].neutronScatterfac);
                    break;
      }
      glPushName(i+1);
      (*drawString)(name, mol->atoms[i].x+0.05, mol->atoms[i].y+0.05,
                    mol->atoms[i].z+0.05, 0.0, windows[VIEWER].GLfontId, 'l');
      glPopName();
    }
  }
}

void drawAnnotation()
{
  Dimension width, height;
  register double h, l;
  float savedRGBColor[3];
  int savedColor;
  register int i;

  glEnable(GL_DEPTH_TEST);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  if (!picking) glLoadIdentity(); 
  else          glPushName((GLuint)0x80000000);     /* The name of the "molecule" */
  glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
  XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
  h=2.0*(double)StringHeight(windows[VIEWER].font)/height;
  for (i=0; i<nAnnotations; i++)
  {
    if (annotation[i].flags & DRAWANN)
    {
      if (picking)
      {
        if (annotation[i].flags & EDITABLE)
        {
          setWindowColor(BACKGROUND, windows[VIEWER].background,
                         windows[VIEWER].background_rgb);
          glPushName((GLuint)((i+1) | 0x80000000));  /* The name of the annotation */
          l=2.0*(double)StringWidth(windows[VIEWER].font, annotation[i].text)/width;
          glBegin(GL_POLYGON);
          glVertex3d(annotation[i].x, annotation[i].y, annotation[i].z);
          glVertex3d(annotation[i].x+l, annotation[i].y, annotation[i].z);
          glVertex3d(annotation[i].x+l, annotation[i].y+h, annotation[i].z);
          glVertex3d(annotation[i].x, annotation[i].y+h, annotation[i].z);
          glEnd();
          glPopName();
        }
      }
      else
      {
	savedColor=-1;
        if ((rgbMode && windows[VIEWER].background_rgb[0] == annotation[i].color_rgb[0] &&
             windows[VIEWER].background_rgb[1] == annotation[i].color_rgb[1] &&
             windows[VIEWER].background_rgb[2] == annotation[i].color_rgb[2]) ||
             (!rgbMode && windows[VIEWER].background == annotation[i].color))
        {
	  savedColor=annotation[i].color;
          annotation[i].color=stdcol[WHITE];
	  savedRGBColor[0]=annotation[i].color_rgb[0];
          annotation[i].color_rgb[0]=1.0-annotation[i].color_rgb[0];
	  savedRGBColor[1]=annotation[i].color_rgb[1];
          annotation[i].color_rgb[1]=1.0-annotation[i].color_rgb[1];
	  savedRGBColor[2]=annotation[i].color_rgb[2];
          annotation[i].color_rgb[2]=1.0-annotation[i].color_rgb[2];
        }
        setWindowColor(FOREGROUND, annotation[i].color, annotation[i].color_rgb);
        (*drawString)(annotation[i].text, annotation[i].x, annotation[i].y, annotation[i].z,
                      0.0, windows[VIEWER].GLfontId, 'l');
	if (savedColor != -1)
	{
	  annotation[i].color=savedColor;
	  annotation[i].color_rgb[0]=savedRGBColor[0];
	  annotation[i].color_rgb[1]=savedRGBColor[1];
	  annotation[i].color_rgb[2]=savedRGBColor[2];
	  savedColor=-1;
	}
      }
    }
  }
  if (picking) glPopName();
  glPopMatrix();
  glDisable(GL_DEPTH_TEST);
}

void drawForces(struct MOLECULE *mol, int lighting)
{
  const float black[4] = {0.0, 0.0, 0.0, 0.0};
  double gx, gy, gz;
  int primitiveSave=primitive;
  register int i, j, k;

/* Find number of atoms original read in (forces for atoms build in expandCell
   are obtained by reference to originally read atoms) */

  if (mol->history == NULL) return;
  i=0;
  while ((i < mol->na) && (mol->atoms[i].flags & ORIGINAL)) i++;
/*if (mol->unitcell && i != mol->na) i+=8;*/
  j=i*(mol->cycle-1);

  if (lighting)
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
  else
    shadowColor();
  (*drawLineWidth)((GLfloat)1.);
  if (drawBegin != glBegin) primitive=GLU_LINE;
  for (i=0; i<mol->na; i++)
  {
    k=mol->atoms[i].ref;
    if (mol->atoms[i].flags & X_FIXED)
      gx=0.0;
    else
      gx=-100.0*forceScale*mol->history[j+k].gx;
    if (mol->atoms[i].flags & Y_FIXED)
      gy=0.0;
    else
      gy=-100.0*forceScale*mol->history[j+k].gy;
    if (mol->atoms[i].flags & Z_FIXED)
      gz=0.0;
    else
      gz=-100.0*forceScale*mol->history[j+k].gz;
    drawArrow(mol->atoms[i].x, mol->atoms[i].y, mol->atoms[i].z, gx, gy, gz);
  }
  if (drawBegin != glBegin) primitive=primitiveSave;
}

void drawNormalMode(struct MOLECULE *mol, int lighting)
{
  const float black[4] = {0.0, 0.0, 0.0, 0.0};
  int primitiveSave=primitive, mode;
  register int i, j;

  if (mol->normal_modes == NULL) return;
  mode=mol->mode;
  if (mode == (-1)) return;
  if (lighting)
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
  else
    shadowColor();
  if (drawBegin != glBegin) primitive=GLU_LINE;
  for (i=0; i<mol->na; i++)
  {
    j=3*mol->atoms[i].ref;
    drawArrow(mol->atoms[i].x, mol->atoms[i].y, mol->atoms[i].z,
              amplitude*mol->cnm[mode+mol->nmodes*j],
              amplitude*mol->cnm[mode+mol->nmodes*(j+1)],
              amplitude*mol->cnm[mode+mol->nmodes*(j+2)]);
  }
  if (drawBegin != glBegin) primitive=primitiveSave;
}

void drawArrow(double x1, double y1, double z1, double x2, double y2, double z2)
{
  double length;

  if ((length=dist(x1, y1, z1, x1+x2, y1+y2, z1+z2)) > 1.0e-04)
  {
    if (length > 0.2*windows[VIEWER].top || drawBegin != glBegin)
      cylinder(x1, y1, z1, x1+0.8*x2, y1+0.8*y2, z1+0.8*z2, 0.01*length,
              (int)sphereres, (GLenum)primitive);
    else
    {
      (*drawLineWidth)((GLfloat)1.);
      (*drawBegin)(GL_LINES);
      (*drawVertex3d)(x1, y1, z1);
      (*drawVertex3d)(x1+0.8*x2, y1+0.8*y2, z1+0.8*z2);
      (*drawEnd)();
    }
    cone(x1+0.8*x2, y1+0.8*y2, z1+0.8*z2, x1+x2, y1+y2, z1+z2,
         0.03*length, (int)sphereres);
  }
}

void setupProjection(int picking)
{
  GLfloat xview, yview, zview;
  Dimension width, height;

  if (!picking)
  {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
  }
  if (projectionMode == ORTHO)
    glOrtho(windows[VIEWER].left, windows[VIEWER].right, windows[VIEWER].bottom,
            windows[VIEWER].top, windows[VIEWER].near, windows[VIEWER].far);
  else
  {
    XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
    if (2.0*windows[VIEWER].far > transObject[3*VIEWPOINT+2])
      zview=2.0*windows[VIEWER].far-transObject[3*VIEWPOINT+2];
    else
      zview=0.1;
    yview=0.75*windows[VIEWER].near*windows[VIEWER].top/windows[VIEWER].far;
    xview=yview*(double)(width)/(double)(height);
    glFrustum(xview, (-xview), yview, (-yview), zview,
              10.0*windows[VIEWER].far-transObject[3*VIEWPOINT+2]);
    glTranslatef(transObject[3*VIEWPOINT], transObject[3*VIEWPOINT+1],
                 transObject[3*VIEWPOINT+2]);
    quaternionToMatrix(GL_PROJECTION, &rotObject[4*VIEWPOINT]);
    gluLookAt(0.0, 0.0, 3.0*windows[VIEWER].far, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  }
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void setupModelview(int i)
{
  glTranslatef(transObject[3*(i+MOLECULES)], transObject[3*(i+MOLECULES)+1],
               transObject[3*(i+MOLECULES)+2]);
  quaternionToMatrix(GL_MODELVIEW, &rotObject[4*(i+MOLECULES)]);
}

void drawGround(void)
{
  GLdouble plane[4]={0.0, 1.0, 0.0, 0.0};

  setWindowColor(FOREGROUND, windows[VIEWER].foreground,
                 windows[VIEWER].foreground_rgb);
  if (shadows)
  {
    glEnable(GL_STENCIL_TEST);
    glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  }
  glPassThrough((GLfloat)(ADD_DEPTH+(int)(3.0*windows[VIEWER].far)));
  glBegin(GL_POLYGON);
  glVertex3d(10.0*windows[VIEWER].left, windows[VIEWER].bottom, 10.0*windows[VIEWER].near);
  glVertex3d(10.0*windows[VIEWER].left, windows[VIEWER].bottom, 10.0*windows[VIEWER].far);
  glVertex3d(10.0*windows[VIEWER].right, windows[VIEWER].bottom, 10.0*windows[VIEWER].far);
  glVertex3d(10.0*windows[VIEWER].right, windows[VIEWER].bottom, 10.0*windows[VIEWER].near);
  glEnd();
  plane[3]=(-windows[VIEWER].bottom);
  glClipPlane(GL_CLIP_PLANE0, plane);
  glPassThrough((GLfloat)(ADD_DEPTH));
}

void shadow(GLfloat ground0[3], GLfloat ground1[3], GLfloat ground2[3], GLfloat lightpos[4])
{
  GLfloat vec0[3], vec1[3], plane[4];
  GLfloat shadowMat[4][4];
  register GLfloat dot;

  /* Need 2 vectors to find cross product. */
  vec0[0]=ground1[0]-ground0[0];
  vec0[1]=ground1[1]-ground0[1];
  vec0[2]=ground1[2]-ground0[2];

  vec1[0]=ground2[0]-ground0[0];
  vec1[1]=ground2[1]-ground0[1];
  vec1[2]=ground2[2]-ground0[2];

  /* find cross product to get A, B, and C of plane equation */
  plane[0]=vec0[1]*vec1[2]-vec0[2]*vec1[1];
  plane[1]=vec0[0]*vec1[2]-vec0[2]*vec1[0];
  plane[2]=vec0[0]*vec1[1]-vec0[1]*vec1[0];
  plane[3]=-(plane[0]*ground0[0]+plane[1]*ground0[1]+plane[2]*ground0[2]);

  /* Find dot product between light position vector and ground plane normal. */
  dot=plane[0]*lightpos[0]+plane[1]*lightpos[1]+plane[2]*lightpos[2]
     +plane[3]*lightpos[3];

  shadowMat[0][0]=dot-lightpos[0]*plane[0];
  shadowMat[1][0]=(-lightpos[0]*plane[1]);
  shadowMat[2][0]=(-lightpos[0]*plane[2]);
  shadowMat[3][0]=(-lightpos[0]*plane[3]);

  shadowMat[0][1]=(-lightpos[1]*plane[0]);
  shadowMat[1][1]=dot-lightpos[1]*plane[1];
  shadowMat[2][1]=(-lightpos[1]*plane[2]);
  shadowMat[3][1]=(-lightpos[1]*plane[3]);

  shadowMat[0][2]=(-lightpos[2]*plane[0]);
  shadowMat[1][2]=(-lightpos[2]*plane[1]);
  shadowMat[2][2]=dot-lightpos[2]*plane[2];
  shadowMat[3][2]=(-lightpos[2]*plane[3]);

  shadowMat[0][3]=(-lightpos[3]*plane[0]);
  shadowMat[1][3]=(-lightpos[3]*plane[1]);
  shadowMat[2][3]=(-lightpos[3]*plane[2]);
  shadowMat[3][3]=dot-lightpos[3]*plane[3];

  glMultMatrixf((const GLfloat *)shadowMat);
}

void setAtomProperties(struct ELEMENT *element, int lighting)
{
  GLfloat v[4];

  if (drawBegin == raytracerBegin) raytracerMaterial(element);
  if (!lighting)
  {
    shadowColor();
    return;
  }

  if (rgbMode)
  {
    v[0]=0.5*(element->dark[0]+element->light[0]);
    v[1]=0.5*(element->dark[1]+element->light[1]);
    v[2]=0.5*(element->dark[2]+element->light[2]);
    v[3]=element->alpha;
  }

  if (primitive == GLU_FILL && (windows[VIEWER].mode != WIREMODEL ||
      !strcmp(element->symbol, "Ps") || !strcmp(element->symbol, "Ms")))
  {
    if (rgbMode)
    {
      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, element->ambient);
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, element->specular);
      glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, element->emission);
      glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, element->shininess);
    }
    else
    {
      glMaterialiv(GL_FRONT_AND_BACK, GL_COLOR_INDEXES, element->colormap);
      glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, element->shininess);
    }
  }
  else
  {
    if (rgbMode)
      (*drawColor4fv)(v);
    else
      glIndexi(element->colormap[1]);
  }
}

void shadowColor(void)
{
  GLfloat v[4]={0.0, 0.0, 0.0, 1.0};

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, v);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
  glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, v);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}

void viewerKeyAction(KeySym keysym)
{
  XmToggleButtonCallbackStruct data;

  switch (keysym)
  {
    case XK_F1:          manual((Widget)0, (caddr_t)0, (caddr_t)0);
                         break;
    case XK_Print:       printDialog((Widget)0, (caddr_t)VIEWER, (XmAnyCallbackStruct *)0);
                         break;
    case XK_KP_Add:
    case XK_plus:        forceScale*=1.4;
                         break;
    case XK_KP_Subtract:
    case XK_minus:       forceScale/=1.4;
                         break;
    case XK_Up:          windows[VIEWER].top/=1.5;
                         windows[VIEWER].bottom/=1.5;
                         windows[VIEWER].right/=1.5;
                         windows[VIEWER].left/=1.5;
                         if (nAnnotations) scaleAnnotation(1.0/1.5);
                         break;
    case XK_Down:        windows[VIEWER].top*=1.5;
                         windows[VIEWER].bottom*=1.5;
                         windows[VIEWER].right*=1.5;
                         windows[VIEWER].left*=1.5;
                         if (nAnnotations) scaleAnnotation(1.5);
                         break;
    case XK_Tab:         if (nmolecule > 1)
                         {
                           data.set=TRUE;
                           windows[VIEWER].set++;
                           if (windows[VIEWER].set >= nmolecule)
                           {
                             windows[VIEWER].set=(-1);
                             (void)selectMolecule((Widget)0,
                               (XtPointer)&windows[VIEWER].selectMenu[0], &data);
                           }
                           else
                             (void)selectMolecule((Widget)0,
                                (XtPointer)&windows[VIEWER].selectMenu[windows[VIEWER].set+2], &data);
                         }
                         break;
    case XK_x:           incrementQuaternion(5.0, 0.0, 0.0, &rotObject[4*moveItem]);
                         break;
    case XK_X:           incrementQuaternion(-5.0, 0.0, 0.0, &rotObject[4*moveItem]);
                         break;
    case XK_y:           incrementQuaternion(0.0, 5.0, 0.0, &rotObject[4*moveItem]);
                         break;
    case XK_Y:           incrementQuaternion(0.0, -5.0, 0.0, &rotObject[4*moveItem]);
                         break;
    case XK_z:           incrementQuaternion(0.0, 0.0, 5.0, &rotObject[4*moveItem]);
                         break;
    case XK_Z:           incrementQuaternion(0.0, 0.0, -5.0, &rotObject[4*moveItem]);
                         break;
    case XK_0:           rotObject[4*moveItem]=rotObject[4*moveItem+1]=rotObject[4*moveItem+2]=0.0;
                         rotObject[4*moveItem+3]=1.0;
                         break;
    case XK_Escape:      deleteSelection();
                         break;
  }
  redraw(VIEWER);
}
