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

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

#include 
#include 
#include 
#include 

#define MOLECULE
#include "molecule.h"
#include "command.h"
#include "abstree.h"
#include "transfor.h"
#include "render.h"


#define GroupPool    8
#define HBondPool   32
#define BondPool    32
#define AtomPool    32

#define NoLadder     0x00
#define ParaLadder   0x01
#define AntiLadder   0x02

#define Cos70Deg     0.34202014332567

#define FeatHelix    1
#define FeatSheet    2
#define FeatTurn     3

#define SourceNone   0
#define SourcePDB    1
#define SourceCalc   2

#define MaxHBondDist   ((Long)300*300)
#define MaxBondDist    ((Long)475*475)
#define MinBondDist    ((Long)100*100)
#define AbsMaxBondDist 500

typedef struct {
	int init, term;
	char chain;
	char type;
	} FeatEntry;

#ifdef APPLEMAC
#define AllocSize   256
typedef struct _AllocRef {
	struct _AllocRef *next;
	void *data[AllocSize];
	int count;
	} AllocRef;
static AllocRef *AllocList;  
#endif


#define FeatSize    32
typedef struct _Feature {
	    struct _Feature __far *fnext;
	FeatEntry data[FeatSize];
	int count;
	} Feature;

typedef struct {
	  char src[4];
	  char dst[4];
	  } ConvTable;

#define MAXALCATOM   5
static ConvTable AlcAtomTable[MAXALCATOM] = {
    { { 'S', 'O', '2', ' ' }, { ' ', 'S', '2', ' ' } },  /*  1 */
    { { 'C', 'A', 'R', ' ' }, { ' ', 'C', ' ', ' ' } },  /*  2 */
    { { 'N', 'A', 'R', ' ' }, { ' ', 'N', ' ', ' ' } },  /*  3 */
    { { 'N', 'A', 'M', ' ' }, { ' ', 'N', ' ', ' ' } },  /*  4 */
    { { 'N', 'P', 'L', '3' }, { ' ', 'N', '3', ' ' } },  /*  5 */
				 };

static Molecule __far *FreeMolecule;
static HBond __far *FreeHBond;
static Chain __far *FreeChain;
static Group __far *FreeGroup;
static Atom __far *FreeAtom;
static Bond __far *FreeBond;

static char PDBInsert;
static Feature __far *FeatList;
static HBond __far * __far *CurHBond;
static Molecule __far *CurMolecule;
static Chain __far *CurChain;
static Group __far *CurGroup;
static Atom __far *CurAtom;
static Atom __far *ConnectAtom;
static int InfoStrucSource;
static int HMinMaxFlag;
static int MMinMaxFlag;
static int MemSize;

static char Record[82];
static FILE *DataFile;

/* 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)


#ifdef APPLEMAC
/* External RasMac Function Declaration! */
void SetFileInfo( char*, OSType, OSType, short );
#endif


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

    sprintf(buffer,"Database Error: %s!",ptr);
    RasMolFatalExit(buffer);
}


static void FetchRecord()
{
    register char *ptr;
    register int ch;

    ptr = Record + 1;
    if( !feof(DataFile) )
    {   do {
	    ch = getc(DataFile);
	    if( (ch=='\n') || (ch==EOF) )
	    {   *ptr = 0;
		return;
            } else if( (ch=='\r') )
            {   ch = getc(DataFile);
                if( ch != '\n' )
                    ungetc(ch,DataFile);
                *ptr = 0;
                return;
	    } else *ptr++ = ch;
	} while( ptr < Record+80 );

	/* skip to the end of the line! */
	do { ch = getc(DataFile);
	} while( (ch!='\n') && (ch!='\r') && (ch!=EOF) );

        if( ch=='\r' )
        {   ch = getc(DataFile);
            if( ch != '\n' )
                ungetc(ch,DataFile);
        }
    }
    *ptr = 0;
}


static void ExtractString( len, src, dst )
    int len;  char *src, *dst;
{
    register char *ptr;
    register char ch;
    register int i;

    ptr = dst;
    for( i=0; i='0') && (ch<='9') )
	{   result = (10*result)+(ch-'0');
	} else if( ch=='-' )
	    neg = True;
    }
    return( neg? -result : result );
}


void DescribeMolecule()
{
    char buffer[40];

    if( CommandActive )
	WriteChar('\n');
    CommandActive=False;

    if( *InfoMoleculeName )
    {   WriteString("Molecule name ....... ");
	WriteString(InfoMoleculeName);
	WriteChar('\n');
    }

    if( *InfoClassification )
    {   WriteString("Classification ...... ");
	WriteString(InfoClassification);
	WriteChar('\n');
    }

    if( Database && (MainGroupCount>1) )
    {   WriteString("Secondary Structure . ");
	if( InfoStrucSource==SourceNone )
	{   WriteString("No Assignment\n");
	} else if( InfoStrucSource==SourcePDB )
	{   WriteString("PDB Data Records\n");
	} else WriteString("Calculated\n");
    }


    if( *InfoIdentCode )
    {   WriteString("Brookhaven Code ..... ");
	WriteString(InfoIdentCode);
	WriteChar('\n');
    }

    if( InfoChainCount>1 )
    {   sprintf(buffer,"Number of Chains .... %d\n",InfoChainCount);
	WriteString(buffer);
    }

    sprintf(buffer,"Number of Groups .... %d",MainGroupCount);
    WriteString(buffer);
    if( HetaAtomCount )
    {   sprintf(buffer," (%d)\n",HetaGroupCount);
	WriteString(buffer);
    } else WriteChar('\n');

    sprintf(buffer,"Number of Atoms ..... %ld",MainAtomCount);
    WriteString(buffer);
    if( HetaAtomCount )
    {   sprintf(buffer," (%d)\n",HetaAtomCount);
	WriteString(buffer);
    } else WriteChar('\n');

    if( InfoBondCount )
    {   sprintf(buffer,"Number of Bonds ..... %ld\n",InfoBondCount);
	WriteString(buffer);
    }

    if( InfoSSBondCount != -1 )
    {   WriteString("Number of Bridges ... ");
	sprintf(buffer,"%d\n\n",InfoSSBondCount);
	WriteString(buffer);
    }

    if( InfoHBondCount != -1 )
    {   WriteString("Number of H-Bonds ... ");
	sprintf(buffer,"%d\n",InfoHBondCount);
	WriteString(buffer);
    }

    if( InfoHelixCount != -1 )
    {   WriteString("Number of Helices ... ");
	sprintf(buffer,"%d\n",InfoHelixCount);
	WriteString(buffer);

	WriteString("Number of Strands ... ");
	sprintf(buffer,"%d\n",InfoLadderCount);
	WriteString(buffer);

	WriteString("Number of Turns ..... ");
	sprintf(buffer,"%d\n",InfoTurnCount);
	WriteString(buffer);
    }
}


#ifdef APPLEMAC
/* Avoid System Memory Leaks! */
static void RegisterAlloc( data )
    void *data;
{
    register AllocRef *ptr;
    
    if( !AllocList || (AllocList->count==AllocSize) )
    {   ptr = (AllocRef *)_fmalloc( sizeof(AllocRef) );
	if( !ptr ) FatalDataError("Memory allocation failed");
	
	ptr->next = AllocList;
	ptr->data[0] = data;
	ptr->count = 1;
	AllocList = ptr;
    } else AllocList->data[AllocList->count++] = data;
}
#else
#define RegisterAlloc(x)
#endif


static void CreateChain( ident )
    char ident;
{
    register Chain __far *prev;

    if( !CurMolecule )
    {   if( !(CurMolecule = FreeMolecule) )
	{   MemSize += sizeof(Molecule);
	    CurMolecule = (Molecule __far *)_fmalloc(sizeof(Molecule));
	    if( !CurMolecule ) FatalDataError("Memory allocation failed");
	    RegisterAlloc( CurMolecule );
	} else FreeMolecule = (void __far*)0;

	CurChain = (void __far*)0;
	CurMolecule->slist = (void __far*)0;
	CurMolecule->hlist = (void __far*)0;
	CurMolecule->blist = (void __far*)0;
	CurMolecule->clist = (void __far*)0;
	Database = CurMolecule;
    }

    /* Handle chain breaks! */
    if( !(prev=CurChain) )
	if( (prev=CurMolecule->clist) )
	    while( prev->cnext )
		prev = prev->cnext;

    if( !(CurChain = FreeChain) )
    {   MemSize += sizeof(Chain);
	CurChain = (Chain __far *)_fmalloc(sizeof(Chain));
	if( !CurChain ) FatalDataError("Memory allocation failed");
	RegisterAlloc( CurChain );
    } else FreeChain = FreeChain->cnext;

    if( prev )
    {   prev->cnext = CurChain;
    } else CurMolecule->clist = CurChain;
    CurChain->cnext = (void __far*)0;
     
    CurChain->ident = ident;
    CurChain->glist = (void __far*)0;
    CurChain->blist = (void __far*)0;
    ConnectAtom = (void __far*)0;
    CurGroup = (void __far*)0;
    InfoChainCount++;
}


