/* Computer Graphics, Assignment, Bezier curves
 * Filename ........ bezier.c
 * Description ..... Bezier curves
 * Date ............ 22.07.2009
 * Created by ...... Paul Melis
 *
 * Student name .... Sander van Veen & Taddeus Kroes
 * Student email ... sandervv@gmail.com & taddeuskroes@hotmail.com
 * Collegekaart .... 6167969 & 6054129
 * Date ............ 02.10.2010
 * Comments ........ 
 */

#include <math.h>
#include "bezier.h"
#include <stdio.h>
#include <stdlib.h>

#define INTERSECTION_PRECISION 0.001

/* Calculate the factorial of a number.
 */

int F(int x) { return !x ? 1 : x * F(x-1); }

/* Given a Bezier curve defined by the 'num_points' control points
 * in 'p' compute the position of the point on the curve for parameter
 * value 'u'.
 *
 * Return the x and y values of the point by setting *x and *y,
 * respectively.
 */

void
evaluate_bezier_curve(float *x, float *y, control_point p[], int num_points, float u)
{
	int i, n = num_points-1;
	float b;
	
	*x = 0.0;
	*y = 0.0;
	
	for( i = 0; i <= n; i++ )
	{
		b = ( (float)F(n) / (F(i) * F(n-i)) ) * pow(u, i) * pow(1.0 - u, n - i);
		*x += b * p[i].x;
		*y += b * p[i].y;
	}
}

/* Draw a Bezier curve defined by the control points in p[], which
 * will contain 'num_points' points.
 *
 * Draw the line segments you compute directly on the screen
 * as a single GL_LINE_STRIP. This is as simple as using
 *
 *      glBegin(GL_LINE_STRIP);
 *      glVertex2f(..., ...);
 *      ...
 *      glEnd();
 *
 * DO NOT SET ANY COLOR!
 *
 * The 'num_segments' parameter determines the "discretization" of the Bezier
 * curve and is the number of straight line segments that should be used
 * to approximate the curve.
 *
 * Call evaluate_bezier_curve() to compute the necessary points on
 * the curve.
 */

void
draw_bezier_curve(int num_segments, control_point p[], int n)
{
	int i;
	float x, y, u_multiplier = 1.0 / num_segments;
	
	glBegin(GL_LINE_STRIP);
	
	for( i = 0; i <= num_segments; i++ )
	{
		evaluate_bezier_curve(&x, &y, p, n, i * u_multiplier);
		glVertex2f(x, y);
	}
	
	glEnd();
}

/* Find the intersection of a cubic Bezier curve with the line X=x.
   Return 1 if an intersection was found and place the corresponding y
   value in *y.
   Return 0 if no intersection exists.
*/

int
intersect_cubic_bezier_curve(float *y, control_point p[], float x)
{
	float x_int, y_int, u = 0.5, u_diff = 0.25;
	int i = 0, max_i = (int)(1 / INTERSECTION_PRECISION);
	
	while( i++ < max_i )
	{
		evaluate_bezier_curve(&x_int, &y_int, p, 4, u);
		
		if( fabs(x - x_int) < INTERSECTION_PRECISION )
		{
			*y = y_int;
			return 1;
		}
		else if( x_int > x )
			u -= u_diff;
		else
			u += u_diff;
		
		u_diff *= 0.5;
	}
	
	return 0;
}

