CCL Home Page
Up Directory CCL render.c
/* render.c
 * RasMol2 Molecular Graphics
 * Roger Sayle, October 1994
 * Version 2.5
 */
#include "rasmol.h"

#ifdef IBMPC
#include 
#include 
#endif
#ifdef APPLEMAC
#ifdef __CONDITIONALMACROS__
#include 
#else
#include 
#endif
#include 
#endif
#ifndef sun386
#include 
#endif

#include 
#include 
#include 

#define RENDER
#include "graphics.h"
#include "render.h"
#include "molecule.h"
#include "transfor.h"
#include "command.h"
#include "abstree.h"
#include "pixutils.h"

/* Avoid PowerPC Errors! */
#ifdef INFINITY
#undef INFINITY
#endif

#define PoolSize        16
#define RootSix         2.4494897427831780
#define OneOverRootSix  0.4082482904638630
#define TwoOverRootSix  0.8164965809277260
#define ApproxZero      1.0E-3
#define INFINITY       200000
#define FUDGEFACTOR    1000



typedef struct { int dx, dy, dz; } DotVector;
typedef struct { 
	DotVector __far *probe;
	DotVector __far *dots;
	int count;
	} ElemDotStruct;

typedef struct _Item {
	struct _Item __far *list;
	Atom  __far *data;
    } Item;

typedef struct { Real h,l; } Interval;


static ElemDotStruct __far *ElemDots;
static Label *FreeLabel, *LabelList;
static Atom __far * __far *YBucket;
static Atom __far * __far *IBuffer;
static int BuckY,ItemX;
static int FBufX,FBufY;
static int DBClear;

static Atom __far *SBuffer;
static Atom __far *Exclude;
static Real ShadowI, ShadowJ, ShadowK;
static int  ShadowX, ShadowY, ShadowZ;
static int deltax, deltay, deltaz;
static int xcord, ycord, zcord;
static int xflag, yflag, zflag;
static int xhash, yhash, zhash;
static int RayCount;

static Item __far *FreeItem;
static Real VoxRatio,IVoxRatio;
static int VoxelCount,InVoxCount;
static int ProbeCount;
static int VoxelsDone;

/* Identified Atom Info */
static Long IdentDist;
static int IdentFound;
static int IdentDepth;


/* Macros for commonly used loops */
#define ForEachAtom  for(chain=Database->clist;chain;chain=chain->cnext) \
		     for(group=chain->glist;group;group=group->gnext)    \
		     for(aptr=group->alist;aptr;aptr=aptr->anext)
#define ForEachBond  for(bptr=Database->blist;bptr;bptr=bptr->bnext)
#define ForEachBack  for(chain=Database->clist;chain;chain=chain->cnext) \
		     for(bptr=chain->blist;bptr;bptr=bptr->bnext)




static void FatalRenderError(ptr)
    char *ptr;
{
    char buffer[80];

    sprintf(buffer,"Renderer Error: Unable to allocate %s!",ptr);
    RasMolFatalExit(buffer);
}


int isqrt( val )
    Card val;
{
#ifndef sun386
    register int i,result;
    register Card side, left;
    register Card temp;

    result = 0;
    side = left = 0;
    for( i=0; i>30);
	result <<= 1; side <<= 1;
	temp = side | 1;
	val <<= 2;

	if( left >= temp )
	{   side = temp+1;
	    left -= temp;
	    result |= 1;
	}
    }
    return( (int)result );
#else
    return( (int)sqrt((double)val) );
#endif
}

#ifdef IBMPC
void ClearBuffers()
{
    register char __huge *ptr;
    register Long count;

    if( !FBClear )
    {   FBClear = True;
	ptr = (Pixel __huge*)GlobalLock(FBufHandle);
	count = (Long)XRange*YRange;
	while( count > 65534 )
	{   _fmemset(ptr,0,(size_t)65534);
	    count -= 65534;
	    ptr += 65534;
	}
	if( count )
	    _fmemset(ptr,0,(size_t)count);
	GlobalUnlock(FBufHandle);
    }

    if( !DBClear )
    {   DBClear = True;
	ptr = (char __huge*)GlobalLock(DBufHandle);
	count = (Long)XRange*YRange*sizeof(short);
	while( count > 65534 )
	{   _fmemset(ptr,0,(size_t)65534);
	    count -= 65534;
	    ptr += 65534;
	}
	if( count )
	    _fmemset(ptr,0,(size_t)count);
	GlobalUnlock(DBufHandle);
    }
}
#else /* !IBMPC */
void ClearBuffers()
{
    register Long __huge *ptr;
    register Long __huge *end;
    register Long fill;

    if( !FBClear )
    {   FBClear = True;
	fill = Lut[BackCol];
#ifdef EIGHTBIT
	fill |= fill<<8;
	fill |= fill<<16;
#endif
	ptr = (Long __huge*)FBuffer;
	end = (Long __huge*)(FBuffer+(Long)XRange*YRange);
	do { *ptr++=fill; *ptr++=fill;
	     *ptr++=fill; *ptr++=fill;
	} while( ptr>2;  iptr = IBuffer;
	for( index=0; index<=len; index++ )
	{   *iptr++ = (void __far*)0;  *iptr++ = (void __far*)0;
	    *iptr++ = (void __far*)0;  *iptr++ = (void __far*)0;
	}
	ItemX = XRange;
    }
}


void ReSizeScreen()
{
    register Real orig;

    if( Range != ZoomRange )
    {   orig = MaxZoom;
	MaxZoom = 0.236*(WorldSize+1000)/Range;
	ZoomRange = Range;  MaxZoom -= 1.0;

	/* Handle Change in MaxZoom */
	if( DialValue[3]>0.0 )
	{   DialValue[3] *= orig/MaxZoom;
	    if( DialValue[3]>1.0 )
		DialValue[3] = 1.0;
	}
    }

#ifdef IBMPC
    if( !FBufHandle || (FBufX!=XRange) || (FBufY!=YRange) )
#else /* UNIX */
    if( !FBuffer || (FBufX!=XRange) || (FBufY!=YRange) )
#endif
    {   if( !CreateImage() )
	    FatalRenderError("frame buffer");

	BucketFlag = False;
	FBufX=XRange;  FBufY=YRange;  FBClear = False;
	ReAllocBuffers();
	ClearBuffers();
    }
}



static void PrepareYBucket()
{
    register Atom __far * __far *temp;
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register int scan;
    register int rad;

    temp = YBucket;
    for( scan=0; scanflag&SphereFlag )
	    {   rad = aptr->irad;
		if( (aptr->x-rad>=XRange) || 
		    (aptr->x+rad<0) || (aptr->y+rad<0) )
		    continue;
		if( (scan=aptr->y-rad) > BuckY ) continue;

		if( scan>0 )
		{   aptr->bucket = YBucket[scan];
		    YBucket[scan] = aptr;
		} else
		{   aptr->bucket = *YBucket;
		    *YBucket = aptr;
		}
	    }
    } else
	ForEachAtom
	    if( aptr->flag&SphereFlag )
	    {   scan = aptr->y-aptr->irad;
		aptr->bucket = YBucket[scan];
		YBucket[scan] = aptr;
	    }
    BucketFlag = True;
}

#ifdef FUNCPROTO
/* Function Prototypes */
static void SqrInterval( Interval __far* );
static void VoxelInsert( Atom __far*, int );
static int AtomInter( Atom __far* );
#endif


static void SqrInterval( ival )
    Interval __far *ival;
{   register Real l,h;

    l = ival->l;
    h = ival->h;

    if( l>=0.0 )
    {   ival->l = l*l;
	ival->h = h*h;
    } else if( h<0.0 )
    {   ival->l = h*h;
	ival->h = l*l;
    } else
    {   ival->h = (-l>h)? l*l : h*h;
	ival->l = 0.0;
    }
}

