/*
	Accumulation buffer example from Computer Graphics textbook
	Adapted from an example by Mike Bailey, Oregon State University
	
	Initially does not use the accumulation buffer; press 'a' to see the effect on the image and on
	the performance.

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

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

//#define M_PI	3.14159265
#include <GLUT/glut.h>

#define WINDOWTITLE	"Four-Bar Linkage Animation with Inversion"


/* lower-left corner of the window:					*/

#define WIN_LEFT	30
#define WIN_TOP		30


/* initial window size:							*/

#define WINDOW_SIZE	700


/* close to zero:							*/

#define SMALL	0.001


/* size (in window coords) of the links:				*/

#define L1		5.
#define L2		2.
#define L3		4.
#define L4		3.5

#define THICK		0.40
#define BUTT		0.40


/* size of the marker:							*/

#define MARK		0.10


/* colors:								*/

#define COLOR1		1.,1.,1.,1.
#define COLOR2		1.,0.,0.,1.
#define COLOR3		0.,1.,0.,1.
#define COLOR4		0.,0.,1.,1.
#define TRACECOLOR	1.,1.,0.,1.


/* how much to increment theta2:					*/

#define NTHETA2		180
#define DTHETA2		( 2.*M_PI/(float)NTHETA2 )


/* default place to locate link #3 peak:				*/

#define PEAKX		2.
#define PEAKY		2.
#define DPEAK		0.20


/* the escape key:                                                      */

#define ESCAPE          0x1b


/* handy:								*/

#define D2R(d)		( (M_PI/180.)*(d) )
#define R2D(r)		( (180./M_PI)*(r) )


/* success or failure:							*/

// #define FAILED		0
// #define SUCCEEDED	1


/* minimum scale factor allowed:					*/

#define MIN_SCALE	0.01


/* multiplication factors for input interaction:			*/
/*  (these are known from previous experience)				*/

#define ANGFACT		1.0
#define SCLFACT		 0.005
#define TRANFACT	0.05


/* active mouse buttons (or them together):				*/

#define LEFT		4
#define MIDDLE		2
#define RIGHT		1


/* values for menu items:						*/

#define  RESET		2
#define  QUIT		3


/* window background color (rgba):					*/

#define BACKGROUND_COLOR	0.,0.,0.,0.


/* color and line width for the axes:					*/

#define AXES_COLOR	1.,1.,0.
#define AXES_WIDTH	2.


/* handy to have around:						*/

#ifndef FALSE
#define FALSE		0
#define TRUE		( ! FALSE )
#endif

#define OFF		FALSE
#define ON		TRUE



/**
 ** global variables:
 **/

int	ActiveButton;		/* current button that is down		*/
int	AxesList;		/* list to hold the axes		*/
int	AxesMenu;		/* id of the axes pop-up menu		*/
int	AxesOnOff;		/* ON or OFF				*/
int	ColorMenu;		/* id of the color pop-up menu		*/
int	Debug;			/* non-zero means print debug info	*/
int	DoAccum;		/* TRUE means to use accum buffer	*/
int	MainMenu;		/* id of the main pop-up menu		*/
int	Projection;		/* ORTHO or PERSP			*/
int	ProjMenu;		/* id of the projection pop-up menu	*/
float	Red, Green, Blue;	/* star colors				*/
float	Scale;			/* scaling factor			*/
int	StarList;		/* list to hold the basic star		*/
int	StarsList;		/* list to hold the series of stars	*/
int	TopWindow;		/* window id for top-level window	*/
float	TranX, TranY;		/* scene translation			*/
float	Xang, Yang;		/* rotation angles in degrees		*/
int	Xmouse, Ymouse;		/* mouse values				*/


