#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
/*
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/mui.h>
*/
#include "gl.h"
#include "glu.h"
#include "glut.h"
#include "mui.h"

#include "axes.c"

/**
 **
 **	This is a sample OpenGL / GLUT / MUI program for ames 293
 **
 **	The objective is to draw one of a number of 3d objects
 **
 **	The left mouse button allows rotation
 **	The middle mouse button allows scaling
 **	The right mouse button brings up a pop-up menu that allows:
 **		0. The 3d object to be changed
 **		1. The projection to be changed
 **		2. The colors to be changed
 **		3. Axes to be turned on and off
 **		4. The transformations to be reset
 **		5. The program to quit
 **
 **	Author: Joe Graphics
 **
 **/

/**
 ** defines:
 **/

/* title of this window:						*/
#define WINDOWTITLE	"OpenGL / GLUT / MUI Sampler -- Joe Graphics"

/* the escape key:							*/
#define ESCAPE          0x1b

/* lower-left corner of the window:					*/
#define WIN_LEFT	30
#define WIN_BOTTOM	30

/* initial window size:							*/
#define INIT_WINDOW_SIZE	700


/* multiplication factors for input interaction:			*/
/*  (these are known from previous experience)				*/
#define ANGFACT		1.0
#define SCLFACT		 0.005

/* active mouse buttons (or them together):				*/
#define LEFT		4
#define MIDDLE		2
#define RIGHT		1

/* values for menu items:						*/
#define  SETCOLORS	1
#define  RESET		2
#define  QUIT		3

#define  ORTHO		1
#define  PERSP		2

#define  RED		1
#define  YELLOW		2
#define  GREEN		3
#define  CYAN		4
#define  BLUE		5
#define  MAGENTA	6
#define  WHITE		7
#define  BLACK		8

/* window background color (rgba):					*/
#define BACKGROUND_COLOR	0.,0.,0.,0.

/* color and line width for the axes:					*/
#define GAXES_COLOR	1.,.5,0.
#define GAXES_WIDTH	2.
#define LAXES_COLOR	1.,1.,0.
#define LAXES_WIDTH	1.

/* max # of objects:							*/
#define MAXOBJECTS	9

/* these are all here to support the mui window, which is used		*/
/* for messages and user interface stuff:							*/
#define MWINDOWTITLE     	"UI Window"
#define MX0			800
#define MY0			100
#define MDX			300
#define MDY			300
#define NLINES			 5

/* min and max values for the slider:					*/
#define TEXTMIN		0.00
#define TEXTMAX		3.00

/* handy to have around:						*/
#ifndef FALSE
#define FALSE		0
#define TRUE		( ! FALSE )
#endif

#define OFF		FALSE
#define ON		TRUE

#define DISABLED	FALSE
#define ENABLED		TRUE

/**
 ** global variables:
 **/

int	ActiveButton;		/* current button that is down		*/
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	GlobalAxesList;		/* list to hold the global axes		*/
int	GrWindow;		/* window id for graphics window	*/
int	LocalAxesList;		/* list to hold the local axes		*/
int	MainMenu;		/* id of the main pop-up menu		*/
int	MuiWindow;		/* the glut id for the mui window	*/
int	ObjectMenu;		/* id of the object selection menu	*/
int	ObjectLists[MAXOBJECTS];	/* object lists			*/
int	Projection;		/* ORTHO or PERSP			*/
int	ProjMenu;		/* id of the projection pop-up menu	*/
muiObject *QuitButton;		/* quit button in the mui window	*/
float	Red, Green, Blue;	/* object colors			*/
float	Scale;			/* scaling factor			*/
muiObject *TextLabel;		/* label to put on the slider		*/
muiObject *TextSlider;		/* slider in the mui window		*/
float	TextValue;		/* value read from the slider		*/
int	WhichObject;		/* object index to use			*/
long	WindowId;		/* identifier for the graphics window	*/
float	Xang, Yang;		/* rotation angles in degrees		*/
int	Xmouse, Ymouse;		/* mouse values				*/

/**
 ** function declarations:
 **/