static void VoxelInsert( ptr, ref )
    Atom __far *ptr;
    int ref;
{
    register Item __far *datum;
    register int i;

    if( !FreeItem )
    {   datum = (Item __far*)_fmalloc( PoolSize*sizeof(Item) );
	if( !datum ) FatalRenderError("voxel item");
	for( i=1; ilist = FreeItem;
	    FreeItem = datum++;
	}
    } else
    {   datum = FreeItem;
	FreeItem = datum->list;
    }
    datum->data = ptr;
    InVoxCount++;

    if( !HashTable[ref] ) VoxelCount++;
    datum->list = (Item __far*)HashTable[ref];
    HashTable[ref] = (void __far*)datum;
}


void ResetVoxelData()
{
    register Item __far *datum;
    register int i;

    if( VoxelsDone )
    {   for( i=0; ilist ) datum = datum->list;
		datum->list = FreeItem;
		FreeItem = (Item __far*)HashTable[i];
		HashTable[i] = (void __far*)0;
	    }
	VoxelsDone = False;
    } else for( i=0; iflag & flag )
    {   mx = aptr->xorg+Offset;
	my = aptr->yorg+Offset;
	mz = aptr->zorg+Offset;
	if( flag != SphereFlag )
	{   if( SolventDots || !ProbeRadius )
	    {   rad = ElemVDWRadius(GetElemNumber(aptr));
	    } else rad = ProbeRadius;
	} else rad = aptr->radius;  
	radius2 = (Long)rad*rad;

	lvx = (int)((mx-rad)*IVoxRatio);  hvx = (int)((mx+rad)*IVoxRatio);
	lvy = (int)((my-rad)*IVoxRatio);  hvy = (int)((my+rad)*IVoxRatio);
	lvz = (int)((mz-rad)*IVoxRatio);  hvz = (int)((mz+rad)*IVoxRatio);


	for( px=lvx; px<=hvx; px++ )
	{   ix.l=px*VoxRatio-mx;
	    ix.h=ix.l+VoxRatio;  
	    SqrInterval(&ix);
	    i = VOXORDER2*px + VOXORDER*lvy;
       
	    for( py=lvy; py<=hvy; py++ )
	    {   iy.l=py*VoxRatio-my;
		iy.h=iy.l+VoxRatio;
		SqrInterval(&iy);
		
		for( pz=lvz; pz<=hvz; pz++ )
		{   iz.l=pz*VoxRatio-mz; 
		    iz.h=iz.l+VoxRatio;
		    SqrInterval(&iz);

		    if( ((ix.h+iy.h+iz.h)>radius2) && 
			((ix.l+iy.l+iz.l)ApproxZero )
    {   deltax =  (int)(FUDGEFACTOR/ShadowI); xhash =  VOXORDER2; xflag =  1;
    } else if( ShadowI<-ApproxZero )
    {   deltax = -(int)(FUDGEFACTOR/ShadowI); xhash = -VOXORDER2; xflag = -1;
    } else xflag = 0;

    if( ShadowJ>ApproxZero )
    {   deltay =  (int)(FUDGEFACTOR/ShadowJ); yhash =  VOXORDER; yflag =  1;
    } else if( ShadowJ<-ApproxZero )
    {   deltay = -(int)(FUDGEFACTOR/ShadowJ); yhash = -VOXORDER; yflag = -1;
    } else yflag = 0;

    if( ShadowK>ApproxZero )
    {   deltaz =  (int)(FUDGEFACTOR/ShadowK); zhash = zflag =  1;
    } else if( ShadowK<-ApproxZero )
    {   deltaz = -(int)(FUDGEFACTOR/ShadowK); zhash = zflag = -1;
    } else zflag = 0;
}


static int AtomInter( ptr )
    Atom __far *ptr;
{
    register Long modv,radius2;
    register int vx, vy, vz;
    register Real tca;

    if( ptr->mbox == RayCount )
	return( False );
    ptr->mbox = RayCount;

    vx = (int)ptr->xorg-ShadowX;
    vy = (int)ptr->yorg-ShadowY;
    vz = (int)ptr->zorg-ShadowZ;

    tca = vx*ShadowI + vy*ShadowJ + vz*ShadowK;
    if( tca<0.0 ) return( False );
    
    radius2 = ptr->radius+10;  radius2 = radius2*radius2;
    modv = (Long)vx*vx + (Long)vy*vy + (Long)vz*vz - radius2;
    return( modvlist )
	    if( (ptr->data!=Exclude) && AtomInter(ptr->data) )
	    {   SBuffer = ptr->data;
		return( True );
	    }

	if( (dx<=dy) && (dx<=dz) )
	{   xcord += xflag;
	    if( (xcord<0) || (xcord>=VOXORDER) ) return( False );
	    ident += xhash; dx += deltax;
	} else if( dy<=dz  ) /*(dy<=dx)*/
	{   ycord += yflag;
	    if( (ycord<0) || (ycord>=VOXORDER) ) return( False );
	    ident += yhash; dy += deltay;
	} else /* (dz<=dx) && (dz<=dy) */
	{   zcord += zflag;
	    if( (zcord<0) || (zcord>=VOXORDER) ) return( False );
	    ident += zhash; dz += deltaz;
	}
    }
}



#define UpdateScanAcross \
	if( depth>*dptr )   \
	{   *dptr = depth;  \
	    iptr[dx] = ptr; \
	} dptr++; dx++;


/* ScanLine for Shadows! */
static void ScanLine()
{
    static Atom __far *list;
    register Atom __far *ptr;
    register Atom __far * __far *iptr;
    register Atom __far * __far *prev;
    register short __huge *dbase;
    register short __huge *dptr;
    register Pixel __huge *fptr;
    register Byte __far *tptr;

    register int pos,depth,inten;
    register int lastx,wide,scan;
    register int dx,dy,dz;

    fptr = FBuffer;
    dbase = DBuffer;
    list = (void __far*)0;  

    wide = XRange>>2;  iptr = IBuffer;
    for( pos=0; pos<=wide; pos++ )
    {   *iptr++ = (void __far*)0;  *iptr++ = (void __far*)0;
	*iptr++ = (void __far*)0;  *iptr++ = (void __far*)0;
    }


    for( scan=0; scanbucket )
	{    ptr->next = list; list = ptr; }

	prev = &list;
	for( ptr=list; ptr; ptr=ptr->next )
	{   dy = scan - ptr->y;
	    wide = LookUp[ptr->irad][AbsFun(dy)];
	    lastx = (XRange-1)-ptr->x;
	    if( widex);

	    iptr = IBuffer+ptr->x;
	    tptr = LookUp[wide];

	    dptr = dbase+ptr->x+dx;
	    while( dx<=lastx )
	    {   depth = tptr[AbsFun(dx)]+ptr->z;
		UpdateScanAcross;
	    }

	    /* Remove completed atoms */
	    if( dy == ptr->irad )
	    {   *prev = ptr->next;
	    } else prev = &ptr->next;
	} /*ptr*/


	/* Process visible scanline */
	prev = (Atom __far* __far*)IBuffer;
	SBuffer = (void __far*)0;
	dptr = dbase; 

	for( pos=0; posz;
#ifdef INVERT
		inten = (dz<<1)+(pos-ptr->x)+(scan-ptr->y);
#else
		inten = (dz<<1)+(pos-ptr->x)-(scan-ptr->y);
#endif
		if( inten>0 )
		{   inten = (int)( (inten*ColConst[ptr->irad])>>ColBits);
		    dz = *dptr-ZOffset;
		    dx = pos-XOffset;
		    dy =   scan-YOffset;

		    ShadowX = (int)(dx*InvX[0]+dy*InvX[1]+dz*InvX[2]);
		    ShadowY = (int)(dx*InvY[0]+dy*InvY[1]+dz*InvY[2]);
		    ShadowZ = (int)(dx*InvZ[0]+dy*InvZ[1]+dz*InvZ[2]);

		    Exclude = ptr;
		    if( ShadowRay() )
		    {   *fptr = Lut[ptr->col+(inten>>2)];
		    } else *fptr = Lut[ptr->col+inten];
		} else *fptr = Lut[ptr->col];
		*prev = (void __far*)0;
	    }
	    dptr++; fptr++; prev++;
	}
	dbase = dptr;
    } /*scan*/
}


#ifdef FUNCPROTO
/* Function Prototype */
static void DisplayHBonds( HBond __far *, int );
static void DisplayRibbon( Chain __far * );
#endif


static void DisplaySpaceFill()
{
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;

    if( UseShadow )
    {   if( !BucketFlag )
	    PrepareYBucket();
	ScanLine();
    } else if( UseClipping )
    {   ForEachAtom
	    if( aptr->flag&SphereFlag )
		ClipSphere(aptr->x,aptr->y,aptr->z,aptr->irad,aptr->col);
    } else 
	ForEachAtom
	    if( aptr->flag&SphereFlag )
		DrawSphere(aptr->x,aptr->y,aptr->z,aptr->irad,aptr->col);
}


static void DisplayWireFrame()
{
    register Bond __far *bptr;
    register Atom __far *s;
    register Atom __far *d;
    register int sc,dc;

    if( UseClipping )
    {   ForEachBond
	   if( bptr->flag&WireFlag )
	   {   s = bptr->srcatom; d = bptr->dstatom;
	       if( !bptr->col ) 
	       {   sc = s->col;  dc = d->col;
	       } else sc = dc = bptr->col;
	       ClipTwinVector(s->x,s->y,s->z,d->x,d->y,d->z,sc,dc);
	   } else if( bptr->flag&CylinderFlag )
	   {   s = bptr->srcatom; d = bptr->dstatom;
	       if( !bptr->col ) 
	       {   sc = s->col;  dc = d->col;
	       } else sc = dc = bptr->col;

	       if( bptr->irad>0 )
	       {  ClipCylinder(s->x,s->y,s->z,sc,d->x,d->y,d->z,dc,bptr->irad);
	       } else ClipTwinLine(s->x,s->y,s->z,d->x,d->y,d->z,
				   sc+ColourMask,dc+ColourMask);
	   }
    } else
	ForEachBond
	   if( bptr->flag&WireFlag )
	   {   s = bptr->srcatom; d = bptr->dstatom;
	       if( !bptr->col ) 
	       {   sc = s->col;  dc = d->col;
	       } else sc = dc = bptr->col;
	       DrawTwinVector(s->x,s->y,s->z,d->x,d->y,d->z,sc,dc);
	   } else if( bptr->flag&CylinderFlag )
	   {   s = bptr->srcatom; d = bptr->dstatom;
	       if( !bptr->col ) 
	       {   sc = s->col;  dc = d->col;
	       } else sc = dc = bptr->col;

	       if( bptr->irad>0 )
	       {  DrawCylinder(s->x,s->y,s->z,sc,d->x,d->y,d->z,dc,bptr->irad);
	       } else DrawTwinLine(s->x,s->y,s->z,d->x,d->y,d->z,
				   sc+ColourMask,dc+ColourMask);
	   }
}


static void DisplayBackBone()
{
    register Chain __far *chain;
    register Bond __far *bptr;
    register Atom __far *s;
    register Atom __far *d;
    register int sc,dc;

    ForEachBack
       if( bptr->flag&DrawBondFlag )
       {   s = bptr->srcatom; d = bptr->dstatom;
	   if( !bptr->col ) 
	   {   sc = s->col;  dc = d->col;
	   } else sc = dc = bptr->col;

	   if( bptr->flag&CylinderFlag )
	   {   if( bptr->irad>0 )
	       { ClipCylinder(s->x,s->y,s->z,sc,d->x,d->y,d->z,dc,bptr->irad);
	       } else ClipTwinLine(s->x,s->y,s->z,d->x,d->y,d->z,
				   sc+ColourMask,dc+ColourMask);
	   } else ClipTwinVector(s->x,s->y,s->z,d->x,d->y,d->z,sc,dc);
       }
}


static void DisplayHBonds( list, mode )
    HBond __far *list; 
    int mode;
{
    register HBond __far *ptr;
    register Atom __far *s;
    register Atom __far *d;
    register int sc,dc;

    for( ptr=list; ptr; ptr=ptr->hnext )
	if( ptr->flag & DrawBondFlag )
	{   if( mode )
	    {   s = ptr->srcCA; d = ptr->dstCA;
		if( !s || !d ) continue;
	    } else
	    {   d = ptr->src;
		s = ptr->dst;
	    }

	    if( !ptr->col )
	    {   sc = s->col;  dc = d->col;
	    } else sc = dc = ptr->col;
	    if( ptr->flag & CylinderFlag )
	    {   if( ptr->irad>0 )
		{   ClipCylinder(s->x,s->y,s->z,sc,
				 d->x,d->y,d->z,dc,ptr->irad);
		} else ClipTwinLine(s->x,s->y,s->z,d->x,d->y,d->z,
				    sc+ColourMask,dc+ColourMask);
	    } else ClipDashVector(s->x,s->y,s->z,d->x,d->y,d->z,sc,dc);
	}
}



static void CalculateInten( ptr )
    Knot *ptr;
{
    register Real inten;
    register int size;

    size = isqrt( (Long)ptr->cx*ptr->cx + 
		  (Long)ptr->cy*ptr->cy + 
		  (Long)ptr->cz*ptr->cz );

    if( size )
    {   
#ifdef INVERT
	inten = ptr->cx + ptr->cy + ptr->cz + ptr->cz;
#else
	inten = ptr->cx - ptr->cy + ptr->cz + ptr->cz;
#endif
	inten /= size*RootSix;
	if( ptr->cz < 0 ) inten = -inten;

	ptr->inten = (int)(ColourMask*inten);
	if( ptr->inten<0 ) ptr->inten = 0;
    } else ptr->inten = ColourMask;
}


static void DisplayRibbon( chain )
    Chain  __far *chain;
{
    register Group __far *group;
    register Atom __far *captr;
    register Atom __far *o1ptr;
    register Atom __far *o2ptr;
    register Atom __far *next;

    register int prev,wide;
    register int col1,col2;
    register int bx,by,bz;
    register int dx,dy,dz;
    register int size;

    static Knot mid1, mid2, mid3;
    static Knot knot1, knot2;

    prev = False;  
    group = chain->glist;
    if( IsAmino(group->refno) )
    {   captr = FindGroupAtom(group,1);
    } else captr = FindGroupAtom(group,7);

    while( group->gnext )
    {   if( IsAmino(group->gnext->refno) )
	{   next = FindGroupAtom(group->gnext,1);
	    o1ptr = FindGroupAtom(group,3);
	} else /* Nucleic Acid */
	{   next = FindGroupAtom(group->gnext,7);
	    o1ptr = FindGroupAtom(group->gnext,10);
	}

	/* When not to have a control point! */
	if( !next || !captr || !o1ptr || (next->flag&BreakFlag) ||
	    !((group->flag|group->gnext->flag)&(RibbonFlag|StrandFlag)) )
	{   group = group->gnext;
	    captr = next;
	    prev = False;
	    continue;
	}

	knot2.tx = next->x - captr->x;
	knot2.ty = next->y - captr->y;
	knot2.tz = next->z - captr->z;

	if( IsAmino(group->refno) )
	{   bx = o1ptr->x - captr->x;
	    by = o1ptr->y - captr->y;
	    bz = o1ptr->z - captr->z;

	} else if( !FindGroupAtom(group,17) && 
		   (o2ptr=FindGroupAtom(group,8)) )
	{   /* Deoxyribonucleic Acid */
	    o2ptr = FindGroupAtom(group,8);
	    bx = (o1ptr->x + o2ptr->x)/2 - captr->x;
	    by = (o1ptr->y + o2ptr->y)/2 - captr->y;
	    bz = (o1ptr->z + o2ptr->z)/2 - captr->z;

	} else /* Ribonucleic Acid */
	{   bx = o1ptr->x - captr->x;
	    by = o1ptr->y - captr->y;
	    bz = o1ptr->z - captr->z;
	}

	/* c := a x b */
	knot2.cx = knot2.ty*bz - knot2.tz*by;
	knot2.cy = knot2.tz*bx - knot2.tx*bz;
	knot2.cz = knot2.tx*by - knot2.ty*bx;

	knot2.px = (captr->x + next->x)/2;
	knot2.py = (captr->y + next->y)/2;
	knot2.pz = (captr->z + next->z)/2;

	if( (group->struc&group->gnext->struc) & HelixFlag )
	{   size = isqrt((Long)knot2.cx*knot2.cx + 
			 (Long)knot2.cy*knot2.cy + 
			 (Long)knot2.cz*knot2.cz);

	    if( size )
	    {   wide = (int)(375*Scale);

#ifdef INVERT
		knot2.px += (int)(((Long)wide*knot2.cx)/size);
		knot2.py += (int)(((Long)wide*knot2.cy)/size);
		knot2.pz += (int)(((Long)wide*knot2.cz)/size);
#else
		knot2.px -= (int)(((Long)wide*knot2.cx)/size);
		knot2.py -= (int)(((Long)wide*knot2.cy)/size);
		knot2.pz -= (int)(((Long)wide*knot2.cz)/size);
#endif
	    }
	}

	/* d := c x a */
	dx = (int)(((Long)knot2.cy*knot2.tz - 
		    (Long)knot2.cz*knot2.ty)/96);
	dy = (int)(((Long)knot2.cz*knot2.tx - 
		    (Long)knot2.cx*knot2.tz)/96);
	dz = (int)(((Long)knot2.cx*knot2.ty - 
		    (Long)knot2.cy*knot2.tx)/96);

	/* Average Ribbon Width */
	if( group->flag & (RibbonFlag|StrandFlag) )
	{   if( group->gnext->flag & (RibbonFlag|StrandFlag) )
	    {   wide = (group->width+group->gnext->width)>>1;
	    } else wide = group->width;
	} else wide = group->gnext->width;

	wide = (int)(wide*Scale);

	size = isqrt((Long)dx*dx + (Long)dy*dy + (Long)dz*dz);

	if( size )
	{   dx = (int)(((Long)wide*dx)/size);
	    dy = (int)(((Long)wide*dy)/size);
	    dz = (int)(((Long)wide*dz)/size);

	    /* Handle Carbonyl Oxygen Flip */
	    if( prev && ((knot1.wx*dx + knot1.wy*dy + knot1.wz*dz) < 0) )
	    {   knot2.wx = -dx;
		knot2.wy = -dy;
		knot2.wz = -dz;
	    } else
	    {   knot2.wx = dx;
		knot2.wy = dy;
		knot2.wz = dz;
	    }
	} else
	{   knot2.wx = 0;
	    knot2.wy = 0;
	    knot2.wz = 0;
	}


	if( (group->flag|group->gnext->flag)&RibbonFlag )
	    CalculateInten( &knot2 );

	if( !(col1 = group->col1) )
	    col1 = captr->col;
	if( !(col2 = group->col2) )
	    col2 = captr->col;
	    

	if( prev )
	{   /* Approximate spline segment with straight line! */
	    /* StrandRibbon( &knot1, &knot2, col1, col2 );   */

	    /* Calculate Hermite Spline Points */
	    mid1.px = (int)(((Long)54*knot1.px + (Long)9*knot1.tx +
			     (Long)10*knot2.px - (Long)3*knot2.tx)>>6);
	    mid1.py = (int)(((Long)54*knot1.py + (Long)9*knot1.ty +
			     (Long)10*knot2.py - (Long)3*knot2.ty)>>6);
	    mid1.pz = (int)(((Long)54*knot1.pz + (Long)9*knot1.tz +
			     (Long)10*knot2.pz - (Long)3*knot2.tz)>>6);

	    mid2.px = (int)(((Long)4*knot1.px + knot1.tx + 
			     (Long)4*knot2.px - knot2.tx)>>3);
	    mid2.py = (int)(((Long)4*knot1.py + knot1.ty + 
			     (Long)4*knot2.py - knot2.ty)>>3);
	    mid2.pz = (int)(((Long)4*knot1.pz + knot1.tz + 
			     (Long)4*knot2.pz - knot2.tz)>>3);

	    mid3.px = (int)(((Long)10*knot1.px + (Long)3*knot1.tx +
			     (Long)54*knot2.px - (Long)9*knot2.tx)>>6);
	    mid3.py = (int)(((Long)10*knot1.py + (Long)3*knot1.ty +
			     (Long)54*knot2.py - (Long)9*knot2.ty)>>6);
	    mid3.pz = (int)(((Long)10*knot1.pz + (Long)3*knot1.tz +
			     (Long)54*knot2.pz - (Long)9*knot2.tz)>>6);

	    /* Calculate Hermite Spline Widths */
	    mid1.wx = (27*knot1.wx + 5*knot2.wx)>>5;
	    mid1.wy = (27*knot1.wy + 5*knot2.wy)>>5;
	    mid1.wz = (27*knot1.wz + 5*knot2.wz)>>5;

	    mid2.wx = (knot1.wx + knot2.wx)>>1;
	    mid2.wy = (knot1.wy + knot2.wy)>>1;
	    mid2.wz = (knot1.wz + knot2.wz)>>1;

	    mid3.wx = (5*knot1.wx + 27*knot2.wx)>>5;
	    mid3.wy = (5*knot1.wy + 27*knot2.wy)>>5;
	    mid3.wz = (5*knot1.wz + 27*knot2.wz)>>5;

	    /* Draw the Spline Segments */
	    if( group->flag & RibbonFlag )
	    {   mid1.cx = (27*knot1.cx + 5*knot2.cx)>>5;
		mid1.cy = (27*knot1.cy + 5*knot2.cy)>>5;
		mid1.cz = (27*knot1.cz + 5*knot2.cz)>>5;
		CalculateInten(&mid1);

		mid2.cx = (knot1.cx + knot2.cx)>>1;
		mid2.cy = (knot1.cy + knot2.cy)>>1;
		mid2.cz = (knot1.cz + knot2.cz)>>1;
		CalculateInten(&mid2);

		mid3.cx = (5*knot1.cx + 27*knot2.cx)>>5;
		mid3.cy = (5*knot1.cy + 27*knot2.cy)>>5;
		mid3.cz = (5*knot1.cz + 27*knot2.cz)>>5;
		CalculateInten(&mid3);

		SolidRibbon( &knot1, &mid1,  col1 );
		SolidRibbon( &mid1,  &mid2,  col1 );
		SolidRibbon( &mid2,  &mid3,  col1 );
		SolidRibbon( &mid3,  &knot2, col1 );

	    } else if( group->flag & StrandFlag )
	    {   StrandRibbon( &knot1, &mid1,  col1, col2 );
		StrandRibbon( &mid1,  &mid2,  col1, col2 );
		StrandRibbon( &mid2,  &mid3,  col1, col2 );
		StrandRibbon( &mid3,  &knot2, col1, col2 );
	    }
	} else prev = True;

	group = group->gnext;
	captr = next;

	knot1 = knot2;
    }
}



static void ResetLabels()
{
    register Label *ptr;

    while( LabelList )
    {   ptr = LabelList;
	LabelList = ptr->next;
	ptr->next = FreeLabel;
	free(ptr->label);
	FreeLabel = ptr;
    }
}


int DeleteLabels()
{
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register Label *label;
    register Label **ptr;
    register int result;

    if( !Database )
	return( True );

    result = True;
    DrawLabels = False;

    ForEachAtom
	if( aptr->flag & SelectFlag )
	{   result = False;
	    if( aptr->label )
	    {   label = (Label*)aptr->label;
		aptr->label = (void*)0;
		label->refcount--;
		result = False;

		if( !label->refcount )
		{   ptr = &LabelList;
		    while( *ptr != label )
		       ptr = &(*ptr)->next;

		    *ptr = label->next;
		    label->next = FreeLabel;
		    free(label->label);
		    FreeLabel = label;
		}
	    }
	} else if( aptr->label )
	    DrawLabels = True;
    return( result );
}


void DefineLabels( label )
    char *label;
{
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register Label *ptr;
    register char *cptr;
    register int len;


    if( !Database ) return;
    if( DeleteLabels() ) 
	return;

    len = 0;
    for( cptr=label; *cptr; cptr++ )
	len++;

    /* Strip trailing spaces */
    while( len && cptr[-1]==' ' )
    {   cptr--;  len--;
	*cptr = '\0';
    }
    if( !len ) return;


    /* Test for existing label */
    for( ptr=LabelList; ptr; ptr=ptr->next )
	if( !strcmp(ptr->label,label) )
	    break;

    if( !ptr )
    {   if( FreeLabel )
	{   ptr = FreeLabel;  FreeLabel = ptr->next;
	} else if( !(ptr=(Label*)malloc(sizeof(Label))) )
	    FatalRenderError("label");

	ptr->label = (char*)malloc(len+1);
	if( !ptr->label ) FatalRenderError("label");

	cptr = ptr->label;
	while( *cptr++ = *label++ );

	ptr->next = LabelList;
	ptr->refcount = 0;
	LabelList = ptr;
    }

    ForEachAtom
	if( aptr->flag & SelectFlag )
	{   aptr->label = ptr;
	    DrawLabels = True;
	    ptr->refcount++;
	}
}


static void DisplayLabels()
{
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register Label *label;
    register int col,z;

    auto char buffer[256];


    if( !Database )
	return;

    if( !UseSlabPlane )
    {   z = ImageRadius + ZOffset;
    } else z = SlabValue - 1;

    ForEachAtom
	if( aptr->label )
	{   /* Peform Label Slabbing! */
	    if( !ZValid(aptr->z) )
		continue;

	    label = (Label*)aptr->label;
	    FormatLabel(chain,group,aptr,label->label,buffer);

	    if( !UseLabelCol )
	    {   /* Depth-cue atom labels */
		/* col = aptr->col + (ColorDepth*                  */
		/*       (aptr->z+ImageRadius-ZOffset))/ImageSize; */
		col = aptr->col + ColourMask;
	    } else col = LabelCol;

	    /* (aptr->z+2) + ((aptr->flag & SphereFlag)?aptr->irad:0); */
	    DisplayString(aptr->x+4,aptr->y,z,buffer,col);
	}
}



#ifdef FUNCPROTO
/* Function Prototype */
static void AddDot( Long, Long, Long, int );
static void CheckVDWDot( Long, Long, Long, int );
static int TestSolventDot( Long, Long, Long );
#endif


void DeleteSurface()
{
    register DotStruct __far *ptr;
    register int shade;
    register int i;

    while( DotPtr )
    {   for( i=0; icount; i++ )
	{   shade = Colour2Shade(DotPtr->col[i]);
	    Shade[shade].refcount--;
	}

	ptr = DotPtr->next;
	_ffree( DotPtr );
	DotPtr = ptr;
    }
    DrawDots = False;
}


static void AddDot( x, y, z, col )
    Long x, y, z; int col;
{
    register DotStruct __far *ptr;
    register int i, shade;

    if( !DotPtr || (DotPtr->count==DotMax) )
    {   ptr = (DotStruct __far*)_fmalloc(sizeof(DotStruct));
	if( !ptr ) FatalRenderError("dot surface");

	ptr->next = DotPtr;
	ptr->count = 0;
	DotPtr = ptr;
    } else ptr = DotPtr;

    shade = Colour2Shade(col);
    Shade[shade].refcount++;

    i = ptr->count++;
    ptr->col[i] = col;
    ptr->xpos[i] = x;
    ptr->ypos[i] = y;
    ptr->zpos[i] = z;
    DrawDots = True;
}


static void CheckVDWDot( x, y, z, col )
    Long x, y, z; int col;
{
    register Item __far *item;
    register Atom __far *aptr;
    register int ix,iy,iz;
    register int dx,dy,dz;
    register Long dist;
    register Long rad;
    register int i;


    ix = (int)((x+Offset)*IVoxRatio);
    iy = (int)((y+Offset)*IVoxRatio);
    iz = (int)((z+Offset)*IVoxRatio);

    i = VOXORDER2*ix + VOXORDER*iy + iz;
    for( item=HashTable[i]; item; item=item->list )
	if( item->data != Exclude )
	{   aptr = item->data;
	    if( !ProbeRadius )
	    {   rad = ElemVDWRadius(GetElemNumber(aptr));
	    } else rad = ProbeRadius;
	    rad = rad*rad;

	    /* Optimized Test! */
	    dx = (int)(aptr->xorg - x);
	    if( (dist=(Long)dx*dx) < rad )
	    {   dy = (int)(aptr->yorg - y);
		if( (dist+=(Long)dy*dy) < rad )
		{   dz = (int)(aptr->zorg - z);
		    if( (dist+=(Long)dz*dz) < rad )
			return;
		}
	    }
	}
    AddDot( x, y, z, col );
}


static int TestSolventDot( x, y, z )
    Long x, y, z;
{
    register Item __far *item;
    register Atom __far *aptr;
    register int lx,ly,lz;
    register int hx,hy,hz;
    register int dx,dy,dz;
    register int ix,iy,iz;
    register Long dist;
    register Long rad;
    register int i;

    dist = Offset-ProbeRadius;
    lx = (int)((x+dist)*IVoxRatio);
    if( lx >= VOXORDER ) return( True );
    ly = (int)((y+dist)*IVoxRatio);
    if( ly >= VOXORDER ) return( True );
    lz = (int)((z+dist)*IVoxRatio);
    if( lz >= VOXORDER ) return( True );

    dist = Offset+ProbeRadius;
    hx = (int)((x+dist)*IVoxRatio);
    if( hx < 0 ) return( True );
    hy = (int)((y+dist)*IVoxRatio);
    if( hy < 0 ) return( True );
    hz = (int)((z+dist)*IVoxRatio);
    if( hz < 0 ) return( True );

    if( lx < 0 ) lx = 0;  if( hx >= VOXORDER ) hx = VOXORDER-1;
    if( ly < 0 ) ly = 0;  if( hy >= VOXORDER ) hy = VOXORDER-1;
    if( lz < 0 ) lz = 0;  if( hz >= VOXORDER ) hz = VOXORDER-1;

    for( ix=lx; ix<=hx; ix++ )
       for( iy=ly; iy<=hy; iy++ )
	  for( iz=lz; iz<=hz; iz++ )
	  {   i = VOXORDER2*ix + VOXORDER*iy + iz;
	      for( item=HashTable[i]; item; item=item->list )
		  if( item->data != Exclude )
		  {   aptr = item->data;
		      rad = ElemVDWRadius(GetElemNumber(aptr));
		      rad = (rad+ProbeRadius)*(rad+ProbeRadius);

		      /* Optimized Test! */
		      dx = (int)(aptr->xorg - x);
		      if( (dist=(Long)dx*dx) < rad )
		      {   dy = (int)(aptr->yorg - y);
			  if( (dist+=(Long)dy*dy) < rad )
			  {   dz = (int)(aptr->zorg - z);
			      if( (dist+=(Long)dz*dz) < rad )
				  return( False );
			  }
		      }
		  }
	  }
    return( True );
}


static void InitElemDots()
{
    register int i,size;

    size = MAXELEMNO*sizeof(ElemDotStruct);
    ElemDots = (ElemDotStruct __far*)_fmalloc(size);
    if( !ElemDots ) FatalRenderError("dot vector table");

    for( i=0; i>1) )
	vert = 1;

    i = 0;
    for( j=0; (iflag & SelectFlag )
	{   elem = GetElemNumber(aptr);
	    if( !ElemDots[elem].count )
		AddElemDots(elem,density);

	    Exclude = aptr;
	    ptr = ElemDots[elem].dots;
	    probe = ElemDots[elem].probe;
	    count = ElemDots[elem].count;
	    if( SolventDots )
	    {   for( i=0; ixorg + probe[i].dx,
					aptr->yorg + probe[i].dy,
					aptr->zorg + probe[i].dz ) )
			AddDot( aptr->xorg + ptr[i].dx,
				aptr->yorg + ptr[i].dy,
				aptr->zorg + ptr[i].dz,
				aptr->col );
	    } else 
		for( i=0; ixorg + ptr[i].dx,
				 aptr->yorg + ptr[i].dy,
				 aptr->zorg + ptr[i].dz,
				 aptr->col);
	}

    FreeElemDots();
}