int	Ground;			/* which link is ground			*/
int	I2;			/* counter for theta2 increments	*/
float	Peakx, Peaky;		/* link3 peak				*/
int	StartOver;		/* are we starting the display over?	*/
float	Theta1, Theta2, Theta3, Theta4;	/* link angles in radians	*/
int     TraceDone;              /* have the trace arrays been filled?   */
float	TraceX[NTHETA2], TraceY[NTHETA2];	/* trace coords		*/
float	X1, Y1;			/* start of link #1			*/
float	X2, Y2;			/* start of link #2			*/
float	X3, Y3;			/* start of link #3			*/
float	X4, Y4;			/* start of link #4			*/

float	InverseX, InverseY, InverseTheta;
float	PlaceX, PlaceY, PlaceTheta;



/* function prototypes:							*/

void	DisplayLink( float length );
void	FourBar( void );
void	MakeInverse( void );
void	MakePlace( void );

void	Animate();
void	Axes( float length );
void	Display( void );
void	DoAxesMenu( int value );
void	DoMainMenu( int value );
void	DoPlace();
void	DoInverse();
void	InitGraphics( void );
void	InitLists( void );
void	Keyboard( unsigned char c, int i, int j );
void	MouseButton( int button, int state, int x, int y );
void	MouseMotion( int x, int y );
void	Quit( void );
void	Reset( void );
void	Resize( int width, int height );
void	Visibility( int state );


/**
 ** main program:
 **/

int main( int argc, char *argv[] )
{
	/* turn on the glut package:					*/
	/* (do this before checking argc and argv since it might	*/
	/* pull some command line arguments out)			*/

	glutInit( &argc, argv );


	/* set defaults:						*/

	Debug = FALSE;
	X1 = 0.;
	Y1 = 0.;
	Theta1 = M_PI;
	Theta2 = 0.;
	Theta3 = 0.;
	Theta4 = 3.*M_PI/2.;

	Ground = 1;
	MakePlace();
	MakeInverse();

	Peakx = PEAKX;
	Peaky = PEAKY;

	StartOver = TRUE;


	/* read the command line:					*/

/*	for( i=1; i < argc; i++ )
	{
		if( strcmp( argv[i], "-D" )  ==  0 )
		{
			Debug = TRUE;
			continue;
		}

		fprintf( stderr, "Unknown argument: '%s'\n", argv[i] );
		fprintf( stderr, "Usage: %s [-D]\n", argv[0] );
	}
*/

	/* setup all the graphics stuff, including callbacks:		*/

	InitGraphics();


	/* init the transformations and colors:				*/
	/* (will also post a redisplay)					*/

	Reset();


	/* draw the scene once and wait for some interaction:		*/
	/* (will never return)						*/

	glutMainLoop();
}



void
Animate()
{
	I2++;
	Theta2 += DTHETA2;

	if( StartOver == TRUE )
	{
		I2 = 0;
		Theta2 = 0.;
		StartOver = FALSE;
	}

	if( I2 >= NTHETA2 )
	{
		I2 = 0.;
		Theta2 = 0.;
		TraceDone = TRUE;
	}

	FourBar();

	glutSetWindow( TopWindow );
	glutPostRedisplay();
}


/* the stroke characters 'X' 'Y' 'Z' :					*/

static float xx[] = {
		0., 1., 0., 1.
	      };

static float xy[] = {
		-.5, .5, .5, -.5
	      };

static int xorder[] = {
		1, 2, -3, 4
		};


static float yx[] = {
		0., 0., -.5, .5
	      };

static float yy[] = {
		0., .6, 1., 1.
	      };

static int yorder[] = {
		1, 2, 3, -2, 4
		};


static float zx[] = {
		1., 0., 1., 0., .25, .75
	      };

static float zy[] = {
		.5, .5, -.5, -.5, 0., 0.
	      };

static int zorder[] = {
		1, 2, 3, 4, -5, 6
		};


/* fraction of the length to use as height of the characters:		*/

#define LENFRAC		0.10


/* fraction of length to use as start location of the characters:	*/

#define BASEFRAC	1.10


/**
 **	Draw a set of 3D axes:
 **	(length is the axis length in world coordinates)
 **/

