/*
 * Computer Graphics, Assignment, Intersections
 * Filename ........ intersect.c
 * Description ..... Line-polygon intersection algorithm
 * Date ............ 20.08.2008
 * 
 * Student name .... Sander van Veen & Taddeus Kroes
 * Student email ... sandervv@gmail.com & taddeuskroes@hotmail.com
 * Collegekaart .... 6167969 & 6054129
 * Date ............ 10.10.2010
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>

#include "intersect.h"
#include "polys.h"
#include "vectors.h"

#define CUBE_SIZE .1

#define MAX(x, y, z) (x > y ? (x > z ? x : z) : (y > z ? y : z))
#define ABS(x) (x < 0 ? -x : x)

/*
 * Draw a cube around a point p, with simensions CUBE_SIZE
 */
void
drawCube(point p)
{
	glPushMatrix();
	glTranslatef(p.x, p.y, p.z);
	glutSolidCube(CUBE_SIZE);
	glPopMatrix();
}

/*
 * Project a point from 3D to 2D by omitting one of the vectors
 */
void
project(point p, GLdouble *x, GLdouble *y, GLint omit)
{
	switch( omit )
	{
		case 0:  *x = p.y; *y = p.z; break;
		case 1:  *x = p.x; *y = p.z; break;
		default: *x = p.x; *y = p.y; break;
	}
}

void
intersectPoly(poly p, point line_start, point line_end)
{
	// 1. Calculate plane equation
	point n;
	GLdouble D;
	
	// Use the first 3 points of the polygon to determine the plane by
	// creating 2 directional vectors. Then, normalize the cross product
	// of those vectors to calculate the normal.
	p.pnormal = n = normalizeVector(crossProduct(
		subtractVectors(p.pts[1], p.pts[0]),
		subtractVectors(p.pts[2], p.pts[0])
	));
	
	// n.P = D, P is a point on the plane (we'l use a corner of the polygon)
	D = dotProduct(n, p.pts[0]);
	
	// 2. Calculate intersection of line with plane
	GLdouble t, denom;
	point plane_intersect;
	
	denom = dotProduct(n, subtractVectors(line_end, line_start));
	
	// If the line is parallel to the plane, there is no intersection
	if( !denom )
		return;
		
	t = (D - dotProduct(n, line_start)) / denom;
	
	// If t is not in the interval [0,1], the intersection of the line is not
	// on the given line segment.
	if( t < 0 || t > 1 )
		return;
	
	plane_intersect = addVectors(
		line_start,
		scalarMultiply( subtractVectors(line_end, line_start), t )
	);
	
	// 3. Project 3D to 2D by omitting the largest dimension.
	//    Check if plane intersection is in polygon using the 2D projection.
	GLint i, omit, edges = 0;
	GLdouble k, y, Px, Py, Ax, Ay, Bx, By;
	
	// Omit the axis with the largest value in the polygon's normal vector
	if( ABS(n.x) > ABS(n.y) )
		omit = ABS(n.x) > ABS(n.z) ? 0 : 2;
	else
		omit = ABS(n.y) > ABS(n.z) ? 1 : 2;
	
	// Convert P to 2D
	project(plane_intersect, &Px, &Py, omit);
	
	// Draw a line from the projected P in a positive x direction and count the
	// intersections with the edges of the polygon.
	for( i = 0; i < p.points; i++ )
	{
		// Next edge
		project(p.pts[i], &Ax, &Ay, omit);
		project(p.pts[(i+1) % p.points], &Bx, &By, omit);
		
		// Check if P is vertically between A and B
		if( Py <= fmax(Ay, By) && Py > fmin(Ay, By) )
		{
			// Check if P is left of AB. If so, there's an intersection.
			if( Ax == Bx )
			{
				// Corner case: k = 0
				if( Px <= Ax )
					edges++;
			}
			else if( Ay != By )
			{
				// Normal case, compare AB's line equation to P
				k = (By - Ay) / (Bx - Ax);
				y = k * (Px - Ax) + Ay;
				
				if( k > 0 ? y <= Py : y >= Py )
					edges++;
			}
		}
	}
	
	// If there's an odd number of edge intersections, P is inside the polygon.
	if( edges % 2 )
		drawCube(plane_intersect);
}

void
intersect(polys* list, point line_start, point line_end)
{
    int p;
    for (p = 0; p < list->length; p++)
        intersectPoly(list->items[p], line_start, line_end);
}