static void DisplaySurface()
{
    register DotStruct __far *ptr;
    register int xi,yi,zi;
    register Real x,y,z;
    register int i;

    for( ptr=DotPtr; ptr; ptr=ptr->next )
	for( i=0; icount; i++ )
	{   x = ptr->xpos[i];  
	    y = ptr->ypos[i];  
	    z = ptr->zpos[i];

	    xi = (int)(x*MatX[0]+y*MatX[1]+z*MatX[2])+XOffset;
	    if( XValid(xi) )
	    {   yi = (int)(x*MatY[0]+y*MatY[1]+z*MatY[2])+YOffset;
		if( YValid(yi) )
		{   zi = (int)(x*MatZ[0]+y*MatZ[1]+z*MatZ[2])+ZOffset;
		    if( ZValid(zi) ) 
			PlotDeepPoint(xi,yi,zi,ptr->col[i]);
		}
	    }
	}
}


static void DisplayBoxes()
{
    register Real cosa, cosb, cosg;
    register Real sina, sinb, sing;
    register Real lena, lenb, lenc;
    register Real tmpx, tmpy, tmpz;
    register Real temp;

    register int xorg, yorg, zorg;
    register int dxx,dxy,dxz;
    register int dyx,dyy,dyz;
    register int dzx,dzy,dzz;
    register int x, y, z;


    if( DrawAxes  || DrawBoundBox )
    {   dxx = (int)(MaxX*MatX[0]);
	dxy = (int)(MaxX*MatY[0]);
	dxz = (int)(MaxX*MatZ[0]);

	dyx = (int)(MaxY*MatX[1]);
	dyy = (int)(MaxY*MatY[1]);
	dyz = (int)(MaxY*MatZ[1]);

	dzx = (int)(MaxZ*MatX[2]);
	dzy = (int)(MaxZ*MatY[2]);
	dzz = (int)(MaxZ*MatZ[2]);

	if( DrawAxes )
	{   /* Line (MinX,0,0) to (MaxX,0,0) */
	    ClipTwinLine(XOffset-dxx,YOffset-dxy,ZOffset-dxz, 
			 XOffset+dxx,YOffset+dxy,ZOffset+dxz,BoxCol,BoxCol);

	    /* Line (0,MinY,0) to (0,MaxY,0) */
	    ClipTwinLine(XOffset-dyx,YOffset-dyy,ZOffset-dyz, 
			 XOffset+dyx,YOffset+dyy,ZOffset+dyz,BoxCol,BoxCol);

	    /* Line (0,0,MinZ) to (0,0,MaxZ) */
	    ClipTwinLine(XOffset-dzx,YOffset-dzy,ZOffset-dzz, 
			 XOffset+dzx,YOffset+dzy,ZOffset+dzz,BoxCol,BoxCol);
	}

	if( DrawBoundBox )
	{   /* Line (MinX,MinY,MinZ) to (MaxX,MinY,MinZ) */
	    x=XOffset-dyx-dzx;  y=YOffset-dyy-dzy;  z=ZOffset-dyz-dzz;
	    ClipTwinLine(x-dxx,y-dxy,z-dxz,x+dxx,y+dxy,z+dxz,BoxCol,BoxCol);

	    /* Line (MaxX,MinY,MinZ) to (MaxX,MaxY,MinZ) */
	    x=XOffset+dxx-dzx;  y=YOffset+dxy-dzy;  z=ZOffset+dxz-dzz;
	    ClipTwinLine(x-dyx,y-dyy,z-dyz,x+dyx,y+dyy,z+dyz,BoxCol,BoxCol);

	    /* Line (MaxX,MaxY,MinZ) to (MinX,MaxY,MinZ) */
	    x=XOffset+dyx-dzx;  y=YOffset+dyy-dzy;  z=ZOffset+dyz-dzz;
	    ClipTwinLine(x+dxx,y+dxy,z+dxz,x-dxx,y-dxy,z-dxz,BoxCol,BoxCol);

	    /* Line (MinX,MaxY,MinZ) to (MinX,MinY,MinZ) */
	    x=XOffset-dxx-dzx;  y=YOffset-dxy-dzy;  z=ZOffset-dxz-dzz;
	    ClipTwinLine(x+dyx,y+dyy,z+dyz,x-dyx,y-dyy,z-dyz,BoxCol,BoxCol);


	    /* Line (MinX,MinY,MinZ) to (MinX,MinY,MaxZ) */
	    x=XOffset-dxx-dyx;  y=YOffset-dxy-dyy;  z=ZOffset-dxz-dyz;
	    ClipTwinLine(x-dzx,y-dzy,z-dzz,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);

	    /* Line (MaxX,MinY,MinZ) to (MaxX,MinY,MaxZ) */
	    x=XOffset+dxx-dyx;  y=YOffset+dxy-dyy;  z=ZOffset+dxz-dyz;
	    ClipTwinLine(x-dzx,y-dzy,z-dzz,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);

	    /* Line (MaxX,MaxY,MinZ) to (MaxX,MaxY,MaxZ) */
	    x=XOffset+dxx+dyx;  y=YOffset+dxy+dyy;  z=ZOffset+dxz+dyz;
	    ClipTwinLine(x-dzx,y-dzy,z-dzz,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);

	    /* Line (MinX,MaxY,MinZ) to (MinX,MaxY,MaxZ) */
	    x=XOffset-dxx+dyx;  y=YOffset-dxy+dyy;  z=ZOffset-dxz+dyz;
	    ClipTwinLine(x-dzx,y-dzy,z-dzz,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);


	    /* Line (MinX,MinY,MaxZ) to (MaxX,MinY,MaxZ) */
	    x=XOffset-dyx+dzx;  y=YOffset-dyy+dzy;  z=ZOffset-dyz+dzz;
	    ClipTwinLine(x-dxx,y-dxy,z-dxz,x+dxx,y+dxy,z+dxz,BoxCol,BoxCol);

	    /* Line (MaxX,MinY,MaxZ) to (MaxX,MaxY,MaxZ) */
	    x=XOffset+dxx+dzx;  y=YOffset+dxy+dzy;  z=ZOffset+dxz+dzz;
	    ClipTwinLine(x-dyx,y-dyy,z-dyz,x+dyx,y+dyy,z+dyz,BoxCol,BoxCol);

	    /* Line (MaxX,MaxY,MaxZ) to (MinX,MaxY,MaxZ) */
	    x=XOffset+dyx+dzx;  y=YOffset+dyy+dzy;  z=ZOffset+dyz+dzz;
	    ClipTwinLine(x+dxx,y+dxy,z+dxz,x-dxx,y-dxy,z-dxz,BoxCol,BoxCol);

	    /* Line (MinX,MaxY,MaxZ) to (MinX,MinY,MaxZ) */
	    x=XOffset-dxx+dzx;  y=YOffset-dxy+dzy;  z=ZOffset-dxz+dzz;
	    ClipTwinLine(x+dyx,y+dyy,z+dyz,x-dyx,y-dyy,z-dyz,BoxCol,BoxCol);
	}
    }

    if( DrawUnitCell )
    {   /* Calculate Unit Cell! */
	lena =  250.0*InfoCellA;
	lenb =  250.0*InfoCellB;
	lenc = -250.0*InfoCellC;

	cosa = cos(InfoCellAlpha);  sina = sin(InfoCellAlpha);
	cosb = cos(InfoCellBeta);   sinb = sin(InfoCellBeta);
	cosg = cos(InfoCellGamma);  sing = sin(InfoCellGamma);

	temp = cosa*cosa + cosb*cosb + cosg*cosg - 2.0*cosa*cosb*cosg;
	tmpx = cosb; 
	tmpy = (cosa - cosb*cosg)/sing;
	tmpz = sqrt(1.0-temp)/sing;

	dxx = (int)(lena*MatX[0]);
	dxy = (int)(lena*MatY[0]);
	dxz = (int)(lena*MatZ[0]);

	dyx = (int)(lenb*(cosg*MatX[0] + sing*MatX[1]));
	dyy = (int)(lenb*(cosg*MatY[0] + sing*MatY[1]));
	dyz = (int)(lenb*(cosg*MatZ[0] + sing*MatZ[1]));

	dzx = (int)(lenc*(tmpx*MatX[0] + tmpy*MatX[1] + tmpz*MatX[2]));
	dzy = (int)(lenc*(tmpx*MatY[0] + tmpy*MatY[1] + tmpz*MatY[2]));
	dzz = (int)(lenc*(tmpx*MatZ[0] + tmpy*MatZ[1] + tmpz*MatZ[2]));

	xorg = XOffset - (int)(OrigCX*MatX[0]+OrigCY*MatX[1]+OrigCZ*MatX[2]);
	yorg = YOffset - (int)(OrigCX*MatY[0]+OrigCY*MatY[1]+OrigCZ*MatY[2]);
	zorg = ZOffset + (int)(OrigCX*MatZ[0]+OrigCY*MatZ[1]+OrigCZ*MatZ[2]);


	/* Draw Unit Cell! */
	x = xorg;
	y = yorg;
	z = zorg;
	ClipTwinLine(x,y,z,x+dxx,y+dxy,z+dxz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x+dyx,y+dyy,z+dyz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);

	x = xorg + dxx + dyx;
	y = yorg + dxy + dyy;
	z = zorg + dxz + dyz;
	ClipTwinLine(x,y,z,x-dxx,y-dxy,z-dxz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x-dyx,y-dyy,z-dyz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x+dzx,y+dzy,z+dzz,BoxCol,BoxCol);

	x = xorg + dxx + dzx;
	y = yorg + dxy + dzy;
	z = zorg + dxz + dyz;
	ClipTwinLine(x,y,z,x-dxx,y-dxy,z-dxz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x+dyx,y+dyy,z+dyz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x-dzx,y-dzy,z-dzz,BoxCol,BoxCol);

	x = xorg + dyx + dzx;
	y = yorg + dyy + dzy;
	z = zorg + dyz + dzz;
	ClipTwinLine(x,y,z,x+dxx,y+dxy,z+dxz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x-dyx,y-dyy,z-dyz,BoxCol,BoxCol);
	ClipTwinLine(x,y,z,x-dzx,y-dzy,z-dzz,BoxCol,BoxCol);
    }
}