void
Axes( float length )
{
	int i, j;			/* counters			*/
	float fact;			/* character scale factor	*/
	float base;			/* character start location	*/


	glBegin( GL_LINE_STRIP );
		glVertex3f( length, 0., 0. );
		glVertex3f( 0., 0., 0. );
		glVertex3f( 0., length, 0. );
	glEnd();
	glBegin( GL_LINE_STRIP );
		glVertex3f( 0., 0., 0. );
		glVertex3f( 0., 0., length );
	glEnd();

	fact = LENFRAC * length;
	base = BASEFRAC * length;

	glBegin( GL_LINE_STRIP );
		for( i = 0; i < 4; i++ )
		{
			j = xorder[i];
			if( j < 0 )
			{
				
				glEnd();
				glBegin( GL_LINE_STRIP );
				j = -j;
			}
			j--;
			glVertex3f( base + fact*xx[j], fact*xy[j], 0.0 );
		}
	glEnd();

	glBegin( GL_LINE_STRIP );
		for( i = 0; i < 5; i++ )
		{
			j = yorder[i];
			if( j < 0 )
			{
				
				glEnd();
				glBegin( GL_LINE_STRIP );
				j = -j;
			}
			j--;
			glVertex3f( fact*yx[j], base + fact*yy[j], 0.0 );
		}
	glEnd();

	glBegin( GL_LINE_STRIP );
		for( i = 0; i < 6; i++ )
		{
			j = zorder[i];
			if( j < 0 )
			{
				
				glEnd();
				glBegin( GL_LINE_STRIP );
				j = -j;
			}
			j--;
			glVertex3f( 0.0, fact*zy[j], base + fact*zx[j] );
		}
	glEnd();

}



/**
 ** draw the complete scene:
 **/

void
Display( void )
{
	int dx, dy, d;		/* viewport dimensions			*/
	int xl, yb;		/* lower-left corner of viewport	*/
	int limit;			/* limit on the tracing for loop */

	if( Debug )
		fprintf( stderr, "Display\n" );


	/* set which window we want to do the graphics into:		*/

	glutSetWindow( TopWindow );


	/* erase the background:					*/

	glDrawBuffer( GL_BACK );
	glReadBuffer( GL_BACK );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glDisable( GL_DEPTH_TEST );


	/* specify shading to be flat:					*/

	glShadeModel( GL_FLAT );


	/* set the viewport to a square centered in the window:		*/

	dx = glutGet( GLUT_WINDOW_WIDTH );
	dy = glutGet( GLUT_WINDOW_HEIGHT );
	d = dx < dy ? dx : dy;			/* minimum dimension	*/
	xl = ( dx - d ) / 2;
	yb = ( dy - d ) / 2;
	glViewport( xl, yb,  d, d );


	/* set the viewing volume:					*/
	/* remember that the eye is at the origin looking in -Z		*/
	/* remember that the Z values are actually			*/
	/* given as DISTANCES IN FRONT OF THE EYE			*/

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	gluOrtho2D( -8., 2.,     -3., 7. );


	/* translate the object into the viewing volume:		*/

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();


	glTranslatef( TranX, TranY, 0. );
	glScalef( Scale, Scale, Scale );


	/* possibly draw the axes:					*/

	if( AxesOnOff == ON )
		glCallList( AxesList );


	MakeInverse();
	DoPlace();
	DoInverse();

	glLineWidth( 3. );


	/* draw link #3:						*/

	glColor4f( COLOR3 );
	glPushMatrix();
		glTranslatef( X3, Y3, 0. );
		glRotatef( R2D(Theta3), 0., 0., 1. );
		DisplayLink( L3 );

	/* draw the peak, using the CTM to put it in the right place:	*/

		glBegin( GL_TRIANGLES );
			glVertex3f( -BUTT, THICK/2., 0. );
			glVertex3f( Peakx, Peaky, 0. );
			glVertex3f( L3+BUTT, THICK/2., 0. );
		glEnd();
	glPopMatrix();


	/* figure out where the peak point is in window coords:		*/

	if( Ground == 1  &&  TraceDone == FALSE )
	{
		TraceX[I2] = Peakx * cos(Theta3) - Peaky * sin(Theta3) + X3;
		TraceY[I2] = Peakx * sin(Theta3) + Peaky * cos(Theta3) + Y3;
	}


	/* draw link #1:						*/

	glColor4f( COLOR1 );
	glPushMatrix();
		glTranslatef( X1, Y1, 0. );
		glRotatef( R2D(Theta1), 0., 0., 1. );
		DisplayLink( L1 );
	glPopMatrix();


	/* draw link #2:						*/

	glColor4f( COLOR2 );
	glPushMatrix();
		glTranslatef( X2, Y2, 0. );
		glRotatef( R2D(Theta2), 0., 0., 1. );
		DisplayLink( L2 );
	glPopMatrix();


	/* draw link #4:						*/

	glColor4f( COLOR4 );
	glPushMatrix();
		glTranslatef( X4, Y4, 0. );
		glRotatef( R2D(Theta4), 0., 0., 1. );
		DisplayLink( L4 );
	glPopMatrix();


	/* record the trace:						*/

	if( Ground == 1 )
	{
		if( TraceDone )
		{
			limit = NTHETA2 - 1;
		}
		else
		{
			limit = I2;
		}
	}



	if( DoAccum )
	{
		glAccum( GL_MULT,   0.90 );
		glAccum( GL_ACCUM,  0.10 );
		glAccum( GL_RETURN, 1. );
	}


	/* swap the double-buffered framebuffers:			*/

	glutSwapBuffers();


	/* be sure the graphics buffer has been sent:			*/

	glFlush();
}