void	Animate();
void	Axes( float length );
void	Display();
void	DoAxesMenu( int );
void	DoButtons( muiObject *, enum muiReturnValue );
void	DoColorMenu( int );
void	DoKeyboard( unsigned char, int, int );
void	DoMainMenu( int );
void	DoObjectMenu( int );
void	DoProjMenu( int );
void	DoRasterString( float x, float y, float z, char *s );
void	DoSliders( muiObject *, enum muiReturnValue );
void	DoStrokeString( float x, float y, float z, float ht, char *s );
void	InitGraphics();
void	InitLists();
void	InitMui();
void	MouseButton( int, int, int, int );
void	MouseMotion( int, int );
void	Quit();
void	Reset();
void	Resize( int, int );
//	double	Seconds( void );
void	Visibility( int );

/**
 ** main program:
 **/

int
main( int argc, char *argv[] )
{
	int i;			/* counter				*/

	/* 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;

	/* 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();
	return 0;
}


/**
 ** this is where one would put code that is to be called
 ** everytime the glut main loop has nothing to do, ie,
 ** the glut idle function
 **
 ** this is typically where animation parameters are set
 **
 ** do not call Display() from here -- let glutMainLoop() do it
 **/

void
Animate()
{
	/* if nothing else to do, post a redisplay for the mui window:	*/

	glutSetWindow( MuiWindow );
	glutPostRedisplay();

#ifdef HAVE_SOME_ANIMATION_TO_DO
	/* put animation stuff in here -- set some global variables	*/
	/* for Display() to find:					*/

	glutSetWindow( GrWindow );
	glutPostRedisplay();
#endif
}


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

void
Display()
{
	int dx, dy, d;		/* viewport dimensions			*/
	int xl, yb;		/* lower-left corner of viewport	*/
//	int wx, wy, ww, wh;

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

	/* set which window we want to do the graphics into:		*/
	glutSetWindow( GrWindow );

	/* erase the background:					*/
	glDrawBuffer(GL_BACK );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glEnable( 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();
	if( Projection == ORTHO )
		glOrtho( -3., 3.,     -3., 3.,     0.1, 60. );
	else
		gluPerspective( 70., 1.,	0.1, 60. );

	/* translate the object into the viewing volume:		*/
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt( 3., 4., 5.,     0., 0., 0.,     0., 1., 0. );

	/* possibly draw the global axes:				*/
	if( AxesOnOff == ON )
		glCallList( GlobalAxesList );

	/* perform the rotations and scaling about the origin:		*/
	glRotatef( Yang, 0., 1., 0. );
	glRotatef( Xang, 1., 0., 0. );
	glScalef( Scale, Scale, Scale );

	/* possibly draw the local axes:				*/
	if( AxesOnOff == ON )
		glCallList( LocalAxesList );

	/* set the color of the object:					*/
	glColor3f( Red, Green, Blue );

	/* draw the object:						*/
	glCallList( ObjectLists[ WhichObject ] );

	/* draw some text that just rotates with the scene:		*/
	glDisable( GL_DEPTH_TEST );
	glColor3f( 0., 1., 1. );
	DoRasterString( 0., TextValue, 0., "Text That Moves" );

	/* draw some text that is fixed on the screen:			*/
	glDisable( GL_DEPTH_TEST );
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	gluOrtho2D( 0., 100.,     0., 100. );	/* setup "percent units"*/
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	glColor3f( 1., 1., 1. );
	DoRasterString( 5., 5., 0., "Text That Doesn't" );

	/* swap the double-buffered framebuffers:			*/
	glutSwapBuffers();

	/* be sure the graphics buffer has been sent:			*/
	glFlush();
}


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

void
DoAxesMenu( int value )
{
	if( Debug )
		fprintf( stderr, "DoAxesMenu: %d\n", value );
	AxesOnOff = value;
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


/**
 ** mui buttons callback:
 **/

void
DoButtons( muiObject *obj, enum muiReturnValue rv )
{
//	char str[256];

	/* do different things depending on which button it was:	*/
	if( obj == QuitButton )
	{
		Quit();
	}
	glutSetWindow( MuiWindow );
	glutPostRedisplay();
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


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

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

	Red = Green = Blue = 1.;

	switch( value )
	{
		case RED:
			Green = Blue = 0.;
			break;

		case YELLOW:
			Blue = 0.;
			break;

		case GREEN:
			Red = Blue = 0.;
			break;

		case CYAN:
			Red = 0.;
			break;

		case BLUE:
			Red = Green = 0.;
			break;

		case MAGENTA:
			Green = 0.;
			break;

		case WHITE:
			break;

		case BLACK:
			Red = Green = Blue = 0.;
			break;

		default:
			fprintf( stderr, "Unknown color menu value: %d\n", value );
	}
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


/**
 ** the keyboard callback:
 **/

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

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

		case 'r':
		case 'R':
			DoColorMenu( RED );
			break;

		case 'y':
		case 'Y':
			DoColorMenu( YELLOW );
			break;

		case 'g':
		case 'G':
			DoColorMenu( GREEN );
			break;

		case 'c':
		case 'C':
			DoColorMenu( CYAN );
			break;

		case 'b':
		case 'B':
			DoColorMenu( BLUE );
			break;

		case 'm':
		case 'M':
			DoColorMenu( MAGENTA );
			break;

		case 'w':
		case 'W':
			DoColorMenu( WHITE );
			break;

		case 'k':
		case 'K':
			DoColorMenu( BLACK );
			break;


		case 'o':
		case 'O':
			DoProjMenu( ORTHO );
			break;

		case 'p':
		case 'P':
			DoProjMenu( PERSP );
			break;

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

	glutSetWindow( GrWindow );
	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 );
	}
}


/**
 ** process the object-selection pop-up menu:
 **/

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

	WhichObject = value;
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


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

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

	Projection = value;
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


/**
 ** use glut to display a string of characters using a raster font:
 **/

void
DoRasterString( float x, float y, float z, char *s )
{
	char c;			/* one character to print		*/

	glRasterPos3f( x, y, z );
	for( ; ( c = *s ) != '\0'; s++ )
	{
		glutBitmapCharacter( GLUT_BITMAP_TIMES_ROMAN_24, c );
	}
}


/**
 ** slider callback:
 **/

void
DoSliders( muiObject *obj, enum muiReturnValue rv )
{
	float v;		/* 0. <= slider value <= 1.		*/
	char str[256];		/* text to put in label			*/

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

	/* get value from the slider that moved:			*/
	v = muiGetHSVal( obj );

	/* do different things depending on which slider it was:	*/
	if( obj == TextSlider )
	{
		TextValue = TEXTMIN  +  v * ( TEXTMAX - TEXTMIN );
		sprintf( str, "%6.2f", TextValue );
		muiChangeLabel( TextLabel, str );
	}
	glutSetWindow( MuiWindow );
	glutPostRedisplay();
	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


/**
 ** use glut to display a string of characters using a stroke font:
 **/

void
DoStrokeString( float x, float y, float z, float ht, char *s )
{
	char c;			/* one character to print		*/
	float sf;		/* the scale factor			*/

	glPushMatrix();
		glTranslatef( x, y, z );
		sf = ht / ( 119.05 + 33.33 );
		glScalef( sf, sf, sf );
		for( ; ( c = *s ) != '\0'; s++ )
		{
			glutStrokeCharacter( GLUT_STROKE_ROMAN, c );
		}
	glPopMatrix();
}



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

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

	/* start up the mui window:					*/
	InitMui();

	/* setup the display mode:					*/
	/* ( *must* be done before call to glutCreateWindow() )		*/
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );

	/* set the initial window configuration:			*/
	glutInitWindowSize( INIT_WINDOW_SIZE, INIT_WINDOW_SIZE );
	glutInitWindowPosition( WIN_LEFT, WIN_BOTTOM );

	/* open the window and set its title:				*/
	GrWindow = glutCreateWindow( WINDOWTITLE );
	glutSetWindowTitle( WINDOWTITLE );

	/* setup the clear values:					*/
	glClearColor( BACKGROUND_COLOR );

	/* initialize the pop-up menus:					*/
	ObjectMenu = glutCreateMenu( DoObjectMenu );
	glutAddMenuEntry( "Sphere",	 0 );
	glutAddMenuEntry( "Cube",	 1 );
	glutAddMenuEntry( "Cone",	 2 );
	glutAddMenuEntry( "Torus",	 3 );
	glutAddMenuEntry( "Dodecahedron",4 );
	glutAddMenuEntry( "Octahedron",	 5 );
	glutAddMenuEntry( "Tetrahedron", 6 );
	glutAddMenuEntry( "Icosahedron", 7 );
	glutAddMenuEntry( "Teapot",	 8 );

	ProjMenu = glutCreateMenu( DoProjMenu );
	glutAddMenuEntry( "Orthographic", ORTHO );
	glutAddMenuEntry( "Perspective", PERSP );

	ColorMenu = glutCreateMenu( DoColorMenu );
	glutAddMenuEntry( "Red",	RED );
	glutAddMenuEntry( "Yellow",	YELLOW );
	glutAddMenuEntry( "Green",	GREEN );
	glutAddMenuEntry( "Cyan",	CYAN );
	glutAddMenuEntry( "Blue",	BLUE );
	glutAddMenuEntry( "Magenta",	MAGENTA );
	glutAddMenuEntry( "White",	WHITE );
	glutAddMenuEntry( "Black",	BLACK );

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

	MainMenu = glutCreateMenu( DoMainMenu );
	glutAddSubMenu( "Object", ObjectMenu );
	glutAddSubMenu( "Projection", ProjMenu );
	glutAddSubMenu( "Set Color", ColorMenu );
	glutAddSubMenu( "Axes", AxesMenu );
	glutAddMenuEntry( "Reset", RESET );
	glutAddMenuEntry( "Quit",  QUIT );

	/* attach the pop-up menu to the right mouse button:		*/
	glutAttachMenu( GLUT_RIGHT_BUTTON );

	/* create the display structures that will not change:		*/
	InitLists();

	/* setup the callback routines:					*/
	glutSetWindow( GrWindow );

	/* 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( DoKeyboard );
	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( NULL );
	glutTimerFunc( 0, NULL, 0 );
}


/**
 ** initialize the display lists that will not change:
 **/

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

	/* create the objects:						*/
	ObjectLists[0] = glGenLists( 1 );
	glNewList( ObjectLists[0], GL_COMPILE );
		glutWireSphere( 1.0, 20, 20 );
	glEndList();

	ObjectLists[1] = glGenLists( 1 );
	glNewList( ObjectLists[1], GL_COMPILE );
		glutWireCube( 1.5 );
	glEndList();

	ObjectLists[2] = glGenLists( 1 );
	glNewList( ObjectLists[2], GL_COMPILE );
		glutWireCone( 1.0, 1.5, 20, 20 );
	glEndList();

	ObjectLists[3] = glGenLists( 1 );
	glNewList( ObjectLists[3], GL_COMPILE );
		glutWireTorus( 0.5, 0.75, 20, 20 );
	glEndList();

	ObjectLists[4] = glGenLists( 1 );
	glNewList( ObjectLists[4], GL_COMPILE );
		glPushMatrix();
			glScalef( 0.75, 0.75, 0.75 );
			glutWireDodecahedron();
		glPopMatrix();
	glEndList();

	ObjectLists[5] = glGenLists( 1 );
	glNewList( ObjectLists[5], GL_COMPILE );
		glutWireOctahedron();
	glEndList();

	ObjectLists[6] = glGenLists( 1 );
	glNewList( ObjectLists[6], GL_COMPILE );
		glutWireTetrahedron();
	glEndList();

	ObjectLists[7] = glGenLists( 1 );
	glNewList( ObjectLists[7], GL_COMPILE );
		glutWireIcosahedron();
	glEndList();

	ObjectLists[8] = glGenLists( 1 );
	glNewList( ObjectLists[8], GL_COMPILE );
		glutWireTeapot( 1.0 );
	glEndList();

	/* create the axes:						*/
	LocalAxesList = glGenLists( 1 );
	glNewList( LocalAxesList, GL_COMPILE );
		glColor3f( LAXES_COLOR );
		glLineWidth( LAXES_WIDTH );
			Axes( 1.5 );
		glLineWidth( 1. );
	glEndList();

	GlobalAxesList = glGenLists( 1 );
	glNewList( GlobalAxesList, GL_COMPILE );
		glColor3f( GAXES_COLOR );
		glLineWidth( GAXES_WIDTH );
			Axes( 3.0 );
		glLineWidth( 1. );
	glEndList();
}


