#include "view.h"

void crossProduct(a,b,c)
Vector a,b,c;
{
	c[0] = a[1]*b[2] - a[2]*b[1];
	c[1] = a[2]*b[0] - a[0]*b[2];
	c[2] = a[0]*b[1] - a[1]*b[0];
}

void normaliseVector(v)
Vector v;
{
	REAL k;
	register int i;
	
	k = 1.0/NORMVector(v);
	for(i=0;i<3;++i) v[i] = v[i]*k;
}


void setVPNView(view,vpn)
View *view;
Vector vpn;
{
	register int i;
	
	for(i=0;i<3;++i) view->VPN[i] = vpn[i];
}

void setVPN(view,x,y,z)
View *view;
REAL x,y,z;
{	
	MAKEVector(view->VPN,x,y,z);
}


void setVUVView(view,vuv)
View *view;
Vector vuv;
{
	register int i;
	
	for(i=0;i<3;++i) view->VUV[i] = vuv[i];
}

void setVUV(view,x,y,z)
View *view;
REAL x,y,z;
{
	MAKEVector(view->VUV,x,y,z);
}


void setVRPView(view,vrp)
View *view;
Point3D *vrp;
{
	X(view->VRP) = X(*vrp);
	Y(view->VRP) = Y(*vrp);
	Z(view->VRP) = Z(*vrp);	
}

void setVRP(view,x,y,z)
View *view;
REAL x,y,z;
{
	X(view->VRP) = x;
	Y(view->VRP) = y;
	Z(view->VRP) = z;	
}
	
static void setUVNView(view)
View *view;
{	
	REAL k;
	register int i;
	
	COPYVector(view->n,view->VPN);
	normaliseVector(view->n);
	
	crossProduct(view->n,view->VUV,view->u);
	normaliseVector(view->u);
	
	crossProduct(view->u,view->n,view->v);
	
	view->Qu = POINTByVector(view->VRP,view->u);
	view->Qv = POINTByVector(view->VRP,view->v);
	view->Qn = POINTByVector(view->VRP,view->n);
}
	
void setCOPView(view,cop)
View *view;
Point3D *cop;
{
	X(view->COP) = X(*cop);
	Y(view->COP) = Y(*cop);
	Z(view->COP) = Z(*cop);	
}

void setCOP(view,x,y,z)
View *view;
REAL x,y,z;
{
	X(view->COP) = x;
	Y(view->COP) = y;
	Z(view->COP) = z;	
}

void setVPDView(view,d)
View *view;
REAL d;
{
	view->VPD = d;
}

void setVPWindowView(view,u1,u2,v1,v2)
View *view;
REAL u1,u2,v1,v2;
{
	view->Umin = u1;
	view->Umax = u2;
	view->Vmin = v1;
	view->Vmax = v2;
}

void setClipView(view,dmin,dmax)
View *view;
REAL dmin,dmax;
{
	view->FCP = dmin;
	view->BCP = dmax;
}

static void setDistancesView(view)
View *view;
{	
	view->dx = view->Umax - view->Umin;
	view->dy = view->Vmax - view->Vmin;
	
	view->px = view->Umin + view->Umax - 2.0*X(view->COP);
	view->py = view->Vmin + view->Vmax - 2.0*Y(view->COP);
	
	view->D = view->VPD - Z(view->COP);
	view->Dmin = (view->FCP - view->VPD)/view->D;
	view->Dmax = (view->BCP - view->VPD)/view->D;
	
	/*(z-Dmin)*Dz gives the z in 4D space*/
	view->Dz = (view->Dmax+1.0)/(view->Dmax-view->Dmin);
}

#define _M	(VM(view))
#define _PX (view->px)
#define _PY (view->py)
#define _DX (view->dx)
#define _DY (view->dy)
#define _D	(view->D)
#define _U	(view->u)
#define _V 	(view->v)
#define _N	(view->n)
#define _Qu (view->Qu)
#define _Qv	(view->Qv)
#define _Qn	(view->Qn)
#define _Cx	(view->COP.x)
#define _Cy	(view->COP.y)
#define _Cz	(view->COP.z)
#define _d	(view->VPD)

void setView(view)
View *view;
{
	register int i,j;
	REAL d2,dDx,dDy;
	
	setUVNView(view);
	setDistancesView(view);
	
	d2 = _D*2.0;
	dDx = _D*_DX;
	dDy = _D*_DY;
	
	for(i=0;i<3;++i){
		_M[i][0] = (d2*_U[i] - _PX*_N[i])/dDx;
		_M[i][1] = (d2*_V[i] - _PY*_N[i])/dDy;
		_M[i][2] = _N[i]/_D;
		_M[i][3] = 0.0;
	}
	
	_M[3][0] = (_PX*(_Qn + _Cz) - d2*(_Qu + _Cx))/dDx;
	_M[3][1] = (_PY*(_Qn + _Cz) - d2*(_Qv + _Cy))/dDy;
	_M[3][2] = -(_Qn + _d)/_D;
	
	_M[3][3] = 1.0;
}

void transformVPNOfView(view,m)
/*transforms the vpn by matrix m*/
View *view;
TMatrix *m;
{
	Point3D p;
	
	MAKEPoint3D(p,view->VPN[0],view->VPN[1],view->VPN[2]);
	transformPoint3D(&p,m);
	
	setVPN(view,p.x,p.y,p.z);
}