/**
 ** draw a link of a given length:
 **/

void
DisplayLink( float length )
{
	glBegin( GL_QUADS );
		glVertex3f( -BUTT, -THICK/2., 0. );
		glVertex3f( length+BUTT, -THICK/2., 0. );
		glVertex3f( length+BUTT,  THICK/2., 0. );
		glVertex3f( -BUTT,   THICK/2., 0. );
	glEnd();
}




/**
 ** process the axes pop-up menu:
 **/

void
DoAxesMenu( int value )
{
	if( Debug )
		fprintf( stderr, "DoAxesMenu: %d\n", value );


	AxesOnOff = value;

	glutSetWindow( TopWindow );
	glutPostRedisplay();
}



/**
 ** process the main pop-up menu:
 **/

void
DoMainMenu( int value )
{
	if( Debug )
		fprintf( stderr, "DoMainMenu: %d\n", value );


	switch( value )
	{
		case RESET:
			Reset();
			break;

		case QUIT:
			Quit();
			break;	/* never returns -- "don't need to do this" */

		default:
			fprintf( stderr, "Unknown main menu value: %d\n", value );
	}
}



/**
 ** initialize the glut and OpenGL libraries:
 **	also setup display lists and callback functions
 **/

void
InitGraphics( void )
{
	if( Debug )
		fprintf( stderr, "InitGraphics\n" );


	/* setup the display mode:					*/
	/* ( *must* be done before call to glutCreateWindow() )		*/

	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_ACCUM );


	/* set the initial window configuration:			*/

	glutInitWindowSize( WINDOW_SIZE, WINDOW_SIZE );
	glutInitWindowPosition( WIN_LEFT, WIN_TOP );


	/* open the window and set its title:				*/

	TopWindow = glutCreateWindow( WINDOWTITLE );
	glutSetWindowTitle( WINDOWTITLE );


	/* setup the clear values:					*/

	glClearColor( BACKGROUND_COLOR );
	glClearAccum( BACKGROUND_COLOR );
	glClear( GL_ACCUM_BUFFER_BIT );


	/* initialize the pop-up menus:					*/

	AxesMenu = glutCreateMenu( DoAxesMenu );
	glutAddMenuEntry( "Off", OFF );
	glutAddMenuEntry( "On",  ON );

	MainMenu = glutCreateMenu( DoMainMenu );
	glutAddSubMenu( "Axes", AxesMenu );
	glutAddMenuEntry( "Reset", RESET );
	glutAddMenuEntry( "Quit",  QUIT );


	/* attach the pop-up menu to the right mouse button:		*/

	glutAttachMenu( GLUT_RIGHT_BUTTON );


	/* setup the callback routines:					*/

	glutSetWindow( TopWindow );


	/* DisplayFunc -- redraw the window				*/
	/* ReshapeFunc -- handle the user resizing the window		*/
	/* KeyboardFunc -- handle a keyboard input			*/
	/* MouseFunc -- handle the mouse button going down or up	*/
	/* MotionFunc -- handle the mouse moving with a button down	*/
	/* PassiveMotionFunc -- handle the mouse moving with a button up*/
	/* VisibilityFunc -- handle a change in window visibility	*/
	/* EntryFunc	-- handle the cursor entering or leaving the window */
	/* SpecialFunc -- handle special keys on the keyboard		*/
	/* SpaceballMotionFunc -- handle spaceball translation		*/
	/* SpaceballRotateFunc -- handle spaceball rotation		*/
	/* SpaceballButtonFunc -- handle spaceball button hits		*/
	/* ButtonBoxFunc -- handle button box hits			*/
	/* DialsFunc -- handle dial rotations				*/
	/* TabletMotionFunc -- handle digitizing tablet motion		*/
	/* TabletButtonFunc -- handle digitizing tablet button hits	*/
	/* MenuStateFunc -- declare when a pop-up menu is in use	*/
	/* IdleFunc -- what to do when nothing else is going on		*/
	/* TimerFunc -- trigger something to happen every so often	*/

	glutDisplayFunc( Display );
	glutReshapeFunc( Resize );
	glutKeyboardFunc( Keyboard );
	glutMouseFunc( MouseButton );
	glutMotionFunc( MouseMotion );
	glutPassiveMotionFunc( NULL );
	glutVisibilityFunc( Visibility );
	glutEntryFunc( NULL );
	glutSpecialFunc( NULL );
	glutSpaceballMotionFunc( NULL );
	glutSpaceballRotateFunc( NULL );
	glutSpaceballButtonFunc( NULL );
	glutButtonBoxFunc( NULL );
	glutDialsFunc( NULL );
	glutTabletMotionFunc( NULL );
	glutTabletButtonFunc( NULL );
	glutMenuStateFunc( NULL );
	glutIdleFunc( Animate );
	glutTimerFunc( 0, NULL, 0 );


	/* create the axes:						*/

	AxesList = glGenLists( 1 );
	glNewList( AxesList, GL_COMPILE );
		glColor3f( AXES_COLOR );
		glLineWidth( AXES_WIDTH );
			Axes( 5. );
		glLineWidth( 1. );
	glEndList();
}



