/* * Open GL Physics of Man - 'oo-glut.c' */#include <math.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <time.h>#include "gl.h"#include "glut.h"#if defined(__MWERKS__) || defined(__SC__)	#include <console.h>#endif// Begin user alterable defines#define kPsScreenWidth		270.0	// in mm#define kPsScreenHeight		202.0	// in mm#define kPsScreenDistance	570.0	// in mm#define kPsEyeSeparation	65.0	// in mm#define kPsFlashTime		1.5		// in sec#define kPsRectWidth		200.0#define kPsRectHeight		150.0#define kPsNoniusSize		4.0#define kPsLineLength		20.0#define kPsLineLocation		35.0#define kPsLineWidth		2.0// End user alterable defines/* * Open GL Physics of Man - Defines and variables */#define kPsFNameLength		128#define kPsSNameLength		32enum {	kPsRed = 1,	kPsGreen,	kPsYellow};struct sPsTrialRecord {	int		type;		// red=1/green=2/red+green=3	float	line_z;		// line at z location	int 	answer;};typedef struct sPsTrialRecord	sPsTrialRecord;typedef sPsTrialRecord	*		sPsTrialPtr;char gPsReadFormat[ ]	= "%d  %f\n";char gPsWriteFormat[ ]	= "%d\t%f\t%d\n";#define kPsInputItems	2#define kPsRead( a )	&a->type, \						&a->line_z#define kPsWrite( a )	a->type, \						a->line_z, \						a->answer#define kPsMaxTrials	1500enum {	kPsShowStart = 1,	kPsShowNonius,	kPsShowStimulus,	kPsShowObserver};struct sPsSliderRecord {	Point w;	float fx;	float fy;};typedef struct sPsSliderRecord	sPsSliderRecord;typedef sPsSliderRecord	*		sPsSliderPtr;int				gPsInitLevel;Boolean			gPsRandomize;float			gPsScreenWidth;float			gPsScreenHeight;float			gPsScreenDistance;float			gPsEyeSeparation;float			gPsFlashTime;char			gPsInputFileName[ kPsFNameLength ];char			gPsOutputFileName[ kPsFNameLength ];char			gPsSubjectName[	kPsSNameLength	];int				gPsNrOfTrials;sPsTrialRecord	gPsTrialRecs[ kPsMaxTrials ];int				gPsSequenceArr[ kPsMaxTrials ];int				gPsSequenceIndex;sPsSliderRecord	gPsTheSlider;int				gPsDrawMode;/* * Open GL Physics of Man - Prototypes */void	PsDraw( void );void	PsReportError( const char *txt, ... );Boolean	PsReadDataSetFile( void );Boolean	PsSaver( sPsTrialPtr tp );Boolean	PsOpenOutputFile( void );void	PsCloseOutputFile( void );void	PsDrawStr( char *str );Boolean	PsStartOpenGL( void );void	PsCloseOpenGL( void );int		PsInit( int argc, char **argv );int		PsExit( int init_level );Boolean	PsSetupExperiment( void );void	PsKey( unsigned char key, int x, int y );void	PsMouse( int button, int state, int x, int y );void	PsMotion( int x, int y );void	PsIdle( void );/* * Open GL Physics of Man -  Drawing routines *//* *           +y *           |  / -z *           | / *           |/ *   -x -----*----- +x *          /| *         / | *     +z /  | *           -y */#define kPsObserver_X		0.0			// relative to screencenter (in mm)#define kPsObserver_Y		0.0			// relative to screencenter (in mm)#define kPsFarClip 			10000.0#define	kPsCyclopicEye		0#define	kPsLeftEye			1#define	kPsRightEye			2#define kPsPI				3.1415926535897/* * */static void PsSetEye( short theEye ){	float halfIOD;	double nearClip, n, sw2, sh2;	double f_left, f_right, f_top, f_bottom;	if( theEye == kPsLeftEye ) {		glColor3f( 1.0, 0.0, 0.0 );		halfIOD = gPsEyeSeparation * 0.5;	} else if( theEye == kPsRightEye ) {		glColor3f( 0.0, 1.0, 0.0 );		halfIOD = gPsEyeSeparation * -0.5;	} else {		glColor3f( 1.0, 1.0, 0.0 );		halfIOD = 0.0;	}    glMatrixMode( GL_PROJECTION ); 	glLoadIdentity( );	nearClip = gPsScreenDistance - (gPsScreenDistance * 0.9);	n = nearClip / gPsScreenDistance;	sw2 = gPsScreenWidth * 0.5;	sh2 = gPsScreenHeight * 0.5;	f_left   = n * (-kPsObserver_X - sw2 + halfIOD);	f_right  = n * (-kPsObserver_X + sw2 + halfIOD);	f_top    = n * (-kPsObserver_Y + sh2);	f_bottom = n * (-kPsObserver_Y - sh2);	glFrustum(	f_left,   f_right,				f_bottom, f_top,				nearClip, kPsFarClip );	glTranslatef( halfIOD, 0.0, -gPsScreenDistance );	glMatrixMode( GL_MODELVIEW );	glLoadIdentity( );}/* * Draw rectangle */static void PsDrawRect( float width, float height ){	float w2, h2;	w2 = width / 2.0;	h2 = height / 2.0;	glBegin( GL_LINE_LOOP );	glVertex2f( -w2,  h2 );	glVertex2f(  w2,  h2 );	glVertex2f(  w2, -h2 );	glVertex2f( -w2, -h2 );	glEnd( );}/* * Draw text */static void PsDrawText( float x, float y, float z, float scale, char *str ){	glPushMatrix( );	glTranslatef( x, y, z );	glScalef( scale, scale, scale );	PsDrawStr( str );	glPopMatrix( );}/* * Draw text */static void PsDrawStart( void ){	float w2, h2;	char str[ 256 ];	PsSetEye( kPsCyclopicEye );	glPushMatrix( );	PsDrawRect( kPsRectWidth, kPsRectHeight );	w2 = kPsRectWidth / 2.0;	h2 = kPsRectHeight / 2.0;	glBegin( GL_LINES );	glVertex2f( -1000,     0 );	glVertex2f(   -w2,     0 );	glVertex2f(    w2,     0 );	glVertex2f(  1000,     0 );	glVertex2f(     0,  1000 );	glVertex2f(     0,    h2 );	glVertex2f(     0,   -h2 );	glVertex2f(     0, -1000 );	glEnd( );	glPopMatrix( );	PsDrawText( -66.0, 50.0, 0.0, 0.1, "This rectangle must be" );	sprintf( str, "width  = %.2f", kPsRectWidth );	PsDrawText( -46.0, 40.0, 0.0, 0.1, str );	sprintf( str, "height = %.2f", kPsRectHeight );	PsDrawText( -46.0, 30.0, 0.0, 0.1, str );	PsDrawText( -36.0,  0.0, 0.0, 0.1, "Hit spacebar" );	sprintf( str, "-sw = %.2f", gPsScreenWidth );	PsDrawText( -36.0, -25.0, 0.0, 0.1, str );	sprintf( str, "-sh = %.2f", gPsScreenHeight );	PsDrawText( -36.0, -35.0, 0.0, 0.1, str );	sprintf( str, "-sd = %.2f", gPsScreenDistance );	PsDrawText( -36.0, -45.0, 0.0, 0.1, str );	sprintf( str, "-es = %.2f", gPsEyeSeparation );	PsDrawText( -36.0, -55.0, 0.0, 0.1, str );	sprintf( str, "-ft = %.2f", gPsFlashTime );	PsDrawText( -36.0, -65.0, 0.0, 0.1, str );}/* * Draw nonius */static void PsDrawNonius( int eye, float size ){	glPushMatrix( );	PsDrawRect( size, size );	if( eye == kPsLeftEye ) {		glBegin( GL_LINES );		glVertex2f( 0.0, size );		glVertex2f( 0.0, size * 2.0 );		glEnd( );	} else {		glBegin( GL_LINES );		glVertex2f( 0.0, -size );		glVertex2f( 0.0, -size * 2.0 );		glEnd( );	}	glPopMatrix( );}/* * Draw lines */static void PsDrawLines( float z ){	float l;	l = kPsLineLength * 0.5;	glPushMatrix( );	glTranslatef( 0.0, 0.0, z );	glBegin( GL_LINES );	glVertex3f( -kPsLineLocation, l, 0.0 );	glVertex3f( -kPsLineLocation, -l, 0.0 );	glVertex3f( kPsLineLocation, l, 0.0 );	glVertex3f( kPsLineLocation, -l, 0.0 );	glEnd( );	glPushMatrix( );}/* * Draw text */static void PsDrawStimulus( Boolean lines ){	sPsTrialPtr	tp;		tp = &gPsTrialRecs[ gPsSequenceArr[ gPsSequenceIndex ] ];	PsSetEye( kPsLeftEye );	PsDrawNonius( kPsLeftEye, kPsNoniusSize );	if( lines && (tp->type & kPsRed) ) {		PsDrawLines( tp->line_z );	}	PsSetEye( kPsRightEye );	PsDrawNonius( kPsRightEye, kPsNoniusSize );	if( lines && (tp->type & kPsGreen) ) {		PsDrawLines( tp->line_z );	}}/* * Draw observer */static void PsDrawObserver( void ){	float angle, x, y, r;	x = 0.0;	y = -75.0;	r = 10.0;	PsSetEye( kPsCyclopicEye );	glPushMatrix( );	glBegin( GL_LINES );	glVertex2f( -75.0, 0.0 );	glVertex2f(  75.0, 0.0 );	glVertex2f( -50.0, gPsTheSlider.fy );	glVertex2f(  50.0, gPsTheSlider.fy );	glEnd();	glPopMatrix();	glPushMatrix( );	glTranslatef( x, y, 0);	glBegin( GL_LINE_LOOP) ;	for(angle = 2 * kPsPI; angle >= 0; angle -= kPsPI / 12) {		glVertex2f( r * cos( angle ), r * sin( angle ) );	}	glEnd( );	glBegin( GL_LINES );	glVertex2f( -r, 0.0 );	glVertex2f( -r * 2.0, 0.0 );	glVertex2f(  r, 0.0 );	glVertex2f(  r * 2.0, 0.0 );	glVertex2f( -r * 0.2, r );	glVertex2f( 0.0, r + (r * 0.5) );	glVertex2f( r * 0.2, r );	glVertex2f( 0.0, r + (r * 0.5) );	glEnd( );	glPopMatrix( );}/* * Draw */void PsDraw( void ){	glClear( GL_COLOR_BUFFER_BIT );	switch( gPsDrawMode ) {	case kPsShowStart:		PsDrawStart( );		break;	case kPsShowNonius:		PsDrawStimulus( false );		break;	case kPsShowStimulus:		PsDrawStimulus( true );		break;	case kPsShowObserver:		PsDrawObserver( );		break;	}	glutSwapBuffers( );}/* * Open GL Physics of Man - File related routines */FILE *gPsOfp;/* * Display any error */void PsReportError( const char *txt, ... ){	va_list	ap;	char	str[ 256 ];	va_start( ap, txt );	vsprintf( str, txt, ap );	va_end( ap );	fprintf( stderr, "%s\n", str );}/* * Display file error */static void PsFileError( char *error, char *file ){	PsReportError( "Error %s file - %s.", error, file );}/* * Read * *		returns true if successful *		returns false otherwise */static Boolean PsReadSetting( char *str, int line ){	int				rs;	sPsTrialRecord	*ir;	if( gPsNrOfTrials < kPsMaxTrials ) {		ir = &gPsTrialRecs[ gPsNrOfTrials ];		rs = sscanf( str, gPsReadFormat, kPsRead( ir ) );		if( rs != kPsInputItems ) {			PsReportError( "Error in line %d of input file.", line );			return false;		}		gPsNrOfTrials++;	} else {		PsReportError( "Error: too many image types" );		return false;	}	return true;}/* * Read data set file * *		returns false on an error *		returns true otherwise */Boolean PsReadDataSetFile( void ){	FILE	*ifp;	int		rs;	int		line;	char	str[ 128 ];	gPsNrOfTrials = 0;	line = 1;	ifp = fopen( gPsInputFileName, "r" ); // Open info file	if( ifp != NULL ) {					// If open was file successful		do {			if( fgets( str, 128, ifp ) ) {				if( str[ 0 ] != '#' ) {					rs = PsReadSetting( str, line );				} else {					rs = true;				}			} else {				rs = EOF;			}			line++;		} while( rs == true );		fclose( ifp );		if( (gPsNrOfTrials == 0) && (rs == EOF) ) {			PsReportError( "Error: no image types found" );			rs = false;		}		if( rs == EOF ) {			rs = true;		}	} else {		PsFileError( "opening", gPsInputFileName );		rs = false;	}	return rs;}/* * Write results * *		returns true if successful *		returns false otherwise */Boolean PsSaver( sPsTrialPtr tp ){	if( fprintf( gPsOfp, gPsWriteFormat, kPsWrite( tp ) ) == EOF ) {		PsFileError( "writing", gPsOutputFileName );		return false;	}	return true;}/* * Open output file append mode * *		returns true if successful *		returns false otherwise */ Boolean PsOpenOutputFile( void ){	long		t;	int			rs;	static char	str1[ ] = "#oo-test\tsw\t%.2f\tsh\t%.2f";	static char	str2[ ] = "\tsd\t%.2f\tes\t%.2f";	static char	str3[ ] = "\tft\t%.3f";	static char	str4[ ] = "\tfile\t%s\t%s";	if( (gPsOfp = fopen( gPsOutputFileName, "a" )) != NULL ) {		rs = fprintf( gPsOfp, str1, gPsScreenWidth, gPsScreenHeight );		if( rs == EOF ) {			return false;		}		rs = fprintf( gPsOfp, str2, gPsScreenDistance, gPsEyeSeparation );		if( rs == EOF ) {			return false;		}		rs = fprintf( gPsOfp, str3, gPsFlashTime );		if( rs == EOF ) {			return false;		}		t = time( NULL );		rs = fprintf( gPsOfp, str4, gPsInputFileName, ctime( &t ) );		if( rs == EOF ) {			return false;		}	} else {		PsFileError( "opening", gPsOutputFileName );		return false;	}	return true;}/* * Close output file */void PsCloseOutputFile( void ){	if( fclose( gPsOfp ) == EOF ) {		// If closing not ok ...		PsFileError( "closing", gPsOutputFileName );	}}/* * Open GL Physics of Man - Draw string */enum {	kPM_BGN = 1,	kPM_NXT,	kPM_END,	kPM_ADV};	short stroke032[ ] = { // space	kPM_ADV, 60,  0};short stroke033[ ] = { // !	kPM_BGN, 20,  0,	kPM_END, 20, 10,	kPM_BGN, 20, 20,	kPM_END, 20, 60,	kPM_ADV, 60,  0};short stroke034[ ] = { // "	kPM_BGN, 10, 60,	kPM_END, 10, 40,	kPM_BGN, 30, 60,	kPM_END, 30, 40,	kPM_ADV, 60,  0};short stroke035[ ] = { // #	kPM_BGN, 10,  0,	kPM_END, 10, 50,	kPM_BGN, 30,  0,	kPM_END, 30, 50,	kPM_BGN, 40, 10,	kPM_END,  0, 10,	kPM_BGN,  0, 40,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke036[ ] = { // $	kPM_BGN, 20,  0,	kPM_END, 20, 60,	kPM_BGN,  0, 10,	kPM_NXT, 30, 10,	kPM_NXT, 40, 20,	kPM_NXT, 30, 30,	kPM_NXT, 10, 30,	kPM_NXT,  0, 40,	kPM_NXT, 10, 50,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short stroke037[ ] = { // %	kPM_BGN,  0,  0,	kPM_NXT,  0, 10,	kPM_NXT, 40, 50,	kPM_END, 40, 60,	kPM_BGN, 10, 60,	kPM_NXT,  0, 60,	kPM_NXT,  0, 50,	kPM_NXT, 10, 50,	kPM_END, 10, 60,	kPM_BGN, 40, 10,	kPM_NXT, 30, 10,	kPM_NXT, 30,  0,	kPM_NXT, 40,  0,	kPM_END, 40, 10,	kPM_ADV, 60,  0};short stroke038[ ] = { // &	kPM_BGN, 40, 20,	kPM_NXT, 20,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT,  0, 20,	kPM_NXT, 20, 40,	kPM_NXT, 20, 50,	kPM_NXT, 10, 60,	kPM_NXT,  0, 50,	kPM_NXT,  0, 40,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke039[ ] = { // '	kPM_BGN, 20, 60,	kPM_END, 20, 40,	kPM_ADV, 60,  0};short stroke040[ ] = { // (	kPM_BGN, 30,  0,	kPM_NXT, 10, 20,	kPM_NXT, 10, 40,	kPM_END, 30, 60,	kPM_ADV, 60,  0};short stroke041[ ] = { // )	kPM_BGN, 10,  0,	kPM_NXT, 30, 20,	kPM_NXT, 30, 40,	kPM_END, 10, 60,	kPM_ADV, 60,  0};short stroke042[ ] = { // *	kPM_BGN, 20, 10,	kPM_END, 20, 50,	kPM_BGN,  0, 50,	kPM_END, 40, 10,	kPM_BGN, 40, 30,	kPM_END,  0, 30,	kPM_BGN,  0, 10,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short stroke043[ ] = { // +	kPM_BGN, 20, 10,	kPM_END, 20, 50,	kPM_BGN,  0, 30,	kPM_END, 40, 30,	kPM_ADV, 60,  0};short stroke044[ ] = { // ,	kPM_BGN,  0, -10,	kPM_NXT, 10,   0,	kPM_END, 10,  10,	kPM_ADV, 60,   0};short stroke045[ ] = { // -	kPM_BGN,  0, 30,	kPM_END, 40, 30,	kPM_ADV, 60,  0};short stroke046[ ] = { // .	kPM_BGN, 10,  0,	kPM_END, 20,  0,	kPM_ADV, 60,  0};short stroke047[ ] = { // /	kPM_BGN,  0, 10,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short stroke048[ ] = { // 0	kPM_BGN,  0, 10,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_NXT,  0, 50,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short stroke049[ ] = { // 1	kPM_BGN, 10,  0,	kPM_END, 30,  0,	kPM_BGN, 20,  0,	kPM_NXT, 20, 60,	kPM_END, 10, 50,	kPM_ADV, 60,  0};short stroke050[ ] = { // 2	kPM_BGN,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 40,	kPM_NXT,  0,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke051[ ] = { // 3	kPM_BGN,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 20,	kPM_NXT, 30, 30,	kPM_END, 20, 30,	kPM_BGN, 30, 30,	kPM_NXT, 40, 40,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_END,  0, 50,	kPM_ADV, 60,  0};short stroke052[ ] = { // 4	kPM_BGN, 30,  0,	kPM_NXT, 30, 60,	kPM_NXT,  0, 30,	kPM_NXT,  0, 20,	kPM_END, 40, 20,	kPM_ADV, 60,  0};short stroke053[ ] = { // 5	kPM_BGN,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 30,	kPM_NXT, 30, 40,	kPM_NXT,  0, 40,	kPM_NXT,  0, 60,	kPM_END, 40, 60,	kPM_ADV, 60,  0};short stroke054[ ] = { // 6	kPM_BGN,  0, 30,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 20,	kPM_NXT, 30, 30,	kPM_NXT,  0, 30,	kPM_NXT,  0, 40,	kPM_NXT, 20, 60,	kPM_END, 30, 60,	kPM_ADV, 60,  0};short stroke055[ ] = { // 7	kPM_BGN,  0, 60,	kPM_NXT, 40, 60,	kPM_NXT, 20, 20,	kPM_END, 20,  0,	kPM_ADV, 60,  0};short stroke056[ ] = { // 8	kPM_BGN, 10, 30,	kPM_NXT,  0, 20,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 20,	kPM_NXT, 30, 30,	kPM_NXT, 10, 30,	kPM_NXT,  0, 40,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 40,	kPM_END, 30, 30,	kPM_ADV, 60,  0};short stroke057[ ] = { // 9	kPM_BGN, 10,  0,	kPM_NXT, 20,  0,	kPM_NXT, 40, 20,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_NXT,  0, 50,	kPM_NXT,  0, 40,	kPM_NXT, 10, 30,	kPM_END, 40, 30,	kPM_ADV, 60,  0};short stroke058[ ] = { // :	kPM_BGN, 10, 10,	kPM_END, 20, 10,	kPM_BGN, 10, 40,	kPM_END, 20, 40,	kPM_ADV, 60,  0};short stroke059[ ] = { // ;	kPM_BGN, 10, -10,	kPM_NXT, 20,   0,	kPM_END, 20,  10,	kPM_BGN, 10,  40,	kPM_END, 20,  40,	kPM_ADV, 60,   0};short stroke060[ ] = { // <	kPM_BGN, 30, 10,	kPM_NXT, 10, 30,	kPM_END, 30, 50,	kPM_ADV, 60,  0};short stroke061[ ] = { // =	kPM_BGN,  0, 20,	kPM_END, 40, 20,	kPM_BGN, 40, 40,	kPM_END,  0, 40,	kPM_ADV, 60,  0};short stroke062[ ] = { // >	kPM_BGN, 10, 50,	kPM_NXT, 30, 30,	kPM_END, 10, 10,	kPM_ADV, 60,  0};short stroke063[ ] = { // ?	kPM_BGN, 10,  0,	kPM_END, 20,  0,	kPM_BGN, 20, 20,	kPM_NXT, 20, 30,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_END,  0, 50,	kPM_ADV, 60,  0};short stroke064[ ] = { // @	kPM_BGN, 30,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 20,	kPM_NXT, 20, 20,	kPM_NXT, 20, 40,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke065[ ] = { // A	kPM_BGN,  0,  0,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_END, 40,  0,	kPM_BGN,  0, 30,	kPM_END, 40, 30,	kPM_ADV, 60,  0};short stroke066[ ] = { // B	kPM_BGN,  0,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 20,	kPM_NXT, 30, 30,	kPM_END,  0, 30,	kPM_BGN, 30, 30,	kPM_NXT, 40, 40,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT,  0, 60,	kPM_END,  0,  0,	kPM_ADV, 60,  0};short stroke067[ ] = { // C	kPM_BGN, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_NXT,  0, 50,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_END, 40, 10,	kPM_ADV, 60,  0};short stroke068[ ] = { // D	kPM_BGN,  0,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT,  0, 60,	kPM_END,  0,  0,	kPM_ADV, 60,  0};short stroke069[ ] = { // E	kPM_BGN, 40,  0,	kPM_NXT,  0,  0,	kPM_NXT,  0, 60,	kPM_END, 40, 60,	kPM_BGN, 30, 30,	kPM_END,  0, 30,	kPM_ADV, 60,  0};short stroke070[ ] = { // F	kPM_BGN,  0,  0,	kPM_NXT,  0, 60,	kPM_END, 40, 60,	kPM_BGN, 30, 30,	kPM_END,  0, 30,	kPM_ADV, 60,  0};short stroke071[ ] = { // G	kPM_BGN, 20, 30,	kPM_NXT, 40, 30,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short stroke072[ ] = { // H	kPM_BGN,  0,  0,	kPM_END,  0, 60,	kPM_BGN,  0, 30,	kPM_END, 40, 30,	kPM_BGN, 40, 60,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke073[ ] = { // I	kPM_BGN, 10,  0,	kPM_END, 30,  0,	kPM_BGN, 20,  0,	kPM_END, 20, 60,	kPM_BGN, 10, 60,	kPM_END, 30, 60,	kPM_ADV, 60,  0};short stroke074[ ] = { // J	kPM_BGN,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_END, 40, 60,	kPM_ADV, 60,  0};short stroke075[ ] = { // K	kPM_BGN,  0,  0,	kPM_END,  0, 60,	kPM_BGN, 40, 60,	kPM_NXT,  0, 30,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke076[ ] = { // L	kPM_BGN,  0, 60,	kPM_NXT,  0,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke077[ ] = { // M	kPM_BGN,  0,  0,	kPM_NXT,  0, 60,	kPM_NXT, 20, 40,	kPM_NXT, 40, 60,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke078[ ] = { // N	kPM_BGN,  0,  0,	kPM_NXT,  0, 60,	kPM_NXT, 40,  0,	kPM_END, 40, 60,	kPM_ADV, 60,  0};short stroke079[ ] = { // O	kPM_BGN,  0, 10,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_END,  0, 10,	kPM_ADV, 60,  0};short stroke080[ ] = { // P	kPM_BGN,  0,  0,	kPM_NXT,  0, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 40,	kPM_NXT, 30, 30,	kPM_END,  0, 30,	kPM_ADV, 60,  0};short stroke081[ ] = { // Q	kPM_BGN,  0, 10,	kPM_NXT,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_END,  0, 10,	kPM_BGN, 30, 10,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke082[ ] = { // R	kPM_BGN,  0,  0,	kPM_NXT,  0, 60,	kPM_NXT, 30, 60,	kPM_NXT, 40, 50,	kPM_NXT, 40, 40,	kPM_NXT, 30, 30,	kPM_END,  0, 30,	kPM_BGN, 10, 30,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke083[ ] = { // S	kPM_BGN, 40, 50,	kPM_NXT, 30, 60,	kPM_NXT, 10, 60,	kPM_NXT,  0, 50,	kPM_NXT,  0, 40,	kPM_NXT, 10, 30,	kPM_NXT, 30, 30,	kPM_NXT, 40, 20,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_END,  0, 10,	kPM_ADV, 60,  0};short stroke084[ ] = { // T	kPM_BGN,  0, 60,	kPM_END, 40, 60,	kPM_BGN, 20, 60,	kPM_END, 20,  0,	kPM_ADV, 60,  0};short stroke085[ ] = { // U	kPM_BGN, 40, 60,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_END,  0, 60,	kPM_ADV, 60,  0};short stroke086[ ] = { // V	kPM_BGN,  0, 60,	kPM_NXT,  0, 30,	kPM_NXT, 20,  0,	kPM_NXT, 40, 30,	kPM_END, 40, 60,	kPM_ADV, 60,  0};short stroke087[ ] = { // W	kPM_BGN,  0, 60,	kPM_NXT,  0,  0,	kPM_NXT, 20, 20,	kPM_NXT, 40,  0,	kPM_END, 40, 60,	kPM_ADV, 60,  0};short stroke088[ ] = { // X	kPM_BGN,  0,  0,	kPM_END, 40, 60,	kPM_BGN,  0, 60,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke089[ ] = { // Y	kPM_BGN, 20,  0,	kPM_NXT, 20, 30,	kPM_END,  0, 60,	kPM_BGN, 40, 60,	kPM_END, 20, 30,	kPM_ADV, 60,  0};short stroke090[ ] = { // Z	kPM_BGN,  0, 60,	kPM_NXT, 40, 60,	kPM_NXT,  0,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke091[ ] = { // [	kPM_BGN, 30,  0,	kPM_NXT, 10,  0,	kPM_NXT, 10, 60,	kPM_END, 30, 60,	kPM_ADV, 60,  0};short stroke092[ ] = { // '\'	kPM_BGN,  0, 50,	kPM_END, 40, 10,	kPM_ADV, 60,  0};short stroke093[ ] = { // ]	kPM_BGN, 10, 60,	kPM_NXT, 30, 60,	kPM_NXT, 30,  0,	kPM_END, 10,  0,	kPM_ADV, 60,  0};short stroke094[ ] = { // ^	kPM_BGN,  0, 40,	kPM_NXT, 20, 60,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke095[ ] = { // _	kPM_BGN,  0,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke096[ ] = { // `	kPM_BGN, 10, 60,	kPM_NXT, 10, 50,	kPM_END, 20, 40,	kPM_ADV, 60,  0};short stroke097[ ] = { // a	kPM_BGN, 10, 40,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_NXT, 40,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT, 10, 20,	kPM_END, 40, 20,	kPM_ADV, 60,  0};short stroke098[ ] = { // b	kPM_BGN,  0, 60,	kPM_NXT,  0,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 40, 30,	kPM_NXT, 30, 40,	kPM_END,  0, 40,	kPM_ADV, 60,  0};short stroke099[ ] = { // c	kPM_BGN, 40, 30,	kPM_NXT, 30, 40,	kPM_NXT, 10, 40,	kPM_NXT,  0, 30,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke100[ ] = { // d	kPM_BGN, 40, 60,	kPM_NXT, 40,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT,  0, 30,	kPM_NXT, 10, 40,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke101[ ] = { // e	kPM_BGN, 40,  0,	kPM_NXT, 10,  0,	kPM_NXT,  0, 10,	kPM_NXT,  0, 30,	kPM_NXT, 10, 40,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_NXT, 40, 20,	kPM_END,  0, 20,	kPM_ADV, 60,  0};short stroke102[ ] = { // f	kPM_BGN, 10,  0,	kPM_NXT, 10, 50,	kPM_NXT, 20, 60,	kPM_NXT, 30, 60,	kPM_END, 40, 50,	kPM_BGN, 20, 30,	kPM_END,  0, 30,	kPM_ADV, 60,  0};short stroke103[ ] = { // g	kPM_BGN, 10, -20,	kPM_NXT, 30, -20,	kPM_NXT, 40, -10,	kPM_NXT, 40,  30,	kPM_NXT, 30,  40,	kPM_NXT, 10,  40,	kPM_NXT,  0,  30,	kPM_NXT,  0,  10,	kPM_NXT, 10,   0,	kPM_END, 40,   0,	kPM_ADV, 60,   0};short stroke104[ ] = { // h	kPM_BGN,  0,  0,	kPM_END,  0, 60,	kPM_BGN,  0, 40,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke105[ ] = { // i	kPM_BGN, 20,  0,	kPM_END, 20, 30,	kPM_BGN, 20, 40,	kPM_END, 20, 50,	kPM_ADV, 60,  0};short stroke106[ ] = { // j	kPM_BGN,  0, -20,	kPM_BGN, 10, -20,	kPM_NXT, 20, -10,	kPM_END, 20,  30,	kPM_BGN, 20,  40,	kPM_END, 20,  50,	kPM_ADV, 60,   0};short stroke107[ ] = { // k	kPM_BGN,  0,  0,	kPM_END,  0, 60,	kPM_BGN, 40, 40,	kPM_NXT, 20, 20,	kPM_END,  0, 20,	kPM_BGN, 20, 20,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke108[ ] = { // l	kPM_BGN, 20,  0,	kPM_NXT, 10,  0,	kPM_END, 10, 60,	kPM_ADV, 60,  0};short stroke109[ ] = { // m	kPM_BGN,  0,  0,	kPM_NXT,  0, 40,	kPM_NXT, 10, 40,	kPM_NXT, 20, 30,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_END, 40,  0,	kPM_BGN, 20,  0,	kPM_END, 20, 30,	kPM_ADV, 60,  0};short stroke110[ ] = { // n	kPM_BGN,  0,  0,	kPM_NXT,  0, 40,	kPM_NXT, 10, 30,	kPM_NXT, 20, 40,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke111[ ] = { // o	kPM_BGN,  0, 10,	kPM_NXT,  0, 30,	kPM_NXT, 10, 40,	kPM_NXT, 30, 40,	kPM_NXT, 40, 30,	kPM_NXT, 40, 10,	kPM_NXT, 30,  0,	kPM_NXT, 10,  0,	kPM_END,  0, 10,	kPM_ADV, 60,  0};short stroke112[ ] = { // p	kPM_BGN,  0, -20,	kPM_NXT,  0,  40,	kPM_NXT, 30,  40,	kPM_NXT, 40,  30,	kPM_NXT, 40,  10,	kPM_NXT, 30,   0,	kPM_END,  0,   0,	kPM_ADV, 60,   0};short stroke113[ ] = { // q	kPM_BGN, 40,   0,	kPM_NXT, 10,   0,	kPM_NXT,  0,  10,	kPM_NXT,  0,  30,	kPM_NXT, 10,  40,	kPM_NXT, 40,  40,	kPM_END, 40, -20,	kPM_ADV, 60,   0};short stroke114[ ] = { // r	kPM_BGN,  0,  0,	kPM_END,  0, 40,	kPM_BGN,  0, 20,	kPM_NXT, 20, 40,	kPM_NXT, 30, 40,	kPM_END, 40, 30,	kPM_ADV, 60,  0};short stroke115[ ] = { // s	kPM_BGN,  0,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_NXT, 30, 20,	kPM_NXT, 10, 20,	kPM_NXT,  0, 30,	kPM_NXT, 10, 40,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke116[ ] = { // t	kPM_BGN, 10, 40,	kPM_END, 40, 40,	kPM_BGN, 20, 60,	kPM_NXT, 20, 10,	kPM_NXT, 30,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke117[ ] = { // u	kPM_BGN,  0, 40,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke118[ ] = { // v	kPM_BGN,  0, 40,	kPM_NXT,  0, 20,	kPM_NXT, 20,  0,	kPM_NXT, 40, 20,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke119[ ] = { // w	kPM_BGN,  0, 40,	kPM_NXT,  0, 10,	kPM_NXT, 10,  0,	kPM_NXT, 20, 10,	kPM_NXT, 30,  0,	kPM_NXT, 40, 10,	kPM_END, 40, 40,	kPM_ADV, 60,  0};short stroke120[ ] = { // x	kPM_BGN,  0,  0,	kPM_END, 40, 40,	kPM_BGN,  0, 40,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke121[ ] = { // y	kPM_BGN,  0, -20,	kPM_NXT, 40,  20,	kPM_END, 40,  40,	kPM_BGN,  0,  40,	kPM_NXT,  0,  20,	kPM_END, 20,   0,	kPM_ADV, 60,   0};short stroke122[ ] = { // z	kPM_BGN,  0, 40,	kPM_NXT, 40, 40,	kPM_NXT,  0,  0,	kPM_END, 40,  0,	kPM_ADV, 60,  0};short stroke123[ ] = { // {	kPM_BGN, 30, 60,	kPM_NXT, 20, 50,	kPM_NXT, 20, 40,	kPM_NXT, 10, 30,	kPM_NXT, 20, 20,	kPM_NXT, 20, 10,	kPM_END, 30,  0,	kPM_ADV, 60,  0};short stroke124[ ] = { // |	kPM_BGN, 20, 60,	kPM_END, 20,  0,	kPM_ADV, 60,  0};short stroke125[ ] = { // }	kPM_BGN, 10, 60,	kPM_NXT, 20, 50,	kPM_NXT, 20, 40,	kPM_NXT, 30, 30,	kPM_NXT, 20, 20,	kPM_NXT, 20, 10,	kPM_END, 10,  0,	kPM_ADV, 60,  0};short stroke126[ ] = { // ~	kPM_BGN,  0, 50,	kPM_NXT, 10, 60,	kPM_NXT, 30, 40,	kPM_END, 40, 50,	kPM_ADV, 60,  0};short *strokeArray[ ] = {	stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032,	stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032,	stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032,	stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032, stroke032,	stroke032, stroke033, stroke034, stroke035, stroke036, stroke037, stroke038, stroke039,	stroke040, stroke041, stroke042, stroke043, stroke044, stroke045, stroke046, stroke047,	stroke048, stroke049, stroke050, stroke051, stroke052, stroke053, stroke054, stroke055,	stroke056, stroke057, stroke058, stroke059, stroke060, stroke061, stroke062, stroke063,	stroke064, stroke065, stroke066, stroke067, stroke068, stroke069, stroke070, stroke071,	stroke072, stroke073, stroke074, stroke075, stroke076, stroke077, stroke078, stroke079,	stroke080, stroke081, stroke082, stroke083, stroke084, stroke085, stroke086, stroke087,	stroke088, stroke089, stroke090, stroke091, stroke092, stroke093, stroke094, stroke095,	stroke096, stroke097, stroke098, stroke099, stroke100, stroke101, stroke102, stroke103,	stroke104, stroke105, stroke106, stroke107, stroke108, stroke109, stroke110, stroke111,	stroke112, stroke113, stroke114, stroke115, stroke116, stroke117, stroke118, stroke119,	stroke120, stroke121, stroke122, stroke123, stroke124, stroke125, stroke126, stroke032};/* * */static void pmDrawChar( unsigned char chr ){	short *theStroke;	short mode;	if( chr > 127 ) {		theStroke = stroke032;	} else {		theStroke = strokeArray[ chr ];	}	do {		mode = theStroke[ 0 ];		if( mode == kPM_BGN ) {			glBegin( GL_LINE_STRIP );			glVertex2s( theStroke[ 1 ], theStroke[ 2 ] );		} else if( mode == kPM_NXT ) {			glVertex2s( theStroke[ 1 ], theStroke[ 2 ] );		} else if( mode == kPM_END ) {			glVertex2s( theStroke[ 1 ], theStroke[ 2 ] );			glEnd();		} else if( mode == kPM_ADV ) {			glTranslatef( (float)theStroke[ 1 ], (float)theStroke[ 2 ], 0.0 );		}		theStroke = &theStroke[ 3 ];	} while( mode != kPM_ADV );}/* * */void PsDrawStr( char *str ){	int i, l;	l = strlen( str );	for( i = 0; i < l; i++ ) {		pmDrawChar( (unsigned char)*str++ );	}}/* * Open GL Physics of Man - Graphical Initialization */#define kPsWindow_ULX		10			// window upper left x (in pixels)#define kPsWindow_ULY 		45			// window upper left y (in pixels)#define kPsWindowWidth		640			// window width (in pixels)#define kPsWindowHeight 	480			// window height (in pixels)int gPsWindow;/* * */static void PsReshape( int w, int h ){	glViewport( 0, 0, w, h );}/* * */Boolean PsStartOpenGL( void ){	glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );	glutInitWindowPosition( kPsWindow_ULX, kPsWindow_ULY );	glutInitWindowSize( kPsWindowWidth, kPsWindowHeight );	gPsWindow = glutCreateWindow( "Physics of Man" );	glClearColor( 0.0, 0.0, 0.0, 0.0 );	glEnable( GL_COLOR_LOGIC_OP );	glLogicOp( GL_OR );	glDisable( GL_DITHER );	glLineWidth( kPsLineWidth );	glutDisplayFunc( PsDraw );	glutKeyboardFunc( PsKey );	glutMouseFunc( PsMouse );	glutPassiveMotionFunc( PsMotion );	glutReshapeFunc( PsReshape );	glutIdleFunc( PsIdle );	return true;}/* * */void PsCloseOpenGL( void ){	glutDestroyWindow( gPsWindow );}/* * Open GL Physics of Man - Initialization */#define kPsScrnW_Rsrc		128#define kPsScrnH_Rsrc		129#define kPsScrnD_Rsrc		130#define kPsEyeSep_Rsrc		131#define kPsFTime_Rsrc		132/* * Add default file extension if not specified */static void PsAddExtension( char *fnam, char *dext ){	if( strlen( fnam ) && *dext ) {		if( strchr( fnam, '.' ) == NULL ) {			strcat( fnam, dext );		}	}}/* * Copy pascal strings */void PsPStrCopy( StringPtr p1, StringPtr p2 ){	register short len;	len = *p2++ = *p1++;	while( --len >= 0 ) {		*p2++ = *p1++;	}}/* * */static void PsGetPrefValue( short thePref, float *value, float defvalue ){	StringHandle	txtH;	Str255			s;	txtH = GetString( thePref );	HLock( (Handle)txtH );	PsPStrCopy( *txtH, s );	HUnlock( (Handle)txtH );	p2cstr( s );	*value = atof( (char *)s );	if( *value <= 0.0 ) {		*value = defvalue;	}}/* * */static void PsSetPrefValue( short thePref, float value ){	StringHandle	txtH;	char			s[ 255 ];	sprintf( s, "%f", value );	c2pstr( s );	txtH = GetString( thePref );	HLock( (Handle)txtH );	PsPStrCopy( (StringPtr)s, *txtH );	HUnlock( (Handle)txtH );	ChangedResource( (Handle)txtH );	WriteResource( (Handle)txtH );}/* * */static Boolean PsParseArgs( int argc, char **argv ){	int i;	srand( clock( ) );	gPsRandomize = true;	PsGetPrefValue( kPsScrnW_Rsrc,	&gPsScreenWidth,	kPsScreenWidth );	PsGetPrefValue( kPsScrnH_Rsrc,	&gPsScreenHeight,	kPsScreenHeight );	PsGetPrefValue( kPsScrnD_Rsrc,	&gPsScreenDistance,	kPsScreenDistance );	PsGetPrefValue( kPsEyeSep_Rsrc,	&gPsEyeSeparation,	kPsEyeSeparation );	PsGetPrefValue( kPsFTime_Rsrc,	&gPsFlashTime,		kPsFlashTime );	strcpy( gPsInputFileName, "default" );	strcpy( gPsSubjectName, "Unknown" );	for( i = 1; i < argc; i++ ) {		if( strcmp( argv[ i ], "-sw" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-sw (No number).\n" );				return false;			} else {				gPsScreenWidth = atof( argv[ ++i ] );				if( gPsScreenWidth <= 0.0 ) {					printf( "-sw (Illegal number %f).\n", gPsScreenWidth );					return false;				}				PsSetPrefValue( kPsScrnW_Rsrc, gPsScreenWidth );			}		} else if( strcmp( argv[ i ], "-sh" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-sh (No number).\n" );				return false;			} else {				gPsScreenHeight = atof( argv[ ++i ] );				if( gPsScreenHeight <= 0.0 ) {					printf( "-sh (Illegal number %f).\n", gPsScreenHeight );					return false;				}				PsSetPrefValue( kPsScrnH_Rsrc, gPsScreenHeight );			}		} else if( strcmp( argv[ i ], "-sd" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-sd (No number).\n" );				return false;			} else {				gPsScreenDistance = atof( argv[ ++i ] );				if( gPsScreenDistance <= 0.0 ) {					printf( "-sd (Illegal number %f).\n", gPsScreenDistance );					return false;				}				PsSetPrefValue( kPsScrnD_Rsrc, gPsScreenDistance );			}		} else if( strcmp( argv[ i ], "-es" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-es (No number).\n" );				return false;			} else {				gPsEyeSeparation = atof( argv[ ++i ] );				if( gPsEyeSeparation <= 0.0 ) {					printf( "-es (Illegal number %f).\n", gPsEyeSeparation );					return false;				}				PsSetPrefValue( kPsEyeSep_Rsrc, gPsEyeSeparation );			}		} else if( strcmp( argv[ i ], "-ft" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-ft (No number).\n" );				return false;			} else {				gPsFlashTime = atof( argv[ ++i ] );				if( gPsFlashTime <= 0.0 ) {					printf( "-ft (Illegal number %f).\n", gPsFlashTime );					return false;				}				PsSetPrefValue( kPsFTime_Rsrc, gPsFlashTime );			}		} else if( strcmp( argv[ i ], "-f" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-f (No file name).\n" );				return false;			} else {				strcpy( gPsInputFileName, argv[ ++i ] );			}		} else if( strcmp( argv[ i ], "-s" ) == 0 ) {			if( i + 1 >= argc || argv[ i + 1 ][ 0 ] == '-' ) {				printf( "-s (No subject name).\n" );				return false;			} else {				strcpy( gPsSubjectName, argv[ ++i ] );			}		} else if( strcmp( argv[ i ], "-r" ) == 0 ) {			gPsRandomize = false;		} else {			PsReportError( "%s (Bad option).\n", argv[ i ] );			PsReportError( "-f <name> - input file name" );			PsReportError( "-s <name> - subject name" );			PsReportError( "-sw <#>   - screen width (default %.2f mm)", gPsScreenWidth );			PsReportError( "-sh <#>   - screen height (default %.2f mm)", gPsScreenHeight );			PsReportError( "-sd <#>   - distance to the screen (default %.2f mm)", gPsScreenDistance );			PsReportError( "-es <#>   - eye separation (default %.2f mm)", gPsEyeSeparation );			PsReportError( "-ft <#>   - flash time (default %.3f sec)", gPsFlashTime );			return false;		}	}	PsAddExtension( gPsInputFileName, ".dst" );	strcpy( gPsOutputFileName, gPsSubjectName );	PsAddExtension( gPsOutputFileName, ".dat" );	return true;}/* * Array of structs * containing pointers to open and the corresponding close functions */struct {	Boolean	(* open)( void );	void	(* close)( void );} oc_fs[ ] = {	PsStartOpenGL,		PsCloseOpenGL,	PsReadDataSetFile,	NULL,	PsOpenOutputFile,	PsCloseOutputFile,	PsSetupExperiment,	NULL,	NULL,				NULL };/* * Initialize application and its environment * *		returns number of succesfull init operations, *		positive if all went well *		negative otherwise */int PsInit( int argc, char **argv ){	int		init_level;	Boolean (*f)( void );	if( PsParseArgs( argc, argv ) == false ) {		return -1;	}	init_level = 2;						// Set init level	while( (f = oc_fs[ init_level - 2 ].open) != NULL ) {		if( (*f)( ) == false ) {		// Execute function, if bad			init_level = -init_level;	// Return negative init level			break;		}		init_level++;					// Next init level	};	return init_level;}/* * Cleanup depending on the level of initialization * *		returns 0 on a positive init level (all went well) *		returns the absolute init level otherwise */int PsExit( int init_level ){	int		abs_il, i;	void	(*f)( void );	abs_il = abs( init_level ); 		// Make absulute	i = abs_il - 2;	while( i >= 0 ) {					// Until < zero		if( (f = oc_fs[ i-- ].close) != NULL ) {			(*f)( );					// Execute function		}	};	if( init_level < 0 ) {				// If the init level was bad		return abs_il;	} else {		return 0;	}}/* * Open GL Physics of Man - Measurements */#define PsRandom(from,to) (from + ( rand() * (to - from) / RAND_MAX))double	gPsAlarmTime;void	(*gPsAlarmFunc)( void ) = NULL;int		gPsWindowH;Point	gPsCurrLocator;static void PsNext( void );/* * */static double PsTimeNow( void ){	UnsignedWide theTime;	Microseconds( &theTime );	return 4294.967296 * theTime.hi + 0.000001 * theTime.lo;}/* * */static void PsSetAlarm( double time, void (*func)( void ) ){	gPsAlarmFunc = func;	gPsAlarmTime = PsTimeNow( ) + time;}/* * */static void PsSet2Default( void ){	gPsTheSlider.w.h = 0;	gPsTheSlider.w.v = PsRandom( -50, 50 );	gPsTheSlider.fx = 0.0;	gPsTheSlider.fy = (float)gPsTheSlider.w.v;}/* * */static void PsEndTrial( void ){	PsSet2Default( );	gPsDrawMode = kPsShowObserver;	glutPostRedisplay( );	PsSetAlarm( 1800.0, PsNext );}/* * */static void PsFlash( void ){	gPsDrawMode = kPsShowStimulus;	PsDraw( );	PsSetAlarm( gPsFlashTime, PsEndTrial );}/* * */static void PsSetupTrial( void ){	gPsDrawMode = kPsShowNonius;	PsSetAlarm( 1800.0, PsFlash );	glutPostRedisplay( );}/* * */Boolean PsSetupExperiment( void ){	int i, h, r, *pa;	for( i = 0, pa = gPsSequenceArr; i < gPsNrOfTrials; ) {		*pa++ = i++;	}	if( gPsRandomize ) {		for( i = 0, pa = gPsSequenceArr; i < gPsNrOfTrials; pa++, i++ ) {			r = (int)( ( (long)gPsNrOfTrials * rand( )) / RAND_MAX );			h = *pa;			*pa = gPsSequenceArr[ r ];			gPsSequenceArr[ r ] = h;		}	}	gPsSequenceIndex = 0;	gPsDrawMode = kPsShowStart;	glutPositionWindow( 0, 0 );	glutFullScreen( );	gPsWindowH = glutGet( GLUT_WINDOW_HEIGHT );	glutPostRedisplay( );	PsSetAlarm( 1800.0, PsSetupTrial );	return true;}/* * */static void PsNext( void ){	sPsTrialPtr tp;	tp = &gPsTrialRecs[ gPsSequenceArr[ gPsSequenceIndex ] ];	tp->answer = -gPsTheSlider.w.v;	PsSaver( tp );	gPsSequenceIndex++;	if( gPsSequenceIndex >= gPsNrOfTrials ) {		PsExit( gPsInitLevel );		exit( 0 );	} else {		PsSetupTrial( );	}}/* * */void PsKey( unsigned char key, int x, int y ){	#pragma unused (x, y)	switch( key ) {	case 0x1B:		exit( 0 );		break;	case ' ':		if( *gPsAlarmFunc ) {			(**gPsAlarmFunc)( );		}		break;	case '*':		PsSet2Default( );		glutPostRedisplay( );		break;	default:		break;	}}/* * */void PsMouse( int button, int state, int x, int y ){	#pragma unused ( button,  state,  x,  y )	if( state == GLUT_UP ) {		if( *gPsAlarmFunc ) {			(**gPsAlarmFunc)( );		}	}}/* * */void PsMotion( int x, int y ){	gPsCurrLocator.h = x;	gPsCurrLocator.v = ((gPsWindowH / 2 ) - y) * gPsScreenHeight / gPsWindowH;}/* * */void PsIdle( void ){	static Point p = { -1, -1 };	if( gPsDrawMode == kPsShowObserver ) {		if( gPsCurrLocator.h != p.h || gPsCurrLocator.v != p.v ) {			gPsTheSlider.w	= p = gPsCurrLocator;			gPsTheSlider.fx	= (float)gPsCurrLocator.h;			gPsTheSlider.fy	= (float)gPsCurrLocator.v;			glutPostRedisplay( );		}	}	if( gPsAlarmTime && (PsTimeNow( ) > gPsAlarmTime) ) {		gPsAlarmTime = 0.0;		if( *gPsAlarmFunc ) {			(**gPsAlarmFunc)( );		}	}}/* * Main program */int main( int argc, char **argv ){	#if defined(__MWERKS__) || defined(__SC__)		argc = ccommand( &argv );	#endif	gPsInitLevel = PsInit( argc, argv );	// Initialize	if( gPsInitLevel >= 0 ) {				// How successful was init ?		glutMainLoop();	}	PsExit( gPsInitLevel );					// Terminate appropriate inits	return 0;}