void translateVRPOfView(view,amount)
/*translates the VRP along the VPN*/
View *view;
REAL amount;
{
	view->VRP.x += (view->VPN[0]*amount);
	view->VRP.y += (view->VPN[1]*amount);
	view->VRP.z += (view->VPN[2]*amount);
}

void transformVUVOfView(view,m)
/*transforms the vuv by matrix m*/
View *view;
TMatrix *m;
{
	Point3D p;
	
	MAKEPoint3D(p,view->VUV[0],view->VUV[1],view->VUV[2]);
	transformPoint3D(&p,m);
	
	setVUV(view,p.x,p.y,p.z);
}
	
		
View *newView(surface)
Surface *surface;
{
	View *view;
	
	view = Malloc(View,1);
	
	view->surface = surface;
	
	/*set up a default view*/
	setVRP(view,0.0,0.0,0.0);
	setVPN(view,0.0,0.0,-1.0);
	setVUV(view,0.0,1.0,0.0);
	setCOP(view,0.0,0.0,-1.0);
	setVPDView(view,0.0);
	setVPWindowView(view,-4.0,4.0,-4.0,4.0);
	setClipView(view,0.0,2.0);
	
	setView(view);
	
	return(view);
}

#define XPART(p) (X(*p)*_M[0][0]+Y(*p)*_M[1][0]+Z(*p)*_M[2][0]+_M[3][0])
#define YPART(p) (X(*p)*_M[0][1]+Y(*p)*_M[1][1]+Z(*p)*_M[2][1]+_M[3][1])
#define ZPART(p) (X(*p)*_M[0][2]+Y(*p)*_M[1][2]+Z(*p)*_M[2][2]+_M[3][2])

void point3DTo4DView(view,p,q)
View *view;
Point3D *p;
Point4D *q;
{
	REAL z;
	
	q->x = XPART(p);
	q->y = YPART(p);
	q->z = ((z = ZPART(p)) - view->Dmin)*view->Dz;
	q->w = z+1.0;
}

static void projectPoint(vertex,i,view)
Vertex *vertex;
int i;
View *view;
{
	Point3D *p;
	Point4D q;
	Point3D s;
	
	p = atVertex(vertex,i);
	point3DTo4DView(view,p,&q);

	PROJECTION(q,s);
	addToPolygonOnSurface(view->surface,&s);
}
	
	
void displayFaceOnView(view,face)
View *view;
Face *face;
{
	/*start up a new polygon on the surface*/
	newPolygonOnSurface(view->surface);

	/*traverse each point on the surface*/
	traversePtrFace(face,&projectPoint,view);

	displayPolygonOnSurface(view->surface,SHADINGOfFace(face));
}
	

void displayClipFaceOnView(view,face)
View *view;
Face *face;
{
	/*start up a new clip polygon*/
	newClipPolygon();
	
	/*traverse each point on the surface*/
	traversePtrFace(face,&constructClipPolygon,view);
	
	doClipPolygon(view->surface,SHADINGOfFace(face));
}

#if NOSHADOWS

void displayFaceSetOnView(view,facelist,faceset)
/*this only displays one level of shadows - no shadows on shadows*/
View *view;
FaceList *facelist;
Index *faceset;
{
	Index *ind;
	Face *face;
	register int i;
	Index *indShadow;
	
	ind = faceset;
	
	while(ind != NOINDEX){
		displayClipFaceOnView(view, (face = atFaceList(facelist,INDEX(ind))) );
		if(face->noOfShadows > 0){
			indShadow = face->shadow;
			while(indShadow != NOINDEX){
				displayClipFaceOnView(view,atFaceList(facelist,INDEX(indShadow)));
				indShadow = NEXT(indShadow);
			}
		}
		/*
			for(i=0;i<face->noOfShadows;++i) 
				displayClipFaceOnView(view,face->shadowFace[i]);
		*/
		ind = NEXT(ind);
	}
}
#endif
		
		
void displayTreeOnView(view,facelist,t)
View *view;
FaceList *facelist;
Tree *t;
{
	Point3D wc_cop;
	PlaneEq *plane;
	Face *face;
	Classification c;
	REAL d;
	
	/*obtain the COP in WC*/
	wc_cop.x = WC_COP_X(view);
	wc_cop.y = WC_COP_Y(view);
	wc_cop.z = WC_COP_Z(view);
	
	if(t == EMPTYTREE) return;
	
	face = atFaceList(facelist,INDEX(FACESETOfTree(t)));
	plane = PLANEQOfFace(face);
	/*
	c = classifyPoint3D(plane,&wc_cop,&d);
	*/
	CLASSIFYPoint3D(plane,wc_cop,d,c);
	if(c == FRONT || c == ON){
		displayTreeOnView(view,facelist,BACKNODEOfTree(t));
		displayFaceSetOnView(view,facelist,FACESETOfTree(t));
		displayTreeOnView(view,facelist,FRONTNODEOfTree(t));
	}
	else{
		displayTreeOnView(view,facelist,FRONTNODEOfTree(t));
		/*
		displayFaceSetOnView(view,facelist,FACESETOfTree(t));
		*/
		displayTreeOnView(view,facelist,BACKNODEOfTree(t));
	}
}
		