static void DisplaySelected()
{
    register Atom __far *s, __far *d;
    register Chain __far *chain;
    register Group __far *group;
    register Bond __far *bptr;
    register Atom __far *aptr;
    register int irad,sc,dc;
    register int col;

    irad = (int)(Scale*20);

    if( irad>0 )
    {   ForEachBond
	{   s = bptr->srcatom;  
	    col = (s->flag&SelectFlag)? 1 : 0;
	    sc = Shade2Colour(col);

	    d = bptr->dstatom;  
	    col = (d->flag&SelectFlag)? 1 : 0;
	    dc = Shade2Colour(col);
	    ClipCylinder(s->x,s->y,s->z,sc,d->x,d->y,d->z,dc,irad);
	}
    } else ForEachBond
	{   s = bptr->srcatom;  
	    col = (s->flag&SelectFlag)? 1 : 0;
	    sc = Shade2Colour(col);

	    d = bptr->dstatom;  
	    col = (d->flag&SelectFlag)? 1 : 0;
	    dc = Shade2Colour(col);
	    ClipTwinLine(s->x,s->y,s->z,d->x,d->y,d->z,
			 sc+ColourMask,dc+ColourMask);
	}


    irad = (int)(Scale*50);
    ForEachAtom
	if( aptr->flag&NonBondFlag )
	{   col = Shade2Colour( (aptr->flag&SelectFlag)? 1 : 0 );
	    ClipSphere(aptr->x,aptr->y,aptr->z,irad,col);
	}
}