void
Keyboard( unsigned char c, int i, int j )
{
	if( Debug )
		fprintf( stderr, "Keyboard: '%c' (0x%0x)\n", c, c );

	switch( c )
	{
		case 'a':
			DoAccum = ! DoAccum;
			break;

		case 'h':
			Peakx -= DPEAK;
			StartOver = TRUE;
			TraceDone = FALSE;
			break;

		case 'l':
			Peakx += DPEAK;
			StartOver = TRUE;
			TraceDone = FALSE;
			break;

		case 'j':
			Peaky -= DPEAK;
			StartOver = TRUE;
			TraceDone = FALSE;
			break;

		case 'k':
			Peaky += DPEAK;
			StartOver = TRUE;
			TraceDone = FALSE;
			break;

		case '1':
			Ground = 1;
			MakePlace();
			break;

		case '2':
			Ground = 2;
			MakePlace();
			break;

		case '3':
			Ground = 3;
			MakePlace();
			break;

		case '4':
			Ground = 4;
			MakePlace();
			break;

		case 'q':
		case 'Q':
		case ESCAPE:
			Quit();		/* will not return here		*/

		default:
			fprintf( stderr, "Don't know what to do with keyboard hit:: '%c' (0x%0x)\n", c, c );
	}
}



/**
 ** called when the mouse button transitions down or up:
 **/

