/*

	Dynamical system simulation; example for possible introductory
	computer graphics project

	(c) February 2001, Steve Cunninghan & Ken Brown

*/

#define PC

#ifdef MAC
#include "glut.h"
#endif
#ifdef OSX
#include <GLUT/glut.h>
#endif
#ifdef UNIX
#include <GL/glut.h>
#endif
#ifdef PC
#include <windows.h>
#include <wingdi.h>
#include <GL/glut.h>
#endif

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

#define LotsOfPoints 50000

typedef GLfloat point3[3];
typedef GLfloat point4[4];

//	arrays to hold properties of bodies
point3 location[LotsOfPoints];
int    NumberSoFar = 0;
float  sigma = 10.0, r = 28.0, b = 2.66667;

//	variables to hold light properties */
point4 white={1.0, 1.0, 1.0, 1.0};
point4 light_specular={0.5, 0.5, 0.5, 1.0};
point4 light_pos={100.0, 100.0, 0.0, 1.0};

//	global variables to store rotation control and eyepoint variable
float ep = 100.0;
#define ROT_AMT 2.0
GLfloat rotx = 0.0, roty = 0.0, rotz = 0.0; // rotational controls

//	function prototypes
void myinit(void);
void keyboard(unsigned char, int, int);
void rotate();
void animate(void);
void display( void );
void reshape(int,int);

// initialize OpenGL and all necessary global variables
void myinit(void)
{
	glClearColor(0.0, 0.0, 0.0, 1.0);     // black background
	// set light properties
	glLightfv(GL_LIGHT0, GL_AMBIENT, white);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);

	glEnable(GL_LIGHTING);	  // enable lighting
	glEnable(GL_LIGHT0);	  // enable light 0

	// material properties common to all bodies
	glMaterialfv(GL_FRONT, GL_SPECULAR, white);
	glMaterialf(GL_FRONT, GL_SHININESS, 10);
	glPointSize(2.0);

	//	set initial position of point
	location[NumberSoFar][0] = 0.1;
	location[NumberSoFar][1] = 0.2;
	location[NumberSoFar][2] = 0.3;
}

//	keyboard callback to handle any camera movement
void keyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
        case 'q' :	// rotate around X; q,o = positive, w,p = negative
        case 'o' :
			rotx += ROT_AMT; break;
        case 'w' :
        case 'p' :
			rotx -= ROT_AMT; break;
        case 'a' :	// rotate around Y; a,k = positive, s,l = negative
        case 'k' :
			roty += ROT_AMT; break;
        case 's' :
        case 'l' :
			roty -= ROT_AMT; break;
        case 'z' :	// rotate around Z; z,m = positive, x,, = negative
        case 'm' :
			rotz += ROT_AMT; break;
        case 'x' :
        case ',' :
			rotz -= ROT_AMT; break;
	case 'b' :
			ep = ep + 1.0; break;	// move back
	case 'f' :
			ep = ep - 1.0; break;	// move forward
	}
	glutPostRedisplay();
}

// Perform the calculations for the next animation frame, updating position
void animate(void)
{
	point3 delta;
	int i;
	
	delta[0] = sigma*(location[NumberSoFar][1]-location[NumberSoFar][0]);
	delta[1] = r*location[NumberSoFar][0] - location[NumberSoFar][1] -
			     location[NumberSoFar][0]*location[NumberSoFar][2];
	delta[2] = location[NumberSoFar][0]*location[NumberSoFar][1] - b*location[NumberSoFar][2];

	for (i=0; i<3; i++)
		location[NumberSoFar+1][i] = location[NumberSoFar][i] + 0.01*delta[i];
	NumberSoFar++;
	glutPostRedisplay();
}

//	Function to perform rotation around coordinate axes, not around object axes

//	rotation control uses two keypads embedded in standard keyboard
//    q  w                     o  p  -- rotate around x-axis + -
//     a  s                   k  l   -- rotate around y-axis + -
//      z  x                 m  ,    -- rotate around z-axis + -
void rotate()
{
	glRotatef(rotx,1.0,0.0,0.0);
	glRotatef(roty,0.0,1.0,0.0);
	glRotatef(rotz,0.0,0.0,1.0);
}

void display( void )
{
	int i;

	// clear the window and z-buffer bit
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//         eye point   center of view       up
	gluLookAt(ep, ep, ep, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);

	// rotate by the current rotation on each axis
	rotate();

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, white );
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white );
	glBegin(GL_LINE_STRIP);
		for (i=0; i<NumberSoFar; i++)
			glVertex3fv(location[i]);
	glEnd();
	
	glutSwapBuffers();
}

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0,(float)w/(float)h,1.0,300);
}

int main(int argc, char** argv)
{
// Standard GLUT initialization
        glutInit(&argc,argv);
        // Double buffering, RGB color, z-buffer
        glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowSize(500,500);			// 500 x 500 pixel window
        glutInitWindowPosition(50, 50);		// place window top left of display
        glutCreateWindow("Lorenz attractor");	// window title
        glutDisplayFunc(display);			// display callback invoked when window opened
        glutReshapeFunc(reshape);			// perform the viewing on the displayed object
        glutKeyboardFunc(keyboard);			// keyboard callback invoked for keyboard input
        glutIdleFunc(animate);	// callback when when no event has occurred

        myinit();			// set attributes and initialize mass, position, and velocities
        glutMainLoop();		// enter event loop

        return 0;
}