static void CreateGroup( pool )
    int pool;
{
    register Group __far *ptr;
    register int i;

    if( !(ptr = FreeGroup) )
    {   MemSize += pool*sizeof(Group);
	ptr = (Group __far *)_fmalloc( pool*sizeof(Group) );
	if( !ptr ) FatalDataError("Memory allocation failed");
	RegisterAlloc( ptr );
	for( i=1; ignext = FreeGroup;
	    FreeGroup = ptr++;
	} 
    } else FreeGroup = ptr->gnext;
    
    if( CurGroup )
    {   CurGroup->gnext = ptr;
    } else CurChain->glist = ptr;
    CurGroup = ptr;

    CurAtom = (void __far*)0;
    ptr->gnext = (void __far*)0;
    ptr->alist = (void __far*)0;
    ptr->struc = 0;
    ptr->flag = 0;
    ptr->col1 = 0;
    ptr->col2 = 0;
}


static Atom __far *CreateAtom()
{
    register Atom __far *ptr;
    register int i;

    if( !(ptr = FreeAtom) )
    {   MemSize += AtomPool*sizeof(Atom);
	ptr = (Atom __far *)_fmalloc( AtomPool*sizeof(Atom) );
	if( !ptr ) FatalDataError("Memory allocation failed");
	RegisterAlloc( ptr );
	for( i=1; ianext = FreeAtom;
	    FreeAtom = ptr++;
	} 
    } else FreeAtom = ptr->anext;

    if( CurAtom )
    {   CurAtom->anext = ptr;
    } else CurGroup->alist = ptr;
    ptr->anext = (void __far*)0;
    CurAtom = ptr;

    SelectCount++;
    ptr->flag = SelectFlag | NonBondFlag;
    ptr->label = (void*)0;
    ptr->radius = 375;
    ptr->altl = ' ';
    ptr->mbox = 0;
    ptr->col = 0;

    return( ptr );
}


static void ProcessAtom( ptr )
    Atom __far *ptr;
{
    if( GetElemNumber(ptr) == 1 )
    {   ptr->flag |= HydrogenFlag;
	HasHydrogen = True;
    }

    if( !(ptr->flag&(HydrogenFlag|HeteroFlag)) )
	ptr->flag |= NormAtomFlag;

#ifdef INVERT
    ptr->yorg = -ptr->yorg;
#endif

    if( HMinMaxFlag || MMinMaxFlag )
    {   if( ptr->xorg < MinX ) 
	{   MinX = ptr->xorg;
	} else if( ptr->xorg > MaxX ) 
	    MaxX = ptr->xorg;

	if( ptr->yorg < MinY ) 
	{   MinY = ptr->yorg;
	} else if( ptr->yorg > MaxY ) 
	    MaxY = ptr->yorg;

	if( ptr->zorg < MinZ ) 
	{   MinZ = ptr->zorg;
	} else if( ptr->zorg > MaxZ ) 
	    MaxZ = ptr->zorg;
    } else 
    {   MinX = MaxX = ptr->xorg;
	MinY = MaxY = ptr->yorg;
	MinZ = MaxZ = ptr->zorg;
    }
	    
    if( ptr->flag & HeteroFlag )
    {   if( HMinMaxFlag )
	{   if( ptr->temp < MinHetaTemp ) 
	    {   MinHetaTemp = ptr->temp;
	    } else if( ptr->temp > MaxHetaTemp ) 
		MaxHetaTemp = ptr->temp;
	} else MinHetaTemp = MaxHetaTemp = ptr->temp;
	HMinMaxFlag = True;
	HetaAtomCount++;
    } else
    {   if( MMinMaxFlag )
	{   if( ptr->temp < MinMainTemp ) 
	    {   MinMainTemp = ptr->temp;
	    } else if( ptr->temp > MaxMainTemp ) 
		MaxMainTemp = ptr->temp;
	} else MinMainTemp = MaxMainTemp = ptr->temp;
	MMinMaxFlag = True;
	MainAtomCount++;
    }
}


static int FindResNo( ptr )
    char *ptr;
{
    register int refno;

    for( refno=0; refno='0') && (ch<='9') )
	{   result = (10*result)+(ch-'0');
	} else if( ch=='-' )
	    neg = True;
    }

    /* Handle Chem3D PDB Files! */
    if( Record[offset+3]=='.' )
	result /= 10;
    return( neg? -result : result );
}


static void ProcessPDBColourMask()
{
    register MaskDesc *ptr;
    register char *mask;
    register int i;

    if( MaskCount==MAXMASK )
	FatalDataError("Too many COLOR records in file");
    ptr = &UserMask[MaskCount];
    mask = ptr->mask;


    ptr->flags = 0;
    for( i=7; i<12; i++ )
	if( (*mask++ = Record[i]) != '#' )
	    ptr->flags |= SerNoFlag;

    for( i=13; i<21; i++ )
	*mask++ = Record[i];
    *mask++ = Record[22];

    for( i=23; i<27; i++ )
	if( (*mask++ = Record[i]) != '#' )
	    ptr->flags |= ResNoFlag;
    *mask++ = Record[27];

    ptr->r = (int)(ReadPDBCoord(31)>>2) + 5;
    ptr->g = (int)(ReadPDBCoord(39)>>2) + 5;
    ptr->b = (int)(ReadPDBCoord(47)>>2) + 5;
    ptr->radius = (short)(5*ReadValue(55,6))>>1;
    MaskCount++;
}


static Bond __far *ProcessBond( src, dst, flag )
    Atom __far *src, __far *dst;
    int flag;
{
    register Bond __far *ptr;
    register int i;

    if( !(ptr = FreeBond) )
    {   MemSize += BondPool*sizeof(Bond);
	ptr = (Bond __far *)_fmalloc( BondPool*sizeof(Bond) );
	if( !ptr ) FatalDataError("Memory allocation failed");
	RegisterAlloc( ptr );
	for( i=1; ibnext = FreeBond;
	    FreeBond = ptr++;
	} 
    } else FreeBond = ptr->bnext;
    
    ptr->flag = flag | SelectFlag;
    ptr->srcatom = src;
    ptr->dstatom = dst;
    ptr->radius = 0;
    ptr->col = 0;

    return( ptr );
}


static void ConnectAtoms( src, dst, flag )
    int src, dst, flag;
{
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register Atom __far *sptr;
    register Atom __far *dptr;
    register Bond __far *bptr;
    register int done;

    if( src == dst )
	return;

    sptr = (void __far*)0;
    dptr = (void __far*)0;

    done = False;
    for( chain=Database->clist; chain && !done; chain=chain->cnext )
	for( group=chain->glist; group && !done; group=group->gnext )
	    for( aptr=group->alist; aptr; aptr=aptr->anext )
	    {   if( aptr->serno == src )
		{   sptr = aptr;
		    if( dptr )
		    {   done = True;
			break;
		    }
		} else if( aptr->serno == dst )
		{   dptr = aptr;
		    if( sptr )
		    {   done = True;
			break;
		    }
		}
	    }


    /* Both found! */
    if( done ) 
    {   /* Reset Non-bonded flags! */
	sptr->flag &= ~NonBondFlag;
	dptr->flag &= ~NonBondFlag;

	bptr = ProcessBond( sptr, dptr, flag );
	bptr->bnext = CurMolecule->blist;
	CurMolecule->blist = bptr;
	InfoBondCount++;
    }
}


static void PDBConnectAtoms( src, dst )
    int src, dst;
{
    register Bond __far *bptr;
    register int bs,bd;

    ForEachBond
    {   bs = bptr->srcatom->serno;
	bd = bptr->dstatom->serno;

	if( ((bs==src)&&(bd==dst)) || ((bs==dst)&&(bd==src)) )
	{   if( bptr->flag & NormBondFlag )
	    {  /* Convert Single to Double */
	       bptr->flag &= ~(NormBondFlag);
	       bptr->flag |= DoubBondFlag;
	    } else if( bptr->flag & DoubBondFlag )
	    {  /* Convert Double to Triple */
	       bptr->flag &= ~(DoubBondFlag);
	       bptr->flag |= TripBondFlag;
	    }
	    return;
	}
    }

    ConnectAtoms( src, dst, NormBondFlag );
}



static void ProcessPDBGroup( heta, serno )
    int heta, serno;
{
    PDBInsert = Record[27];
    if( !CurChain || (CurChain->ident!=Record[22]) )
	CreateChain( Record[22] );
    CreateGroup( GroupPool );

    CurGroup->refno = FindResNo( &Record[18] );
    CurGroup->serno = serno;

    /* Solvents should be hetero! */
    if( IsSolvent(CurGroup->refno) )
	heta = True;

    if( heta )
    {   HetaGroupCount++;
	if( HMinMaxFlag )
	{   if( serno > MaxHetaRes ) 
	    {   MaxHetaRes = serno;
	    } else if( serno < MinHetaRes )
		MinHetaRes = serno;
	} else MinHetaRes = MaxHetaRes = serno;
    } else 
    {   MainGroupCount++;
	if( MMinMaxFlag )
	{   if( serno > MaxMainRes )
	    {   MaxMainRes = serno;
	    } else if( serno < MinMainRes )
		MinMainRes = serno;
	} else MinMainRes = MaxMainRes = serno; 
    }
}