void
MouseButton
(
	int button,		/* GLUT_*_BUTTON			*/
	int state,		/* GLUT_UP or GLUT_DOWN			*/
	int x,			/* where mouse was when button was hit	*/
	int y			/* where mouse was when button was hit	*/
)
{
	int b;			/* LEFT, MIDDLE, or RIGHT		*/

	if( Debug )
		fprintf( stderr, "MouseButton: %d, %d, %d, %d\n", button, state, x, y );

	
	/* get the proper button bit mask:				*/

	switch( button )
	{
		case GLUT_LEFT_BUTTON:
			b = LEFT;		break;

		case GLUT_MIDDLE_BUTTON:
			b = MIDDLE;		break;

		case GLUT_RIGHT_BUTTON:
			b = RIGHT;		break;

		default:
			b = 0;
			fprintf( stderr, "Unknown mouse button: %d\n", button );
	}


	/* button down sets the bit, up clears the bit:			*/

	if( state == GLUT_DOWN )
	{
		Xmouse = x;
		Ymouse = y;
		ActiveButton |= b;		/* set the proper bit	*/
	}
	else
		ActiveButton &= ~b;		/* clear the proper bit	*/
}



/**
 ** called when the mouse moves while a button is down:
 **/

void
MouseMotion( int x, int y )
	/* x and y are mouse coords					*/
{
	int dx, dy;		/* change in mouse coordinates		*/

	if( Debug )
		fprintf( stderr, "MouseMotion: %d, %d\n", x, y );


	dx = x - Xmouse;		/* change in mouse coords	*/
	dy = y - Ymouse;

	if( ActiveButton & LEFT )
	{
		TranX += TRANFACT * (float)dx;
		TranY -= TRANFACT * (float)dy;
	}

	if( ActiveButton & MIDDLE )
	{
		Scale += SCLFACT * (float) ( dx - dy );
		if( Scale < MIN_SCALE )
			Scale = MIN_SCALE;	/* do not invert	*/
	}

	Xmouse = x;			/* new current position		*/
	Ymouse = y;

	glutSetWindow( TopWindow );
	glutPostRedisplay();
}




/**
 ** quit the program gracefully:
 **/

void
Quit( void )
{
	/* gracefully close out the graphics:				*/

	glFinish();


	/* gracefully close the graphics window:			*/

	glutDestroyWindow( TopWindow );


	/* gracefully exit the program:					*/

	exit( 0 );
}



/**
 ** reset the transformations and the colors:
 **
 ** this only sets the global variables --
 ** the main loop is responsible for redrawing the scene
 **/

void
Reset( void )
{
	ActiveButton = 0;
	AxesOnOff = OFF;
	DoAccum = FALSE;
	I2 = 0;
	Peakx = PEAKX;
	Peaky = PEAKY;
	Scale = 1.0;
	StartOver = FALSE;
	Theta1 = M_PI;
	Theta2 = 0.;
	Theta3 = 0.;
	Theta4 = 3.*M_PI/2.;
	TranX = TranY = 0.;
	X1 = 0.;
	Y1 = 0.;

	glutSetWindow( TopWindow );
	glutPostRedisplay();
}



/**
 ** called when user resizes the window:
 **/

void
Resize
(
	int width,		/* new value for window width		*/
	int height		/* new value for window height		*/
)
{
	if( Debug )
		fprintf( stderr, "ReSize: %d, %d\n", width, height );

	glutSetWindow( TopWindow );
	glutPostRedisplay();
}



/**
 ** handle a change to the window's visibility:
 **/