/**
 ** initialize the mui window:
 **/

void
InitMui()
{
	char str[256];		/* sprintf buffer			*/

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

	/* setup the mui window:					*/
	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
	glutInitWindowSize( MDX, MDY );
	glutInitWindowPosition( MX0, MY0 );
	MuiWindow = glutCreateWindow( MWINDOWTITLE );
	glutSetWindowTitle( MWINDOWTITLE );

	/* setup the built-in mui callbacks:				*/
	glutSetWindow( MuiWindow );
	muiInit();

	/* start a mui list:						*/
	muiNewUIList( 1 );
	muiSetActiveUIList( 1 );

	/* setup the quit button:					*/
	/* int xmin, int xmax, int ymin, int ymax			*/
	QuitButton = muiNewButton( 10, 50, 10, 35 );
	muiLoadButton( QuitButton, "Quit" );
	muiSetVisible( QuitButton, TRUE );
	muiSetActive( QuitButton, TRUE );
	muiSetEnable( QuitButton, FALSE );
        muiSetCallback( QuitButton, DoButtons );
	muiAddToUIList( 1, QuitButton );

	/* setup the text slider:					*/
	/* int xmin, int ymin, int xmax, int scenter, int shalf		*/
    TextSlider = muiNewHSlider( 10, MDY-40, MDX-10, 10, 10 );
    muiSetVisible( TextSlider, TRUE );
    muiSetActive( TextSlider, TRUE );
    muiSetEnable( TextSlider, TRUE );
    muiSetCallback( TextSlider, DoSliders );
    muiAddToUIList( 1, TextSlider );
	TextValue = TEXTMIN;
	muiSetHSValue( TextSlider, (TextValue-TEXTMIN)/(TEXTMAX-TEXTMIN) );
	sprintf( str, "%6.2f", TextValue );
	TextLabel = muiNewBoldLabel( MDX/2, MDY-55, str );
    muiAddToUIList( 1, TextLabel );
}


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

