#include "view.h"

#define INTERSECTION(plane,q1,q2,q)  {					     		\
	REAL t,dx,dy,dz,dw;												\
	dx = q2.x - q1.x; dy = q2.y - q1.y; 							\
	dz = q2.z - q1.z; dw = q2.w - q1.w;								\
	switch(plane){													\
		case XLEFT :	t  = -(q1.x + q1.w)/(dx + dw);				\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.w = -q.x;									\
						q.z = q1.z + t*dz;							\
						break;										\
																	\
		case XRIGHT :	t = (q1.x - q1.w)/(dw - dx);				\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.z = q1.z + t*dz;							\
						q.w = q.x;									\
						break;										\
																	\
		case YBOTTOM :	t= -(q1.y + q1.w)/(dy + dw);				\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.z = q1.z + t*dz;							\
						q.w = -q.y;									\
						break;										\
																	\
		case YTOP :		t = (q1.y - q1.w)/(dw - dy);				\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.z = q1.z + t*dz;							\
						q.w = q.y;									\
						break;										\
																	\
		case ZFRONT :	t = -q1.z/dz;								\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.z = 0.0;									\
						q.w = q1.w + t*dw;							\
						break;										\
																	\
		case ZBACK :	t = (q1.z - q1.w)/(dw - dz);						\
						q.x = q1.x + t*dx;							\
						q.y = q1.y + t*dy;							\
						q.z = q1.z + t*dz;									\
						q.w = q.z;							\
						break;										\
																	\
		default		:	/*donothing*/								\
						break;										\
	}																\
}


#define TEST(plane,q,result) {				\
	switch(plane){							\
		case XLEFT	:	result = q.x + q.w;	\
						break;				\
		case XRIGHT	:	result = q.w - q.x;	\
						break;				\
		case YBOTTOM:	result = q.y + q.w;	\
						break;				\
		case YTOP	:	result = q.w - q.y;	\
						break;				\
		case ZFRONT	:	result = q.z;		\
						break;				\
		case ZBACK	:	result = q.w - q.z;	\
						break;				\
		default		:	/*donothing*/		\
						break;				\
	}										\
}

#define ADDToClipPolygon(poly,q)	{poly.p[poly.n++] = q;}


typedef struct{
	Point4D p[MAX_POLYGON];
	int n;
} ClipPolygon;

typedef enum{
	ZFRONT,ZBACK,XLEFT,XRIGHT,YBOTTOM,YTOP
} Plane;

static ClipPolygon	CurrentPoly;

static BOOLEAN clipByPlane(plane)
/*returns false if completely clipped*/
Plane plane;
{
	REAL r1,r2;
	Point4D q1,q2,q;
	register int i;
	ClipPolygon outPoly;
	
	if(CurrentPoly.n == 0) return(FALSE);
	
	/*initialise the output Poly*/
	outPoly.n = 0;
	
	/*set new last point to the first*/
	CurrentPoly.p[CurrentPoly.n] = CurrentPoly.p[0];
	
	q1 = CurrentPoly.p[0];
	for(i=1;i<=CurrentPoly.n;++i){
		q2 = CurrentPoly.p[i];
		TEST(plane,q1,r1); TEST(plane,q2,r2);
		
		if(NEGATIVE(r1) && POSITIVE(r2)){   /*crosses into visible region*/
			INTERSECTION(plane,q1,q2,q);	/*assigns intersection to q*/
			ADDToClipPolygon(outPoly,q);
			ADDToClipPolygon(outPoly,q2);
		}
		else
		if(POSITIVE(r1) && POSITIVE(r2)){ /*in visible region*/
			ADDToClipPolygon(outPoly,q2);
		}	
		else
		if(POSITIVE(r1) && NEGATIVE(r2)){ /*crosses out of visible region*/
			INTERSECTION(plane,q1,q2,q);
			ADDToClipPolygon(outPoly,q);
		}
		else
		if(NEGATIVE(q1.z) && NEGATIVE(q2.z)){ /*invisible*/
			/*donothing*/;
		}
		
		q1 = q2;
	}
	
	/*reset the current Poly to the output Poly*/
	CurrentPoly = outPoly;
	if(outPoly.n == 0) return(FALSE);
	else return(TRUE);
}
		

void doClipPolygon(surface,shade)
Surface *surface;
Shading *shade;
{
	register int i;
	Point3D s;
	
	if(clipByPlane(ZFRONT)){
		if(clipByPlane(ZBACK)){
			if(clipByPlane(XLEFT)){	
				if(clipByPlane(XRIGHT)){
					if(clipByPlane(YBOTTOM)){
						if(clipByPlane(YTOP)){
							/*start up a new polygon on the surface*/
							newPolygonOnSurface(surface);
							
							/*output the polygon to the surface*/
							for(i=0;i<CurrentPoly.n;++i){
								PROJECTION(CurrentPoly.p[i],s);
								addToPolygonOnSurface(surface,&s);
							}
							
							displayPolygonOnSurface(surface,shade);
						}
					}
				}
			}
		}
	}
}

void newClipPolygon()
{
	CurrentPoly.n = 0;
}

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

	ADDToClipPolygon(CurrentPoly,q);
}