void
Visibility
(
	int state		/* GLUT_VISIBLE or GLUT_NOT_VISIBLE	*/
)
{
	if( Debug )
		fprintf( stderr, "Visibility: %d\n", state );

	if( state == GLUT_VISIBLE )
	{
		glutSetWindow( TopWindow );
		glutPostRedisplay();
	}
	else
	{
		/* could optimize by keeping track of the fact		*/
		/* that the window is not visible and avoid		*/
		/* redrawing it later ...				*/
	}
}




/**
 ** solve for the configuration of the four-bar linkage:
 **
 ** it assumes that L1, L2, L3, L4, Theta1, and Theta2 are set correctly
 ** and that Theta3 and Theta4 have reasonable guesses in them.
 **
 ** it iterates on Theta3 and Theta4
 **
 ** all angles are in radians
 **
 **/

void
FourBar( void )
{
	float e1, e2;				/* errors		*/
	int i;					/* counter		*/
	float denom;				/* denominator		*/
	float dt3, dt4;				/* delta theta3 & theta4 */
	float a, b, c, d;			/* matrix elements	*/


	/* 5 iterations usually does a good job:			*/
	/* quit early if it converges earlier				*/

	for( i = 0; i < 5; i++ )
	{
		e1 = L1*cos(Theta1) + L2*cos(Theta2) + L3*cos(Theta3) + L4*cos(Theta4);
		e2 = L1*sin(Theta1) + L2*sin(Theta2) + L3*sin(Theta3) + L4*sin(Theta4);

		if( fabs(e1) + fabs(e2)  <=  SMALL )
			break;

		a = -L3 * sin(Theta3);
		b = -L4 * sin(Theta4);
		c =  L3 * cos(Theta3);
		d =  L4 * cos(Theta4);

		denom = a*d - b*c;
		if( denom != 0. )
		{
			dt3 = ( (-e1)*d - b*(-e2) ) / denom;
			dt4 = ( a*(-e2) - (-e1)*c ) / denom;
			Theta3 += dt3;
			Theta4 += dt4;
		}
	}


	/* figure out where each link starts:				*/

	X2 = X1 + L1 * cos(Theta1);
	Y2 = Y1 + L1 * sin(Theta1);

	X3 = X2 + L2 * cos(Theta2);
	Y3 = Y2 + L2 * sin(Theta2);

	X4 = X3 + L3 * cos(Theta3);
	Y4 = Y3 + L3 * sin(Theta3);
}



/**
 ** produce the Inverse matrix based on what the Ground part is:
 **/

void
MakeInverse( void )
{
	switch( Ground )
	{
		case 1:
			InverseTheta = -Theta1;
			InverseX = -X1;
			InverseY = -Y1;
			break;

		case 2:
			InverseTheta = -Theta2;
			InverseX = -X2;
			InverseY = -Y2;
			break;

		case 3:
			InverseTheta = -Theta3;
			InverseX = -X3;
			InverseY = -Y3;
			break;

		case 4:
			InverseTheta = -Theta4;
			InverseX = -X4;
			InverseY = -Y4;
			break;
	}
}



/**
 ** produce the Placement matrix based on where the Ground part is right now:
 **/

void
MakePlace( void )
{
	switch( Ground )
	{
		case 1:
			PlaceTheta = Theta1;
			PlaceX = X1;
			PlaceY = Y1;
			break;

		case 2:
			PlaceTheta = Theta2;
			PlaceX = X2;
			PlaceY = Y2;
			break;

		case 3:
			PlaceTheta = Theta3;
			PlaceX = X3;
			PlaceY = Y3;
			break;

		case 4:
			PlaceTheta = Theta4;
			PlaceX = X4;
			PlaceY = Y4;
			break;
	}
}


void
DoInverse()
{
	glRotatef( R2D(InverseTheta), 0., 0., 1. );
	glTranslatef( InverseX, InverseY, 0. );
}


void
DoPlace()
{
	glTranslatef( PlaceX, PlaceY, 0. );
	glRotatef( R2D(PlaceTheta), 0., 0., 1. );
}