static void ProcessPDBAtom( heta )
    int heta;
{
    auto char name[4];
    register Bond __far *bptr;
    register Atom __far *ptr;
    register Long dx,dy,dz;
    register int serno;
    register int refno;
    register int i;

    /* Ignore Pseudo Atoms!! */
    if( (Record[13]==' ') && (Record[14]=='Q') )
	return; 

    dx = ReadPDBCoord(31);
    dy = ReadPDBCoord(39);
    dz = ReadPDBCoord(47);

    /* Ignore XPLOR Pseudo Atoms!! */
    if( (dx==9999000L) && (dy==9999000L) && (dz==9999000L) )
	return;

    serno = (int)ReadValue(23,4);
    if( !CurGroup || (CurGroup->serno!=serno) 
	|| (CurChain->ident!=Record[22]) 
	|| (PDBInsert!=Record[27]) )
	ProcessPDBGroup( heta, serno );

    /* Solvents should be hetero! */
    if( IsSolvent(CurGroup->refno) )
	heta = True;


    ptr = CreateAtom();
    if( isdigit(Record[14]) )
    {   name[1] = ToUpper(Record[13]);
	name[2] = name[3] = ' ';
	name[0] = ' ';

    } else for( i=0; i<4; i++ )
	name[i] = ToUpper(Record[i+13]);

    for( refno=0; refnorefno = refno;
    ptr->altl = Record[17];
    ptr->serno = (int)ReadValue(7,5);
    ptr->temp = (int)ReadValue(61,6);

    ptr->xorg =  dx/4;
    ptr->yorg =  dy/4;
    ptr->zorg = -dz/4;

    if( heta ) 
	ptr->flag |= HeteroFlag;

    ProcessAtom( ptr );
    if( IsAlphaCarbon(refno) && IsProtein(CurGroup->refno) )
    {   if( ConnectAtom )
	{   dx = ConnectAtom->xorg - ptr->xorg;
	    dy = ConnectAtom->yorg - ptr->yorg;
	    dz = ConnectAtom->zorg - ptr->zorg;

	    /* Break backbone if CA-CA > 7.00A */
	    if( dx*dx+dy*dy+dz*dz < (Long)1750*1750 )
	    {   bptr = ProcessBond(ptr,ConnectAtom,NormBondFlag);
		bptr->bnext = CurChain->blist;
		CurChain->blist = bptr;
	    } else ptr->flag |= BreakFlag;
	}
	ConnectAtom = ptr;
    } else if( IsSugarPhosphate(refno) && IsNucleo(CurGroup->refno) )
    {   if( ConnectAtom )
	{   bptr = ProcessBond(ConnectAtom,ptr,NormBondFlag);
	    bptr->bnext = CurChain->blist;
	    CurChain->blist = bptr;
	}
	ConnectAtom = ptr;
    }
}


static FeatEntry __far *AllocFeature()
{
    register Feature __far *ptr;

    if( !FeatList || (FeatList->count==FeatSize) )
    {   ptr = (Feature __far*)_fmalloc(sizeof(Feature));
	if( !ptr ) FatalDataError("Memory allocation failed");
	/* Features are always deallocated! */
	
	ptr->fnext = FeatList;
	ptr->count = 0;
	FeatList = ptr;
    } else ptr = FeatList;

    return( &(ptr->data[ptr->count++]) );
}


#ifdef FUNCPROTO
static void UpdateFeature( FeatEntry __far*, int );
#endif

static void UpdateFeature( ptr, mask )
    FeatEntry __far *ptr;  int mask;
{
    register Chain __far *chain;
    register Group __far *group;

    for( chain=Database->clist; chain; chain=chain->cnext )
	if( chain->ident == ptr->chain )
	{   group=chain->glist;
	    while( group && (group->sernoinit) )
		group = group->gnext;

	    while( group && (group->serno<=ptr->term) )
	    {   group->struc |= mask;
		group = group->gnext;
	    }
	    return;
	}
}


static void ProcessPDBFeatures()
{
    register Feature __far *next;
    register Feature __far *ptr;
    register int i;

    InfoTurnCount = 0;
    InfoHelixCount = 0;
    InfoLadderCount = 0;
    InfoStrucSource = SourcePDB;

    for( ptr=FeatList; ptr; ptr=next )
    {    if( Database )
	     for( i=0; icount; i++ )
		 if( ptr->data[i].type==FeatHelix )
		 {   UpdateFeature( &ptr->data[i], HelixFlag );
		     InfoHelixCount++;
		 } else if( ptr->data[i].type==FeatSheet )
		 {   UpdateFeature( &ptr->data[i], SheetFlag );
		     InfoLadderCount++;
		 } else /* FeatTurn */
		 {   UpdateFeature( &ptr->data[i], TurnFlag );
		     InfoTurnCount++;
		 }

	 /* Deallocate Memory */
	 next = ptr->fnext;
	 _ffree( ptr );
    }
}


int LoadPDBMolecule( fp )
    FILE *fp;
{
    register FeatEntry __far *ptr;
    register int srcatm, dstatm;
    register char *src, *dst;
    register int model;

    model = True;
    FeatList = (void __far*)0;
    DataFile = fp;


    do {   
	FetchRecord();

	if( !strncmp("ATOM",Record+1,4) )
	{   if( model ) ProcessPDBAtom(False);
	} else if( !strncmp("HETA",Record+1,4) )
	{   if( model ) ProcessPDBAtom(True);
	} else if( !strncmp("SHEE",Record+1,4) )
	{   if( !model ) continue;
	
	    /* Remaining SHEET record fields   */
	    /* 39-40 .... Strand Parallelism   */
	    /* 33 ....... Same Chain as 22?    */
	    ptr = AllocFeature();
	    ptr->type = FeatSheet;
	    ptr->chain = Record[22];
	    ptr->init = (int)ReadValue(23,4);
	    ptr->term = (int)ReadValue(34,4);
       
	} else if( !strncmp("HELI",Record+1,4) )
	{   if( !model ) continue;
	
	    /* Remaining HELIX record fields   */
	    /* 39-40 .... Helix Classification */
	    /* 32 ....... Same Chain as 20?    */
	    ptr = AllocFeature();
	    ptr->type = FeatHelix;
	    ptr->chain = Record[20];
	    ptr->init = (int)ReadValue(22,4);
	    ptr->term = (int)ReadValue(34,4);

	} else if( !strncmp("TURN",Record+1,4) )
	{   if( !model ) continue;
	
	    ptr = AllocFeature();
	    ptr->type = FeatTurn;
	    ptr->chain = Record[20];
	    ptr->init = (int)ReadValue(21,4);
	    ptr->term = (int)ReadValue(32,4);

	} else if( !strncmp("CONE",Record+1,4) )
	{   if( !model ) continue;

	    if( (srcatm = (int)ReadValue(7,5)) )
	    {   if( (dstatm = (int)ReadValue(12,5)) )
		    if( dstatm > srcatm )
			PDBConnectAtoms( srcatm, dstatm );

		if( !Record[17] ) continue;
		if( (dstatm = (int)ReadValue(17,5)) )
		    if( dstatm > srcatm )
			PDBConnectAtoms( srcatm, dstatm );

		if( !Record[22] ) continue;
		if( (dstatm = (int)ReadValue(22,5)) )
		    if( dstatm > srcatm )
			PDBConnectAtoms( srcatm, dstatm );

		if( !Record[27] ) continue;
		if( (dstatm = (int)ReadValue(27,5)) )
		    if( dstatm > srcatm )
			PDBConnectAtoms( srcatm, dstatm );
	    }

	} else if( !strncmp("COLO",Record+1,4) )
	{   ProcessPDBColourMask();
	} else if( !strncmp("TER",Record+1,3) )
	{   if( !Record[4] || (Record[4]==' ') )
	    {   ConnectAtom = (void __far*)0;
		CurGroup = (void __far*)0;
		CurChain = (void __far*)0;
	    }
	} else if( !strncmp("HEAD",Record+1,4) )
	{   ExtractString(40,Record+11,InfoClassification);
	    ExtractString( 4,Record+63,InfoIdentCode);
	    
	} else if( !strncmp("COMP",Record+1,4) )
	{   if( Record[10]==' ' )  /* First COMPND record */
		ExtractString(60,Record+11,InfoMoleculeName);

	} else if( !strncmp("CRYS",Record+1,4) )
	{   dst = InfoSpaceGroup;
	    for( src=Record+56; *src && srcrefno = FindResNo( "MOL" );
    CurGroup->serno = 1;
	
    MinMainRes = MaxMainRes = 1;
    MinHetaRes = MaxHetaRes = 0;
    MainGroupCount = 1;
}


int LoadXYZMolecule( fp )
    FILE *fp;
{
    auto char type[12];
    auto double xpos, ypos, zpos;
    auto double charge, u, v, w;
    auto int atoms;

    register Atom __far *ptr;
    register char *src,*dst;
    register int i,count;


    DataFile = fp;

    /* Number of Atoms */
    FetchRecord();
    sscanf(Record+1,"%d",&atoms);

    /* Molecule (step) Description */
    FetchRecord();
    src = Record+1;
    while( *src == ' ' )
	src++;

    dst = InfoMoleculeName;
    for( i=0; i<78; i++ )
	if( *src ) *dst++ = *src++;
    *dst = '\0';

    if( atoms )
    {   CreateMolGroup();
	for( i=0; iserno = i;

	    xpos = ypos = zpos = 0.0;
	    count = sscanf(Record+1,"%s %lg %lg %lg %lg %lg %lg %lg",
			   type, &xpos, &ypos, &zpos, &charge, &u, &v, &w );

	    ptr->refno = SimpleAtomType(type);
	    ptr->xorg =  (Long)(250.0*xpos);
	    ptr->yorg =  (Long)(250.0*ypos);
	    ptr->zorg = -(Long)(250.0*zpos);

	    if( (count==5) || (count==8) )
	    {   ptr->temp = (short)(100.0*charge);
	    } else ptr->temp = 0;
	    ProcessAtom( ptr );
	}
    }
    return( True );
}


static int FindSybylResNo( ptr )
    char *ptr;
{
    register char *src,*dst;
    register int i, j;
    auto char name[4];

    src = ptr;
    dst = name;
    if( ptr[1] && (ptr[1]!='.') )
    {   *dst++ = ToUpper(*src);  src++;
	*dst++ = ToUpper(*src);  src++;
    } else
    {   *dst++ = ToUpper(*src);  src++;
	*dst++ = ' ';
    }

    if( *src )
    {   src++;

	if( *src == 'a' )
	{   *dst++ = ' ';
	    *dst = ' ';
	} else if( *src == 'p' )
	{   *dst++ = '3';
	    *dst = ' ';
	} else
	{   *dst++ = *src++;
	    if( *src && (*src!='+') )
	    {   *dst = *src;
	    } else *dst = ' ';
	}
    } else
    {   *dst++ = ' ';
	*dst = ' ';
    }

    for( j=0; jMOLECULE",Record+1,17) )
	{   FetchRecord();  /* Molecule Name */
	    src = Record+1;
	    while( *src==' ' )
		src++;

	    dst = InfoMoleculeName;
	    while( (*dst++ = *src++) );

	    FetchRecord();
	    atoms = bonds = structs = features = sets = 0;
	    sscanf(Record+1,"%d %d %d %d %d", &atoms, &bonds, &structs,
					      &features, &sets );

	    FetchRecord();  /* Molecule Type  */
	    FetchRecord();  /* Charge Type    */

	} else if( !strncmp("@ATOM",Record+1,13) )
	{   if( !atoms ) continue;

	    CreateMolGroup();
	    for( i=0; irefno = FindSybylResNo( type );
		 ptr->serno = serno;
		 /* ptr->serno = i; */

		 ptr->xorg =  (Long)(250.0*xpos);
		 ptr->yorg =  (Long)(250.0*ypos);
		 ptr->zorg = -(Long)(250.0*zpos);
		 ProcessAtom( ptr );
	    }

	} else if( !strncmp("@BOND",Record+1,13) )
	    for( i=0; irefno = FindAlchemyResNo();
	ptr->temp = (int)ReadValue(41,8);
	ptr->serno = (int)ReadValue(1,5);
	/* ptr->serno = i+1; */

	ptr->xorg =  ReadValue(13,7)/4;
	ptr->yorg =  ReadValue(22,7)/4;
	ptr->zorg = -ReadValue(31,7)/4;
	ProcessAtom( ptr );
    }

    for( i=0; iserno!=resno) )
	{   CreateGroup( GroupPool );
	    CurGroup->refno = FindResNo(Record+12);
	    CurGroup->serno = resno;

	    if( !init )
	    {   MinMainRes = resno;
		MaxMainRes = resno;
		init = True;
	    } else if( resno > MaxMainRes )
	    {   MaxMainRes = resno;
	    } else if( resno < MinMainRes )
		MinMainRes = resno;
	    MainGroupCount++;
	}

	ptr = CreateAtom();
	for( refno=0; refnorefno = refno;
	ptr->temp = (int)ReadValue(61,9);
	ptr->serno = (int)ReadValue(1,5);
	/* ptr->serno = serno+1; */

	ptr->xorg =  ReadValue(21,8)/4;
	ptr->yorg =  ReadValue(31,8)/4;
	ptr->zorg = -ReadValue(41,8)/4;
	ProcessAtom( ptr );
    }
    return( True );
}


int LoadMDLMolecule( fp )
    FILE *fp;
{
    register Bond __far *bptr;
    register Atom __far *src;
    register Atom __far *dst;
    register Atom __far *ptr;

    register int atoms, bonds;
    register int srcatm,dstatm;
    register int i,type,done;
    register Long dx, dy, dz;
    register Card dist2;
    register Real scale;

    DataFile = fp;

    FetchRecord(); /* Molecule Name */
    ExtractString(78,Record+1,InfoMoleculeName);

    FetchRecord(); /* Program Details */
    FetchRecord(); /* Comments */

    FetchRecord();
    atoms = (int)ReadValue(1,3);
    bonds = (int)ReadValue(4,3);

    if( !atoms )
	return( False );

    CreateMolGroup();
    for( i=1; i<=atoms; i++ )
    {   FetchRecord();
	ptr = CreateAtom();
	ptr->refno = SimpleAtomType(Record+32);

	switch( (int)ReadValue(37,3) )
	{   case(1):  ptr->temp =  300;  break;
	    case(2):  ptr->temp =  200;  break;
	    case(3):  ptr->temp =  100;  break;
	    case(5):  ptr->temp = -100;  break;
	    case(6):  ptr->temp = -200;  break;
	    case(7):  ptr->temp = -300;  break;
	    default:  ptr->temp = 0;
	}
	ptr->serno = i;

	ptr->xorg =  ReadValue(1,10)/40;
	ptr->yorg =  ReadValue(11,10)/40;
	ptr->zorg = -ReadValue(21,10)/40;
	ProcessAtom( ptr );
    }

    for( i=0; iblist; bptr; bptr=bptr->bnext )
	if( bptr->flag & NormBondFlag )
	{   src = bptr->srcatom;
	    dst = bptr->dstatom;
	    if( (src->refno==2) && (dst->refno==2) )
	    {   dx = dst->xorg - src->xorg;
		dy = dst->yorg - src->yorg;
		dz = dst->zorg - src->zorg;
		if( dx || dy || dz )
		{   dist2 = dx*dx + dy*dy + dz*dz;
		    scale = 385.0/sqrt(dist2);
		    break;
		}
	    }
	}

    if( bptr )
    {   for( ptr=CurGroup->alist; ptr; ptr=ptr->anext )
	{   ptr->xorg = (Long)(ptr->xorg*scale);
	    ptr->yorg = (Long)(ptr->yorg*scale);
	    ptr->zorg = (Long)(ptr->zorg*scale);
	}
	MinX = (Long)(MinX*scale);  MaxX = (Long)(MaxX*scale);
	MinY = (Long)(MinY*scale);  MaxY = (Long)(MaxY*scale);
	MinZ = (Long)(MinZ*scale);  MaxZ = (Long)(MaxZ*scale);
    }
    return( True );
}



int SavePDBMolecule( filename )
    char *filename;
{
    register double x, y, z;
    register Group __far *prev;
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register char *ptr;
    register int count;
    register char ch;
    register int i;

    if( !Database )
	return( False );

    DataFile = fopen( filename, "w" );
    if( !DataFile )
    {   if( CommandActive )
	    WriteChar('\n');
	WriteString("Error: Unable to create file!\n\n");
	CommandActive=False;
	return( False );
    }

    if( *InfoClassification || *InfoIdentCode )
    {   fputs("HEADER    ",DataFile);

	ptr = InfoClassification;
	for( i=11; i<=50; i++ )
	    putc( (*ptr ? *ptr++ : ' '), DataFile );
	fprintf(DataFile,"13-JUL-93   %.4s\n",InfoIdentCode);
    }

    if( *InfoMoleculeName )
	fprintf(DataFile,"COMPND    %.60s\n",InfoMoleculeName);

    prev = (void __far*)0;

    count = 1;
    ForEachAtom
	if( aptr->flag&SelectFlag )
	{   if( prev && (chain->ident!=ch) )
		fprintf( DataFile, "TER   %5d      %.3s %c%4d \n", 
			 count++, Residue[prev->refno], ch, prev->serno);

	    if( aptr->flag&HeteroFlag )
	    {      fputs("HETATM",DataFile);
	    } else fputs("ATOM  ",DataFile);
	    fprintf( DataFile, "%5d %.4s %.3s %c%4d    ",
		     count++, ElemDesc[aptr->refno], Residue[group->refno],
		     chain->ident, group->serno );

	    x = (double)aptr->xorg/250.0;
	    y = (double)aptr->yorg/250.0;
	    z = (double)aptr->zorg/250.0;

#ifdef INVERT
	    fprintf(DataFile,"%8.3f%8.3f%8.3f",x,-y,-z);
#else
	    fprintf(DataFile,"%8.3f%8.3f%8.3f",x,y,-z);
#endif
	    fprintf(DataFile,"  1.00%6.2f\n",aptr->temp/100.0);
	    
	    ch = chain->ident;
	    prev = group;
	}

    if( prev )
	fprintf( DataFile, "TER   %5d      %.3s %c%4d \n", 
		 count, Residue[prev->refno], ch, prev->serno);

    fputs("END   \n",DataFile);
    fclose( DataFile );
#ifdef APPLEMAC
    SetFileInfo(filename,'RSML','TEXT',131);
#endif
    return( True );
}

int SaveXYZMolecule( filename )
    char *filename;
{
    return( True );
}


int SaveCIFMolecule( filename )
    char *filename;
{
    if( !filename )
	    return( False );
    return( True );
}


int SaveAlchemyMolecule( filename )
    char *filename;
{
    register Real x, y, z;
    register float xpos, ypos, zpos;
    register Chain __far *chain;
    register Group __far *group;
    register Atom __far *aptr;
    register Bond __far *bptr;
    register char *ptr;
    register int atomno;
    register int bondno;
    register int num;

    if( !Database )
	return( False );

    DataFile = fopen( filename, "w" );
    if( !DataFile )
    {   if( CommandActive )
	    WriteChar('\n');
	WriteString("Error: Unable to create file!\n\n");
	CommandActive=False;
	return( False );
    }

    atomno = 0;
    ForEachAtom
	if( aptr->flag & SelectFlag )
	{   aptr->mbox = 0;
	    atomno++;
	}

    bondno = 0;
    ForEachBond
	if( (bptr->srcatom->flag&bptr->dstatom->flag) & SelectFlag )
	{   if( bptr->flag&AromBondFlag )
	    {   bptr->srcatom->mbox = -1;
		bptr->dstatom->mbox = -1;
	    } else if( !(bptr->flag&HydrBondFlag) )
	    {   num = (bptr->flag&DoubBondFlag)? 2 : 1;
		if( bptr->srcatom->mbox>0 ) 
		    bptr->srcatom->mbox += num;
		if( bptr->dstatom->mbox>0 ) 
		    bptr->dstatom->mbox += num;
	    }
	    bondno++;
	}

    fprintf(DataFile,"%5d ATOMS, %5d BONDS, ",atomno,bondno);
    fprintf(DataFile,"    0 CHARGES, %s\n", InfoMoleculeName );

    atomno = 1;
    ForEachAtom
	if( aptr->flag & SelectFlag )
	{   aptr->mbox = atomno;
	    fprintf(DataFile,"%5d ",atomno++);

	    switch( GetElemNumber(aptr) )
	    {   case( 6 ):  if( aptr->mbox == -1 )
			    {   ptr = "CAR ";
			    } else if( aptr->mbox == 1 )
			    {   ptr = "C3  ";
			    } else ptr = "C2  ";
			    fputs( ptr, DataFile );
			    break;

		case( 7 ):  if( aptr->mbox == -1 )
			    {   ptr = "NAR ";
			    } else ptr = "N2  ";
			    fputs( ptr, DataFile );
			    break;

		case( 8 ):  if( aptr->mbox == 2 )
			    {   ptr = "O2  ";
			    } else ptr = "O3  ";
			    fputs( ptr, DataFile );
			    break;

		case( 1 ):  fputs( "H   ", DataFile );  break;

		default:    ptr = ElemDesc[aptr->refno];
			    if( *ptr==' ' )
			    {   fprintf(DataFile,"%.3s ",ptr+1);
			    } else fprintf(DataFile,"%.4s",ptr);
	    }

	    x = aptr->xorg/250.0;
	    y = aptr->yorg/250.0;
	    z = aptr->zorg/250.0;

	    /* Apply Current Viewpoint Rotation Matrix */
	    xpos = (float)(x*RotX[0] + y*RotX[1] + z*RotX[2]);
	    ypos = (float)(x*RotY[0] + y*RotY[1] + z*RotY[2]);
	    zpos = (float)(x*RotZ[0] + y*RotZ[1] + z*RotZ[2]);

#ifdef INVERT
	    fprintf(DataFile,"  %8.4f %8.4f %8.4f",xpos,-ypos,-zpos);
#else
	    fprintf(DataFile,"  %8.4f %8.4f %8.4f",xpos,ypos,-zpos);
#endif
	    fprintf(DataFile,"    %7.4f\n",aptr->temp/1000.0);
	}

    bondno = 1;
    ForEachBond
	if( (bptr->srcatom->flag&bptr->dstatom->flag) & SelectFlag )
	{   fprintf(DataFile,"%5d %5d %5d  ", bondno++,
			bptr->srcatom->mbox, bptr->dstatom->mbox );
	    if( bptr->flag & AromBondFlag )
	    {   ptr = "AROMATIC\n";
	    } else if( bptr->flag & TripBondFlag )
	    {   ptr = "TRIPLE\n";
	    } else if( bptr->flag & DoubBondFlag )
	    {   ptr = "DOUBLE\n";
	    } else ptr = "SINGLE\n";
	    fputs( ptr, DataFile );
	}

    ForEachAtom
	    if( aptr->flag & SelectFlag )
	        aptr->mbox = 0;
    fclose( DataFile );
#ifdef APPLEMAC
    SetFileInfo(filename,'RSML','TEXT',131);
#endif
    return( True );
}


static void TestBonded( sptr, dptr, flag )
    Atom __far *sptr, __far *dptr; 
    int flag;
{
    register Bond __far *bptr;
    register Long dx, dy, dz;
    register Long max, dist;

    if( flag )
    {    /* Sum of covalent radii with 0.5A tolerance */
         dist = Element[GetElemNumber(sptr)].covalrad + 
                Element[GetElemNumber(dptr)].covalrad + 125;
         max = dist*dist;  
    } else 
    {    /* Fast Bio-Macromolecule Bonding Calculation */
         if( (sptr->flag|dptr->flag) & HydrogenFlag )
	 {      max = MaxHBondDist;
         } else max = MaxBondDist;
    }

    dx = sptr->xorg-dptr->xorg;   if( (dist=dx*dx)>max ) return;
    dy = sptr->yorg-dptr->yorg;   if( (dist+=dy*dy)>max ) return;
    dz = sptr->zorg-dptr->zorg;   if( (dist+=dz*dz)>max ) return;

    if( dist > MinBondDist )
    {   /* Reset Non-bonded flags! */
	sptr->flag &= ~NonBondFlag;
	dptr->flag &= ~NonBondFlag;

        if( (sptr->flag|dptr->flag) & HydrogenFlag )
        {      flag = HydrBondFlag;
        } else flag = NormBondFlag;
	bptr = ProcessBond(sptr,dptr,flag);
	bptr->bnext = CurMolecule->blist;
	CurMolecule->blist = bptr;
	InfoBondCount++;
    }
}


static void ReclaimHBonds( ptr )
    HBond __far *ptr;
{
    register HBond __far *temp;

    if( (temp = ptr) )
    {   while( temp->hnext )
	    temp=temp->hnext;
	temp->hnext = FreeHBond;
	FreeHBond = ptr;
    }
}


static void ReclaimBonds( ptr )
    Bond __far *ptr;
{
    register Bond __far *temp;

    if( (temp = ptr) )
    {   while( temp->bnext )
	    temp=temp->bnext;
	temp->bnext = FreeBond;
	FreeBond = ptr;
    }
}


void CreateMoleculeBonds( info, flag )
    int info, flag;
{
    register int i, x, y, z;
    register Long tx, ty, tz;
    register Long mx, my, mz; 
    register Long dx, dy, dz;
    register int lx, ly, lz, hx, hy, hz;
    register Atom __far *aptr, __far *dptr;
    register Chain __far *chain;
    register Group __far *group;
    char buffer[40];


    if( !Database ) 
	return;

    dx = (MaxX-MinX)+1;
    dy = (MaxY-MinY)+1;
    dz = (MaxZ-MinZ)+1;

    InfoBondCount = 0;
    ReclaimBonds( CurMolecule->blist );
    CurMolecule->blist = (void __far*)0;
    ResetVoxelData();

    for( chain=Database->clist; chain; chain=chain->cnext )
    {   ResetVoxelData();
	for( group=chain->glist; group; group=group->gnext )
	    for( aptr=group->alist; aptr; aptr=aptr->anext )
	    {   /* Initially non-bonded! */
		aptr->flag |= NonBondFlag;

		mx = aptr->xorg-MinX;
		my = aptr->yorg-MinY;
		mz = aptr->zorg-MinZ;

		tx = mx-AbsMaxBondDist;  
		ty = my-AbsMaxBondDist;  
		tz = mz-AbsMaxBondDist;  

		lx = (tx>0)? (int)((VOXORDER*tx)/dx) : 0;
		ly = (ty>0)? (int)((VOXORDER*ty)/dy) : 0;
		lz = (tz>0)? (int)((VOXORDER*tz)/dz) : 0;

		tx = mx+AbsMaxBondDist;  
		ty = my+AbsMaxBondDist;  
		tz = mz+AbsMaxBondDist;

		hx = (txnext) );
			i += VOXORDER;
		    }
		}
		
		x = (int)((VOXORDER*mx)/dx);
		y = (int)((VOXORDER*my)/dy);
		z = (int)((VOXORDER*mz)/dz);

		i = VOXORDER2*x + VOXORDER*y + z;
		aptr->next = (Atom __far*)HashTable[i];
		HashTable[i] = (void __far*)aptr;
	    }
	VoxelsClean = False;
    }

    if( info )
    {   if( CommandActive )
	    WriteChar('\n');
	CommandActive=False;
	sprintf(buffer,"Number of Bonds ..... %ld\n\n",InfoBondCount);
	WriteString(buffer);
    }
}


Atom __far *FindGroupAtom( group, n )
    Group __far *group;  Byte n;
{
    register Atom __far *ptr;

    ptr = group->alist;
    while( ptr )
    {   if( ptr->refno == n )
	    return( ptr );
	ptr = ptr->anext;
    }
    return( (Atom __far*)0 );
}


void TestDisulphideBridge( group1, group2, cys1 )
    Group __far *group1, __far *group2;  
    Atom __far *cys1;
{
    register HBond __far *ptr;
    register Atom __far *cys2;
    register int dx, dy, dz;
    register Long max,dist;

    if( !(cys2=FindGroupAtom(group2,20)) )
	return;

    max = (Long)750*750;
    dx = (int)(cys1->xorg-cys2->xorg);   if( (dist=(Long)dx*dx)>max ) return;
    dy = (int)(cys1->yorg-cys2->yorg);   if( (dist+=(Long)dy*dy)>max ) return;
    dz = (int)(cys1->zorg-cys2->zorg);   if( (dist+=(Long)dz*dz)>max ) return;

    if( !(ptr = FreeHBond) )
    {   MemSize += sizeof(HBond);
	ptr = (HBond __far*)_fmalloc(sizeof(HBond));
	if( !ptr ) FatalDataError("Memory allocation failed");
	RegisterAlloc( ptr );
    } else FreeHBond = ptr->hnext;

    ptr->dst = cys1;
    if( !(ptr->dstCA=FindGroupAtom(group1,1)) )
	ptr->dstCA = cys1;

    ptr->src = cys2;
    if( !(ptr->srcCA=FindGroupAtom(group2,1)) )
	ptr->srcCA = cys2;

    ptr->offset = 0;
    ptr->energy = 0;
    ptr->flag = 0;
    ptr->col = 0;

    ptr->hnext = CurMolecule->slist;
    CurMolecule->slist = ptr;

    group1->flag |= CystineFlag;
    group2->flag |= CystineFlag;
    InfoSSBondCount++;
}


void FindDisulphideBridges()
{
    register Chain __far *chn1;
    register Chain __far *chn2;
    register Group __far *group1;
    register Group __far *group2;
    register Atom __far *cys;
    char buffer[40];

    if( !Database ) return;
    ReclaimHBonds( CurMolecule->slist );
    InfoSSBondCount = 0;

    for(chn1=Database->clist;chn1;chn1=chn1->cnext)
	for(group1=chn1->glist;group1;group1=group1->gnext)
	    if( IsCysteine(group1->refno) && (cys=FindGroupAtom(group1,20)) )
	    {   for(group2=group1->gnext;group2;group2=group2->gnext)
		    if( IsCysteine(group2->refno) )
			TestDisulphideBridge(group1,group2,cys);

		for(chn2=chn1->cnext;chn2;chn2=chn2->cnext)
		    for(group2=chn2->glist;group2;group2=group2->gnext)
			if( IsCysteine(group2->refno) )
			    TestDisulphideBridge(group1,group2,cys);
	    }

    if( CommandActive )
	WriteChar('\n');
    CommandActive=False;
    
    sprintf(buffer,"Number of Bridges ... %d\n\n",InfoSSBondCount);
    WriteString(buffer);
}


#ifdef FUNCPROTO
static void CreateHydrogenBond( Atom __far*, Atom __far*,
				Atom __far*, Atom __far*, int, int );
static int IsHBonded( Atom __far*, Atom __far*, HBond __far* );
static void TestLadder( Chain __far* );
#endif


static void CreateHydrogenBond( srcCA, dstCA, src, dst, energy, offset )
    Atom __far *srcCA, __far *dstCA;
    Atom __far *src, __far *dst;
    int energy, offset;
{
    register HBond __far *ptr;
    register int i,flag;

    if( !(ptr = FreeHBond) )
    {   MemSize += HBondPool*sizeof(HBond);
	ptr = (HBond __far *)_fmalloc( HBondPool*sizeof(HBond) );
	if( !ptr ) FatalDataError("Memory allocation failed");
	RegisterAlloc( ptr );
	for( i=1; ihnext = FreeHBond;
	    FreeHBond = ptr++;
	} 
    } else FreeHBond = ptr->hnext;

    if( (offset>=-128) && (offset<127) )
    {   ptr->offset = (Char)offset;
    } else ptr->offset = 0;

    flag = ZoneBoth? src->flag&dst->flag : src->flag|dst->flag;
    ptr->flag = flag & SelectFlag;

    ptr->src = src;
    ptr->dst = dst;
    ptr->srcCA = srcCA;
    ptr->dstCA = dstCA;
    ptr->energy = energy;
    ptr->col = 0;

    *CurHBond = ptr;
    ptr->hnext = (void __far*)0;
    CurHBond = &ptr->hnext;
    InfoHBondCount++;
}

/* Coupling constant for Electrostatic Energy   */
/* QConst = -332 * 0.42 * 0.2 * 1000.0 [*250.0] */
#define QConst (-6972000.0)
#define MaxHDist ((Long)2250*2250)
#define MinHDist ((Long)125*125)


/* Protein Donor Atom Coordinates */
static int hxorg,hyorg,hzorg;
static int nxorg,nyorg,nzorg;
static Atom __far *best1CA;
static Atom __far *best2CA;
static Atom __far *best1;
static Atom __far *best2;
static Atom __far *optr;
static int res1,res2;
static int off1,off2;


static int CalculateBondEnergy( group )
    Group __far *group;
{
    register double dho,dhc;
    register double dnc,dno;

    register Atom __far *cptr;
    register Long dx,dy,dz;
    register Long dist;
    register int result;

    if( !(cptr=FindGroupAtom(group,2)) )  return(0);
    if( !(optr=FindGroupAtom(group,3)) )  return(0);

    dx = hxorg-optr->xorg;  
    dy = hyorg-optr->yorg;  
    dz = hzorg-optr->zorg;
    dist = dx*dx+dy*dy+dz*dz;
    if( dist < MinHDist ) 
	return( -9900 );
    dho = sqrt((double)dist);

    dx = hxorg-cptr->xorg;  
    dy = hyorg-cptr->yorg;  
    dz = hzorg-cptr->zorg;
    dist = dx*dx+dy*dy+dz*dz;
    if( dist < MinHDist ) 
	return( -9900 );
    dhc = sqrt((double)dist);

    dx = nxorg-cptr->xorg;  
    dy = nyorg-cptr->yorg;  
    dz = nzorg-cptr->zorg;
    dist = dx*dx+dy*dy+dz*dz;
    if( dist < MinHDist ) 
	return( -9900 );
    dnc = sqrt((double)dist);

    dx = nxorg-optr->xorg;  
    dy = nyorg-optr->yorg;  
    dz = nzorg-optr->zorg;
    dist = dx*dx+dy*dy+dz*dz;
    if( dist < MinHDist ) 
	return( -9900 );
    dno = sqrt((double)dist);

    result = (int)(QConst/dho - QConst/dhc + QConst/dnc - QConst/dno);

    if( result<-9900 ) 
    {   return( -9900 );
    } else if( result>-500 ) 
	return( 0 );

    return( result );

}


void CalcHydrogenBonds()
{
    register int energy, offset, refno;
    register Chain __far *chn1, __far *chn2;
    register Group __far *group1;
    register Group __far *group2;
    register Group __far *best;
    register Atom __far *ca1;
    register Atom __far *ca2;
    register Atom __far *pc1;
    register Atom __far *po1;
    register Atom __far *n1;
    register Long max,dist;
    register int pos1,pos2;
    register int dx,dy,dz;
    register double dco;
    char buffer[40];

    if( !Database ) return;
    ReclaimHBonds( CurMolecule->hlist );
    CurMolecule->hlist = (void __far*)0;
    CurHBond = &CurMolecule->hlist;
    InfoHBondCount = 0;

    if( MainAtomCount > 10000 )
    {   if( CommandActive )
	    WriteChar('\n');
	WriteString("Please wait... ");
	CommandActive=True;
    }

    for(chn1=Database->clist;chn1;chn1=chn1->cnext)
    {   if( !chn1->glist ) continue;

	if( IsProtein(chn1->glist->refno) )
	{   pc1 = po1 = (void __far*)0;
	    pos1 = 0;
	    for(group1=chn1->glist;group1;group1=group1->gnext)
	    {   pos1++;
		if( pc1 && po1 )
		{   dx = (int)(pc1->xorg - po1->xorg);
		    dy = (int)(pc1->yorg - po1->yorg);
		    dz = (int)(pc1->zorg - po1->zorg);
		} else
		{   pc1 = FindGroupAtom(group1,2);
		    po1 = FindGroupAtom(group1,3);
		    continue;
		}

		pc1 = FindGroupAtom(group1,2);
		po1 = FindGroupAtom(group1,3);

		if( !IsAmino(group1->refno) || IsProline(group1->refno) )
		    continue;

		if( !(ca1=FindGroupAtom(group1,1)) ) continue;
		if( !(n1=FindGroupAtom(group1,0)) )  continue;

		dist = (Long)dx*dx + (Long)dy*dy + (Long)dz*dz;
		dco = sqrt( (double)dist )/250.0;

		nxorg = (int)n1->xorg;   hxorg = nxorg + (int)(dx/dco);
		nyorg = (int)n1->yorg;   hyorg = nyorg + (int)(dy/dco);
		nzorg = (int)n1->zorg;   hzorg = nzorg + (int)(dz/dco);
		res1 = res2 = 0;

		/* Only Hydrogen Bond within a single chain!       */
		/* for(chn2=Database->clist;chn2;chn2=chn2->cnext) */

		chn2 = chn1;
		{   /* Only consider non-empty peptide chains! */
		    if( !chn2->glist || !IsProtein(chn2->glist->refno) )
			continue;

		    pos2 = 0;
		    for(group2=chn2->glist;group2;group2=group2->gnext)
		    {   pos2++;
			if( (group2==group1) || (group2->gnext==group1) )
			    continue;

			if( !IsAmino(group2->refno) ) 
			    continue;
			if( !(ca2=FindGroupAtom(group2,1)) ) 
			    continue;

			dx = (int)(ca1->xorg-ca2->xorg);
			if( (dist=(Long)dx*dx) > MaxHDist )
			    continue;

			dy = (int)(ca1->yorg-ca2->yorg);
			if( (dist+=(Long)dy*dy) > MaxHDist )
			    continue;

			dz = (int)(ca1->zorg-ca2->zorg);
			if( (dist+=(Long)dz*dz) > MaxHDist )
			    continue;

			if( (energy = CalculateBondEnergy(group2)) )
			{   if( chn1 == chn2 )
			    {   offset = pos1 - pos2;
			    } else offset = 0;

			    if( energyglist->refno) )
	    for(group1=chn1->glist;group1;group1=group1->gnext)
	    {   if( !IsPurine(group1->refno) ) continue;
		/* Find N1 of Purine Group */
		if( !(n1=FindGroupAtom(group1,21)) )
		    continue;

		/* Maximum N1-N3 distance 5A */
		refno = NucleicCompl(group1->refno);
		max = (Long)1250*1250;
		best = (void __far*)0;

		for(chn2=Database->clist;chn2;chn2=chn2->cnext)
		{   /* Only consider non-empty peptide chains! */
		    if( (chn1==chn2) || !chn2->glist || 
			!IsNucleo(chn2->glist->refno) )
			continue;

		    for(group2=chn2->glist;group2;group2=group2->gnext)
			if( group2->refno == (Byte)refno )
			{   /* Find N3 of Pyramidine Group */
			    if( !(ca1=FindGroupAtom(group2,23)) )
				continue;

			    dx = (int)(ca1->xorg - n1->xorg);
			    if( (dist=(Long)dx*dx) >= max ) 
				continue;

			    dy = (int)(ca1->yorg - n1->yorg);
			    if( (dist+=(Long)dy*dy) >= max ) 
				continue;

			    dz = (int)(ca1->zorg - n1->zorg);
			    if( (dist+=(Long)dz*dz) >= max )
				continue;

			    best1 = ca1;
			    best = group2;
			    max = dist;
			}
		}

		if( best )
		{   /* Find the sugar phosphorous atoms */
		    ca1 = FindGroupAtom( group1, 7 );
		    ca2 = FindGroupAtom( best, 7 );

		    CreateHydrogenBond( ca1, ca2, n1, best1, 0, 0 );
		    if( IsGuanine(group1->refno) )
		    {   /* Guanine-Cytosine */
			if( (ca1=FindGroupAtom(group1,22)) &&  /* G.N2 */
			    (ca2=FindGroupAtom(best,26)) )     /* C.O2 */
			    CreateHydrogenBond( (void __far*)0, (void __far*)0,
						ca1, ca2, 0, 0 );

			if( (ca1=FindGroupAtom(group1,28)) &&  /* G.O6 */
			    (ca2=FindGroupAtom(best,24)) )     /* C.N4 */
			    CreateHydrogenBond( (void __far*)0, (void __far*)0,
						ca1, ca2, 0, 0 );

		    } else /* Adenine-Thymine */
			if( (ca1=FindGroupAtom(group1,25)) &&  /* A.N6 */
			    (ca2=FindGroupAtom(best,27)) )     /* T.O4 */
			    CreateHydrogenBond( (void __far*)0, (void __far*)0,
						ca1, ca2, 0, 0 );
		}
	    }
    }

    if( CommandActive )
	WriteChar('\n');
    CommandActive=False;
    
    sprintf(buffer,"Number of H-Bonds ... %d\n",InfoHBondCount);
    WriteString(buffer);
}


static int IsHBonded( src, dst, ptr )
    Atom __far *src, __far *dst;
    HBond __far *ptr;
{
    while( ptr && (ptr->srcCA==src) )
	if( ptr->dstCA == dst )
	{   return( True );
	} else ptr=ptr->hnext;
    return( False );
}


static void FindAlphaHelix( pitch, flag )
    int pitch, flag;
{
    register HBond __far *hbond;
    register Chain __far *chain;
    register Group __far *group;
    register Group __far *first;
    register Group __far *ptr;
    register Atom __far *srcCA;
    register Atom __far *dstCA;
    register int res,dist,prev;

    /* Protein chains only! */
    hbond = Database->hlist;
    for( chain=Database->clist; chain; chain=chain->cnext )
    if( (first=chain->glist) && IsProtein(first->refno) )
    {   prev = False; dist = 0;
	for( group=chain->glist; hbond && group; group=group->gnext )
	{   if( IsAmino(group->refno) && (srcCA=FindGroupAtom(group,1)) )
	    {   if( dist==pitch )
		{   res = False;
		    dstCA=FindGroupAtom(first,1);

		    while( hbond && hbond->srcCA == srcCA )
		    {   if( hbond->dstCA==dstCA ) res=True;
			hbond = hbond->hnext;
		    }

		    if( res )
		    {   if( prev )
			{   if( !(first->struc&HelixFlag) ) 
				InfoHelixCount++;

			    ptr = first;
			    do {
				ptr->struc |= flag;
				ptr = ptr->gnext;
			    } while( ptr != group );
			} else prev = True;
		    } else prev = False;
		} else while( hbond && hbond->srcCA==srcCA )
		    hbond = hbond->hnext;
	    } else prev = False;

	    if( group->struc&HelixFlag )
	    {   first = group; prev = False; dist = 1;
	    } else if( dist==pitch )
	    {   first = first->gnext;
	    } else dist++;
	}
    } else if( first && IsNucleo(first->refno) )
	while( hbond && !IsAminoBackbone(hbond->src->refno) )
	    hbond = hbond->hnext;
}


static Atom __far *cprevi, __far *ccurri, __far *cnexti;
static HBond __far *hcurri, __far *hnexti;
static Group __far *curri, __far *nexti;



static void TestLadder( chain )
    Chain __far *chain;
{
    register Atom __far *cprevj, __far *ccurrj, __far *cnextj;
    register HBond __far *hcurrj, __far *hnextj;
    register Group __far *currj, __far *nextj;
    register int count, result, found;

    /* Already part of atleast one ladder */
    found = curri->flag & SheetFlag;
    nextj = nexti->gnext;

    hnextj = hnexti;
    while( hnextj && hnextj->srcCA==cnexti )
	hnextj = hnextj->hnext;

    while( True )
    {   if( nextj )
	    if( IsProtein(chain->glist->refno) )
	    {   count = 1;
		do {
		    cnextj = FindGroupAtom(nextj,1);
		    if( count == 3 )
		    {   if( IsHBonded(cnexti,ccurrj,hnexti) &&
			    IsHBonded(ccurrj,cprevi,hcurrj) )
			{   result = ParaLadder;
			} else if( IsHBonded(cnextj,ccurri,hnextj) &&
				   IsHBonded(ccurri,cprevj,hcurri) )
			{   result = ParaLadder;
			} else if( IsHBonded(cnexti,cprevj,hnexti) &&
				   IsHBonded(cnextj,cprevi,hnextj) )
			{   result = AntiLadder;
			} else if( IsHBonded(ccurrj,ccurri,hcurrj) &&
				   IsHBonded(ccurri,ccurrj,hcurri) )
			{   result = AntiLadder;
			} else result = NoLadder;

			if( result )
			{   curri->struc |= SheetFlag;
			    currj->struc |= SheetFlag;
			    if( found ) return;
			    found = True;
			}
		    } else count++;

		    cprevj = ccurrj; ccurrj = cnextj; 
		    currj = nextj;   hcurrj = hnextj;

		    while( hnextj && hnextj->srcCA==cnextj )
			hnextj = hnextj->hnext;
		} while( (nextj = nextj->gnext) );

	    } else if( IsNucleo(chain->glist->refno) )
		while( hnextj && !IsAminoBackbone(hnextj->src->refno) )
		    hnextj = hnextj->hnext;

	if( (chain = chain->cnext) ) 
	{   nextj = chain->glist;
	} else return;
    }
}


static void FindBetaSheets()
{
    register Chain __far *chain;
    register int ladder;
    register int count;

    hnexti = Database->hlist;
    for( chain=Database->clist; chain; chain=chain->cnext )
	if( (nexti = chain->glist) )
	    if( IsProtein(nexti->refno) )
	    {   count = 1;
		ladder = False;
		do {
		    cnexti = FindGroupAtom(nexti,1);

		    if( count == 3 )
		    {   TestLadder( chain );
			if( curri->struc & SheetFlag )
			{   if( !ladder )
			    {   InfoLadderCount++;
				ladder = True;
			    }
			} else ladder = False;
		    } else count++;

		    cprevi = ccurri; ccurri = cnexti; 
		    curri = nexti;   hcurri = hnexti;
		    while( hnexti && hnexti->srcCA==cnexti )
			hnexti = hnexti->hnext;
		} while( (nexti = nexti->gnext) );

	    } else if( IsNucleo(nexti->refno) )
		while( hnexti && !IsAminoBackbone(hnexti->src->refno) )
		    hnexti = hnexti->hnext;
}


static void FindTurnStructure()
{
    static Atom __far *aptr[5];
    register Chain __far *chain;
    register Group __far *group;
    register Group __far *prev;
    register Atom __far *ptr;
    register Long ux,uy,uz,mu;
    register Long vx,vy,vz,mv;
    register int i,found,len;
    register Real CosKappa;

    for( chain=Database->clist; chain; chain=chain->cnext )
	if( chain->glist && IsProtein(chain->glist->refno) )
	{   len = 0;  found = False;
	    for( group=chain->glist; group; group=group->gnext )
	    {    ptr = FindGroupAtom(group,1);
		 if( ptr && (ptr->flag&BreakFlag) )
		 {   found = False;
		     len = 0;
		 } else if( len==5 )
		 {   for( i=0; i<4; i++ )
			 aptr[i] = aptr[i+1];
		     len = 4;
		 } else if( len==2 )
		     prev = group;

		 aptr[len++] = ptr;
		 if( len==5 ) 
		 {   if( !(prev->struc&(HelixFlag|SheetFlag)) &&
			 aptr[0] && aptr[2] && aptr[4] )
		     {   ux = aptr[2]->xorg - aptr[0]->xorg;
			 uy = aptr[2]->yorg - aptr[0]->yorg;
			 uz = aptr[2]->zorg - aptr[0]->zorg;

			 vx = aptr[4]->xorg - aptr[2]->xorg;
			 vy = aptr[4]->yorg - aptr[2]->yorg;
			 vz = aptr[4]->zorg - aptr[2]->zorg;

			 mu = ux*ux + uy*uy + uz*uz;
			 mv = vx*vx + vz*vz + vy*vy;
			 if( mu && mv )
			 {   CosKappa = (Real)(ux*vx + uy*vy + uz*vz);
			     CosKappa /= sqrt( (Real)mu*mv );
			     if( CosKappastruc |= TurnFlag;
			     }
			 }
		     }
		     found = prev->struc&TurnFlag;
		     prev = prev->gnext;
		 } /* len==5 */
	    }
	}
}

void DetermineStructure()
{
    register Chain __far *chain;
    register Group __far *group;
    char buffer[40];

    if( !Database )
	return;

    if( InfoHBondCount<0 )
	CalcHydrogenBonds();

    if( InfoHelixCount>=0 )
	for( chain=Database->clist; chain; chain=chain->cnext )
	    for( group=chain->glist; group; group=group->gnext )
		group->struc = 0;

    InfoStrucSource = SourceCalc;
    InfoLadderCount = 0;
    InfoHelixCount = 0;
    InfoTurnCount = 0;

    if( InfoHBondCount )
    {   FindAlphaHelix(4,Helix4Flag);
	FindBetaSheets();
	FindAlphaHelix(3,Helix3Flag);
	FindAlphaHelix(5,Helix5Flag);
	FindTurnStructure();
    }

    if( CommandActive )
	WriteChar('\n');
    CommandActive=False;

    sprintf(buffer,"Number of Helices ... %d\n",InfoHelixCount);
    WriteString(buffer);
    sprintf(buffer,"Number of Strands ... %d\n",InfoLadderCount);
    WriteString(buffer);
    sprintf(buffer,"Number of Turns ..... %d\n",InfoTurnCount);
    WriteString(buffer);
}


void RenumberMolecule( start )
    int start;
{
    register Chain __far *chain;
    register Group __far *group;
    register int hinit, minit;
    register int resno;

    if( !Database )
	return;

    hinit = minit = False;
    for( chain=Database->clist; chain; chain=chain->cnext )
    {   resno = start;
	for( group=chain->glist; group; group=group->gnext )
	{   if( group->alist->flag & HeteroFlag )
	    {   if( hinit )
		{   if( resno > MaxHetaRes )
		    {   MaxHetaRes = resno;
		    } else if( resno < MinHetaRes )
			MinHetaRes = resno;
		} else MinHetaRes = MaxHetaRes = resno;
		hinit = True;
	    } else
	    {   if( minit )
		{   if( resno > MaxMainRes )
		    {   MaxMainRes = resno;
		    } else if( resno < MinMainRes )
			MinMainRes = resno;
		} else MinMainRes = MaxMainRes = resno;
		minit = True;
	    }
	    group->serno = resno++;
	}
    }
}


static void ReclaimAtoms( ptr )
    Atom __far *ptr;
{
    register Atom __far *temp;

    if( (temp = ptr) )
    {   while( temp->anext )
	    temp=temp->anext;
	temp->anext = FreeAtom;
	FreeAtom = ptr;
    }
}

static void ResetDatabase()
{
    
    Database = CurMolecule = (void __far*)0;
    MainGroupCount = HetaGroupCount = 0;
    InfoChainCount = HetaAtomCount = 0;
    MainAtomCount = InfoBondCount = 0;  
    SelectCount = 0;

    InfoStrucSource = SourceNone;
    InfoSSBondCount = InfoHBondCount = -1;
    InfoHelixCount = InfoLadderCount = -1;
    InfoTurnCount = -1;

    CurGroup = (void __far*)0;
    CurChain = (void __far*)0;
    CurAtom = (void __far*)0;

    MinX = MinY = MinZ = 0;
    MaxX = MaxY = MaxZ = 0;

    MinMainTemp = MaxMainTemp = 0;
    MinHetaTemp = MaxHetaTemp = 0;
    MinMainRes = MaxMainRes = 0;
    MinHetaRes = MaxHetaRes = 0;

    *InfoMoleculeName = 0;
    *InfoClassification = 0;
    *InfoIdentCode = 0;
    *InfoSpaceGroup = 0;
    *InfoFileName = 0;

    VoxelsClean = False;
    HMinMaxFlag = False;
    MMinMaxFlag = False;
    HasHydrogen = False;
    ElemNo = MINELEM;
    ResNo = MINRES;
    MaskCount = 0;
}


void DestroyDatabase()
{
    register void __far *temp;
    register Group __far *gptr;

    if( Database )
    {   ReclaimHBonds( Database->slist );
	ReclaimHBonds( Database->hlist );
	ReclaimBonds( Database->blist );

	while( Database->clist )
	{   ReclaimBonds(Database->clist->blist);
	    if( (gptr = Database->clist->glist) )
	    {   ReclaimAtoms(gptr->alist);
		while( gptr->gnext )
		{   gptr = gptr->gnext;
		    ReclaimAtoms(gptr->alist);
		}
		gptr->gnext = FreeGroup;
		FreeGroup = Database->clist->glist;
	    }
	    temp = Database->clist->cnext;
	    Database->clist->cnext = FreeChain;
	    FreeChain = Database->clist;
	    Database->clist = temp;
	}

	FreeMolecule = Database;
	Database = (void __far*)0;
    }
    ResetDatabase();
}


void PurgeDatabase()
{
#ifdef APPLEMAC
    register AllocRef *ptr;
    register AllocRef *tmp;
    register int i;
    
    /* Avoid Memory Leaks */
    for( ptr=AllocList; ptr; ptr=tmp )
    {   for( i=0; icount; i++ )
	    _ffree( ptr->data[i] );
	tmp = ptr->next;
	_ffree( ptr );
    }
#endif
}


void InitialiseDatabase()
{
    FreeMolecule = (void __far*)0;
    FreeHBond = (void __far*)0;
    FreeChain = (void __far*)0;
    FreeGroup = (void __far*)0;
    FreeAtom = (void __far*)0;
    FreeBond = (void __far*)0;

#ifdef APPLEMAC
    AllocList = (void*)0;
#endif

    ResetDatabase();
}

Modified: Thu Oct 27 16:00:00 1994 GMT
Page accessed 1161 times since Sat Apr 17 21:38:30 1999 GMT