void
MouseButton
(	int button,		/* GLUT_*_BUTTON			*/
	int state,		/* GLUT_UP or GLUT_DOWN			*/
	int x, int y		/* where mouse was when button 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		/* 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 )
	{
		Xang += ( ANGFACT*dy );
		Yang += ( ANGFACT*dx );
	}

	if( ActiveButton & MIDDLE )
	{
		Scale += SCLFACT * (float) ( dx - dy );

		/* keep object from turning inside-out:			*/
		if( Scale < 0. )
			Scale = 0.;
	}

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

	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


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

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

	/* gracefully close the graphics window:			*/
	glutDestroyWindow( GrWindow );

	/* 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()
{
	ActiveButton = 0;
	AxesOnOff = ON;
	Projection = PERSP;
	Red = 1.;	Green = 1.;	Blue = 1.;	/* white	*/
	Scale = 1.0;
	WhichObject = 0;
	Xang = Yang = 0.;

	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


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

void
Resize( int width, int height )
{
	if( Debug )
		fprintf( stderr, "ReSize: %d, %d\n", width, height );

	/* don't really need to do anything since window size is	*/
	/* checked each time in Display():				*/

	glutSetWindow( GrWindow );
	glutPostRedisplay();
}


/**
 ** return the time in floating point seconds
 **

double
Seconds()
{
	struct timeval tv;
	double d;

	gettimeofday( &tv, 0 );
	d = tv.tv_sec + 0.000001 * tv.tv_usec;
	return d;
}


/**
 ** 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( GrWindow );
		glutPostRedisplay();
	}
	else
	{
		/* could optimize by keeping track of the fact		*/
		/* that the window is not visible and avoid		*/
		/* redrawing it later ...				*/
	}
}