void DrawFrame()
{
    register Chain __far *chain;

    if( !Database ) 
	return;

    ClearBuffers();

    if( UseSlabPlane )
    {   SlabValue = (int)(DialValue[7]*ImageRadius)+ZOffset;
	SlabInten = (int)(ColourMask*TwoOverRootSix);
	SliceValue = SlabValue+16;
	UseClipping = True;
    } else UseClipping = UseScreenClip;

#ifdef IBMPC
    /* Lock Buffers into Memory */
    FBuffer = (Pixel __huge*)GlobalLock(FBufHandle);
    DBuffer = (short __huge*)GlobalLock(DBufHandle);
#endif

    if( !DisplayMode )
    {   if( UseShadow && DrawAtoms )
	    if( !VoxelsClean )
		CreateVoxelData( SphereFlag );

	if( DrawAtoms ) 
	    DisplaySpaceFill();

	if( !UseSlabPlane || (SlabMode != SlabSection) )
	{   if( DrawBonds ) 
		DisplayWireFrame();

	    if( DrawRibbon )
		for( chain=Database->clist; chain; chain=chain->cnext )
		    if( chain->glist )
			DisplayRibbon( chain );

	    if( DrawDots ) DisplaySurface();
	    if( DrawLabels ) DisplayLabels();
	    DisplayHBonds( Database->slist, SSBondMode );
	    DisplayHBonds( Database->hlist, HBondMode );
	    DisplayBackBone();
	}
    } else DisplaySelected();
    DisplayBoxes();

#ifdef IBMPC
    /* Unlock Buffers */
    GlobalUnlock(FBufHandle);
    GlobalUnlock(DBufHandle);
#endif
    DBClear = False;
    FBClear = False;
}



