/* BBBondShape.m				 
 *
 * This is a N3DShape the draws a simple Bond. For faster drawing we store
 * the needed data.
 *
 * For more interface-info see the header file. More in depth information
 * can be found in the source-code.
 *
 * Written by: 		Thomas Engel
 * Created:    		25.15.1993 (Copyleft)
 * Last modified: 	23.05.1994
 */

#import "BBBondShape.h"
#import "BBBasicShapeBallsSticks.h"
#import "BBMolecule.h"
#import "BBAtom.h"
#import "BBBond.h"
#import <math.h>

@implementation BBBondShape

- initFrom:aBond asPartOf:aRegionShape
{
	self = [super init];
	if( !self ) return self;

	// OK. We really are an object...here we go with our init.
	
	ourBond = aBond;
	ourRegionShape = aRegionShape;
	[self update];

	return self;
}

- renderSelf:(N3DCamera *)theCamera
{
	// Just do the drawing. If the colors are the same we will only render
	// one cylinder. We won't check for any drawing style here.
	// The translation is the same only coloring and cylindering may vary.

	if( length <= 0 ) return self;
		
	RiTranslate( fromX, fromY, fromZ );
	if( angle != 0.0 ) RiRotate( angle, axisX, axisY, axisZ );
	RiColor( color1 );
	
	if( color1[0] != color2[0] ||
	    color1[1] != color2[1] ||
		color1[2] != color2[2] )
	{
		RiCylinder( radius, startingZ, length/2.0, 360.0, RI_NULL );
		RiColor( color2 );
		RiCylinder( radius, length/2.0, length, 360.0, RI_NULL );
	}
	else
	{
		RiCylinder( radius, startingZ, length, 360.0, RI_NULL );
	}
	return self;
}

- renderSelfAsBox:(N3DCamera *)theCamera
{
	// Just set the right color for the box !

	RiColor( color1 );	
	return [super renderSelfAsBox:theCamera];
}

- update
{
	// Here we setup every data useful for drawing at once.
	// The most complex it the axis/angle calculation.
	
	id		anAtom;
	float	deltaX, deltaY, deltaZ;
	
	anAtom = [ourBond fromAtom];
	fromX = [anAtom xPos];
	fromY = [anAtom yPos];
	fromZ = [anAtom zPos];

	anAtom = [ourBond toAtom];
	deltaX = [anAtom xPos] - fromX;
	deltaY = [anAtom yPos] - fromY;
	deltaZ = [anAtom zPos] - fromZ;

	// With startingZ and length we can cause our bond to be only drawn on
	// a limited section. This is useful when drawing bonds that belong to a
	// certain group...they can be renderd only to the half.
	// The default is full length.
		
	radius = [ourRegionShape bondRadius:self];
	startingZ = 0.0;
	length = sqrt( deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ );
	
	if( length == 0.0 )
	{
		length = -1;
		deltaX = -1;
		printf( "BeakerBoy(BBBondShape): ");
		printf( "BondLength is zero. Check your molecules!!!\r\n" );
	}
	// Now we'll reduce the deltaZ to fit delta-vector-length = 1 and then we
	// will calc the angle between the bond and the z-axis (where our cylinder
	// will be by default) with the scalar product. But only if the delta-vec 
	// is not already the z-axis.
	
	if( deltaX == 0.0 && deltaY == 0.0 )
	{
		if( deltaZ > 0.0 )
				angle = 0.0;
		else	angle = 180.0;
	}
	else
	{
		deltaX = deltaX/length;
		deltaY = deltaY/length;
		deltaZ = deltaZ/length;
	
		angle = acos( deltaZ );
		if(  deltaX >= 0.0 && deltaY >= 0.0 )
			angle *= -1.0;
		angle = angle * 180.0/M_PI;
	}
	// If the angle is not 0.0 or 180.0 we will calc the axis now. Otherwise we
	// are exactly in the z-axis an need no calced rotation-axis (it's y-axis)
	// The axis is the normal vector of the plane described by the z-axis and
	// bond we want to draw.
	// We have a left handed RiRotate so we need -angle too.
	
	if( angle == 0.0 ||
		angle == 180.0 )
	{
		axisX = 0.0;
		axisY = 1.0;
		axisZ = 0.0;
	}
	else
	{
		axisX = deltaY;
		axisY = -deltaX;
		axisZ = 0;	
		if( deltaX < 0 || deltaY < 0 ) angle = -angle;
	}
	
	// The color depends on the mode we are in..only in stick modes we
	// have differnt colors. Other we will use standard mol color
	// but both above only if we don't have to use group colors!
	
	if( [[ourRegionShape region] shapesInRegionColor] )
	{
 		NXConvertColorToRGB( [[ourRegionShape region] color],
							 &color1[0], &color1[1], &color1[2] );
 		color2[0] = color1[0];
 		color2[1] = color1[1];
 		color2[2] = color1[2];
	}
	else if( [ourRegionShape respondsTo:@selector(bondColor:)] )
	{
 		NXConvertColorToRGB( [ourRegionShape bondColor:self],
							 &color1[0], &color1[1], &color1[2] );
 		color2[0] = color1[0];
 		color2[1] = color1[1];
 		color2[2] = color1[2];
	}
	else
	{
 		NXConvertColorToRGB( [[ourBond fromAtom] color],
							 &color1[0], &color1[1], &color1[2] );
 		NXConvertColorToRGB( [[ourBond toAtom] color],
							 &color2[0], &color2[1], &color2[2] );
	}
	// The ast action is to calculate the bounding box. Maybe we will 
	// need it later.
	
	boundingBox[0] = - radius;
	boundingBox[1] = - radius;
	boundingBox[2] = 0;
	boundingBox[3] = + radius;
	boundingBox[4] = + radius;
	boundingBox[5] = length;

	return self;
}

- bond
{
	return ourBond;
}

- setDrawAsBox:(BOOL)flag
{
	// This shape does not want to get rendered as a box!
	
	return self;
}

- getBoundingBox:(RtBound *)boundingBox
{
	// We have to apply all the rotation stuff we did while drawing the
	// bond in renderSelf:
	
	// Right now this is too much of work. 
	// The current shape refuses to be rendered as a box!
	
	return self;
}

@end

/*
 * History: 23.05.94 Small changes to work with the new shape stuff.
 *
 *			07.05.94 Changed the naming to BB...
 *
 *			24.01.94 Added startingZ.
 *
 *			18.01.94 Removed the deltaX/deltyY bug 
 *
 *			15.01.94 First coded entered here. RenderMan stuff taken from 
 *					 MolViewer.
 *
 *
 * Bugs: - oh sure. Well box-rendering is quite stupid right now!
 *
 *		 - This is the place to include groupChecking or what ever. If a bond
 *		   belongs to two atoms but only one should be shown we can adjust
 *		   the color1/2, from/delta -> length values tu match the wanted 
 *		   drawing style. Draw just half of the bind or what you like..
 *
 */