/*
	Interpolating specific points with cubic blending functions, without using any
	intermediate control points.  The point of this is to show that Bezier curves are
	only well-behaved within their control points.
	
	2D example only.

	Source file to be used with
	Cunningham, Computer Graphics: Programming in OpenGL for Visual Communication, Prentice-Hall, 2007

	Intended for class use only
*/
#include <GLUT/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

// Bernstein basis for Bezier curves
#define f0(t) (1.-(t))*(1.-(t))*(1.-(t))
#define f1(t) 3.*(t)*(1.-(t))*(1.-(t))
#define f2(t) 3.*(t)*(t)*(1.-(t))
#define f3(t) (t)*(t)*(t)

// function prototypes
void myinit(void);
void drawAxis(void);
void drawAxes(void);
void curve(void);
void display( void );
void reshape(int w,int h);
void keyboard(unsigned char key, int x, int y);

//	control points for each curve segment
#define CURVE_SIZE 8
static GLfloat ctrlpts[CURVE_SIZE][3] = {	{ -5., -1.,  0.},
											{ -3.,  1.,  0.},
											{ -1., -1.,  0.},
											{  1.,  3.,  0.},
											{  3.,  1.,  0.},
											{  2., -2.,  0.},
											{  4., -4.,  0.},
											{  6., -1.,  0.} };

void myinit(void)
{ 
	glClearColor( 0.0, 0.0, 0.0, 0.0 );
	glEnable(GL_DEPTH_TEST);
}

void drawAxis(void)
{
//	draw one axis, the Z-axis, as the template for all axes; axes all white
    GLfloat white[]={1.0, 1.0, 1.0, 1.0};
	
//  Draw the standard axis in Z-orientation
	glColor3fv(white);
    glBegin(GL_QUAD_STRIP);
      glVertex3f( 0.02, 0.02,  3.0 );
      glVertex3f( 0.02, 0.02, -3.0 );
      glVertex3f(-0.02, 0.02,  3.0 );
      glVertex3f(-0.02, 0.02, -3.0 );
      glVertex3f(-0.02,-0.02,  3.0 );
      glVertex3f(-0.02,-0.02, -3.0 );
      glVertex3f( 0.02,-0.02,  3.0 );
      glVertex3f( 0.02,-0.02, -3.0 );
      glVertex3f( 0.02, 0.02,  3.0 );
      glVertex3f( 0.02, 0.02, -3.0 );
    glEnd();
    glPushMatrix();
    glTranslatef( 0.0, 0.0, 3.0 );
    glutSolidCone( 0.1, 0.2, 20, 20 );
    glPopMatrix();
}

void drawAxes(void)
{
//  Draw the Z-axis
//    drawAxis();
//  Draw the standard axis in X-orientation
    glPushMatrix();
    glRotatef( 90.0, 0.0, 1.0, 0.0 );
    drawAxis();
    glPopMatrix();
//  Draw the standard axis in Y-orientation
    glPushMatrix();
    glRotatef( -90.0, 1.0, 0.0, 0.0 );
    drawAxis();
    glPopMatrix();
}

void curve(void)
{
#define NPTS 30

	int i, j;
	float xt, yt, zt, t;
	GLfloat tempPts[4][3];
	
	glPointSize(4.0);
	glColor3f(1.0,0.0,0.0);	// red for control points
	glBegin(GL_POINTS);
		for (i=0; i<CURVE_SIZE-1; i++ )
			glVertex3fv(ctrlpts[i]);
	glEnd();

	for (i = 0; i < 3; i++){
		tempPts[0][i] = ctrlpts[0][i];
		tempPts[1][i] = ctrlpts[1][i];
		tempPts[2][i] = ctrlpts[2][i];
		tempPts[3][i] = ctrlpts[3][i];
	}
	
	glColor3f(0.0, 1.0, 1.0); // cyan
	glBegin(GL_LINE_STRIP);
		for (j=0; j<2; j++)
		{
			for (i=0; i<=NPTS; i++) {
				t = (float)i/(float)NPTS;
				xt = f0(t)*tempPts[0][0]+f1(t)*tempPts[1][0]+
				 	f2(t)*tempPts[2][0]+f3(t)*tempPts[3][0];
				yt = f0(t)*tempPts[0][1]+f1(t)*tempPts[1][1]+
				 	f2(t)*tempPts[2][1]+f3(t)*tempPts[3][1];
				zt = f0(t)*tempPts[0][2]+f1(t)*tempPts[1][2]+
				 	f2(t)*tempPts[2][2]+f3(t)*tempPts[3][2];
				glVertex3f(xt,yt,zt);
			}
			 for (i=0; i<3; i++) {
				tempPts[0][i] = ctrlpts[3+3*j][i];
				tempPts[1][i] = ctrlpts[4+3*j][i];
				tempPts[2][i] = ctrlpts[5+3*j][i];
				tempPts[3][i] = ctrlpts[6+3*j][i];
			}
		}
	glEnd();
}

void display( void )
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	drawAxes();	// axes stay fixed; curve will rotate

	glLineWidth(2.0);
	curve();
	glLineWidth(1.0);

	glutSwapBuffers();
}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(70.0,1.0,1.0,30.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

int main(int argc, char** argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("Splines without blending");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);

	myinit();
	glutMainLoop();
}