#ifdef FUNCPROTO
/* Function Prototype */
static void TestAtomProximity( Atom __far *, int, int );
#endif


static void TestAtomProximity( ptr, xpos, ypos )
    Atom __far *ptr;
    int xpos, ypos;
{
    register Long dist;
    register int dx,dy;

    if( UseSlabPlane && (ptr->z>SlabValue) )
	return;

    dx = ptr->x - xpos;
    dy = ptr->y - ypos;

    dist = (Long)dx*dx + (Long)dy*dy;

    if( IdentFound )
    {   if( dist==IdentDist )
	{   if( ptr->zIdentDist ) 
	    return;
    }

    IdentDepth = ptr->z;
    IdentFound = True;
    IdentDist = dist;
    QAtom = ptr;
}

void IdentifyAtom( xpos, ypos )
    int xpos, ypos;
{
    register int rad, wide, dpth;
    register int new, dx, dy, dz;
    register Chain __far *chain;
    register Group __far *group;
    register HBond __far *hptr;
    register Atom  __far *aptr;
    register Bond __far *bptr;
    register char *str;
    char buffer[40];

    /* Reset Search */
    QChain = (void __far*)0;
    QGroup = (void __far*)0;
    QAtom = (void __far*)0;
    IdentFound = False;

    if( !DisplayMode )
    {   if( !UseSlabPlane || (SlabMode != SlabSection) )
	{   if( DrawBonds )
		ForEachBond
		    if( bptr->flag&DrawBondFlag )
		    {   TestAtomProximity(bptr->srcatom,xpos,ypos);
			TestAtomProximity(bptr->dstatom,xpos,ypos);
		    }

	    ForEachBack
		if( bptr->flag&DrawBondFlag )
		{   TestAtomProximity(bptr->srcatom,xpos,ypos);
		    TestAtomProximity(bptr->dstatom,xpos,ypos);
		}

	    for( hptr=Database->hlist; hptr; hptr=hptr->hnext )
		if( hptr->flag )
		{   if( HBondMode )
		    {   TestAtomProximity(hptr->srcCA,xpos,ypos);
			TestAtomProximity(hptr->dstCA,xpos,ypos);
		    } else
		    {   TestAtomProximity(hptr->src,xpos,ypos);
			TestAtomProximity(hptr->dst,xpos,ypos);
		    }
		}

	    for( hptr=Database->slist; hptr; hptr=hptr->hnext )
		if( hptr->flag )
		{   if( HBondMode )
		    {   TestAtomProximity(hptr->srcCA,xpos,ypos);
			TestAtomProximity(hptr->dstCA,xpos,ypos);
		    } else
		    {   TestAtomProximity(hptr->src,xpos,ypos);
			TestAtomProximity(hptr->dst,xpos,ypos);
		    }
		}
	}

	ForEachAtom
	{   /* Identify bond! */
	    if( aptr == QAtom )
	    {   QChain = chain;
		QGroup = group;
	    }

	    if( aptr->flag & SphereFlag )
	    {   dy = AbsFun(aptr->y-ypos);
		if( dy>aptr->irad ) continue;
		rad = LookUp[aptr->irad][dy];
		dx = AbsFun(aptr->x-xpos);
		if( dx>rad ) continue;

		new = False;
		dpth = aptr->z+LookUp[rad][dx];
		if( UseSlabPlane && (aptr->z+rad>=SlabValue) )
		{   dz = SlabValue-aptr->z;
		    if( SlabMode && (dz >= -rad) )
		    {   wide = LookUp[aptr->irad][AbsFun(dz)];
			if( (dy<=wide) && (dx<=(int)(LookUp[wide][dy])) )
			{   if( SlabMode == SlabFinal )
			    {   dpth = SliceValue;
				new = True;
			    } else if( SlabMode == SlabHollow )
			    {   dpth = aptr->z-LookUp[rad][dx];
				new = !IdentFound || (dpth>IdentDepth);
			    } else if( SlabMode != SlabHalf )
			    {   /* SlabClose, SlabSection */
				dpth = dx*dx+dy*dy+dz*dz+SliceValue;
				if( IdentFound )
				{   new = (IdentDepth0) && (SlabMode!=SlabSection) )
			    new = !IdentFound || (dpth>IdentDepth);
		    }
		} else if( !UseSlabPlane || (SlabMode != SlabSection) )
		    new = !IdentFound || IdentDist || (dpth>IdentDepth);

		if( new )
		{   IdentFound = True;
		    IdentDepth = dpth;
		    IdentDist = 0;

		    QChain = chain;
		    QGroup = group;
		    QAtom = aptr;
		}
	    } 
	}
    } else /* Display Mode */
    {   ForEachAtom
	{   TestAtomProximity(aptr,xpos,ypos);
	    /* Identify bond! */
	    if( aptr == QAtom )
	    {   QChain = chain;
		QGroup = group;
	    }
	}
    }


    if( IdentFound && (IdentDist<50) )
    {   if( CommandActive )
	    WriteChar('\n');
	CommandActive = False;

	WriteString("Atom: ");
	str = ElemDesc[QAtom->refno];
	if( str[0]!=' ' )   WriteChar(str[0]);
	WriteChar(str[1]);  WriteChar(str[2]);
	if( str[3]!=' ' )   WriteChar(str[3]);

	sprintf(buffer," %d  ",QAtom->serno);
	WriteString(buffer);

	str = Residue[QGroup->refno];
	if( QAtom->flag&HeteroFlag )
	{   WriteString("Hetero: ");
	} else WriteString("Group: ");

	if( str[0]!=' ' )  WriteChar(str[0]);
	WriteChar(str[1]); WriteChar(str[2]);

	sprintf(buffer," %d",QGroup->serno);
	WriteString(buffer);

	if( QChain->ident!=' ' )
	{   WriteString("  Chain: ");
	    WriteChar(QChain->ident);
	}
	WriteChar('\n');
	
    } else /* Reset Pick Atom! */
    {   QChain = (void __far*)0;
	QGroup = (void __far*)0;
	QAtom = (void __far*)0;
    }
}


void SetStereoMode( enable )
    int enable;
{
    StereoView = ViewLeft;
    UseStereo = enable;
}


void ResetRenderer()
{
    DeleteSurface();
    SolventDots = False;
    ProbeRadius = 0;

    DrawAtoms = False;  MaxAtomRadius = 0;
    DrawBonds = False;  MaxBondRadius = 0;
    DrawRibbon = False; DrawDots = False;
    DrawLabels = False;
    ResetLabels();

    SlabMode = SlabClose;
    UseSlabPlane = False;
    UseLabelCol = False;
    UseShadow = False;

    SSBondMode = False;
    HBondMode = False;
    DisplayMode = 0;

    DrawBoundBox = False;
    DrawUnitCell = False;
    DrawAxes = False;

    SetStereoMode(False);
}


void InitialiseRenderer()
{
    register Real radius2;
    register Byte __far *ptr;
    register int index,rad;
    register int maxval;

    FBuffer = (void __huge*)0;  
    DBuffer = (void __huge*)0;
    IBuffer = (void __far*)0;   
    YBucket = (void __far*)0;

#if defined(IBMPC) || defined(APPLEMAC)
    FBufHandle = NULL;
    DBufHandle = NULL;
#endif

#if defined(IBMPC) || defined(APPLEMAC)
    /* Allocate tables on FAR heaps */ 
    Array = (Byte __far*)_fmalloc(7261*sizeof(Byte));
    LookUp = (Byte __far* __far*)_fmalloc(120*sizeof(Byte __far*));
    HashTable = (void __far* __far*)_fmalloc(VOXSIZE*sizeof(void __far*));
    ColConst = (Card __far*)_fmalloc(120*sizeof(Card));
    
    if( !Array || !LookUp || !HashTable || !ColConst )
	FatalRenderError("tables");
#endif

    ptr = Array;
    for( rad=0; rad<120; rad++ )
    {   radius2 = rad*rad;
	LookUp[rad] = ptr;
	for( index=0; index<=rad; index++ )
	    *ptr++ = isqrt( (Card)(radius2-index*index) );

	maxval = (int)(RootSix*rad)+2;
	ColConst[rad] = ((Card)ColourDepth<
  
Modified: Mon Oct 24 16:00:00 1994 GMT
Page accessed 1209 times since Sat Apr 17 21:38:34 1999 GMT