/*
 *  ltopengl.c
 *  lt
 *
 *  Created by Shane Saxon on 4/4/05.
 *
 *  Shane Saxon has waived all copyright and related or neighboring rights to
 *  "miniTweezers GUI". Published from the United States as Public Domain.
 *  Creative Commons License CC0 1.0 Universal.
 *
 *  Created by Shane Saxon with contributions by Steven Smith, Evan Sperber,
 *  and Josep-Maria Huguet.  Code base started in 2004.
 *
 *  To view the full license with a list of additional authors and to download or
 *  contribute code goto http://tweezerslab.unipr.it/license.html
 */

#include <string.h>

#include "glCheck.h"

#include "ltopengl.h"

#define kUseMultiSample 0 // define this to 1 to enable multi-sample
#define kPI 3.1416f

// ---------------------------------
#ifndef DTOR
	#define DTOR 0.0174532925
#endif
// ---------------------------------

char gErrorMessage[256] = ""; // buffer for error message output
float gErrorTime = 0.0;

#pragma mark ---- OpenGL Capabilities ----

// GL configuration info globals
// see glcheck.h for more info
GLCaps * gDisplayCaps = NULL; // array of GLCaps
CGDisplayCount gNumDisplays = 0;

#include "drawInfo.h"			//source info for info output

void getCurrentCaps (void)
{
 	// Check for existing opengl caps here
	// This can be called again with same display caps array when display configurations are changed and
	//   your info needs to be updated.  Note, if you are doing dynmaic allocation, the number of displays
	//   may change and thus you should always reallocate your display caps array.
	if (gDisplayCaps && HaveOpenGLCapsChanged (gDisplayCaps, gNumDisplays)) { // see if caps have changed
		free (gDisplayCaps);
		gDisplayCaps = NULL;
		
	}
	if (!gDisplayCaps) { // if we do not have caps
		CheckOpenGLCaps (0, NULL, &gNumDisplays); // will just update number of displays
		gDisplayCaps = (GLCaps*) malloc (sizeof (GLCaps) * gNumDisplays);
		CheckOpenGLCaps (gNumDisplays, gDisplayCaps, &gNumDisplays);
	}
}

#pragma mark ---- Utilities ----

// return float elpased time in seconds since app start
static float getElapsedTime (void)
{	
	static int getStartTime = 1;
	static AbsoluteTime startTime;
	
	if (getStartTime)
	{
		startTime = UpTime (); // get start time
		getStartTime = 0;
	}
	
	float deltaTime = (float) AbsoluteDeltaToDuration (UpTime(), startTime);
    if (0 > deltaTime)	// if negative microseconds
		deltaTime /= -1000000.0;
    else				// else milliseconds
		deltaTime /= 1000.0;
	return deltaTime;
}

#pragma mark ---- Error Reporting ----

// C string to Pascal string
static void cstr2pstr (StringPtr outString, const char *inString)
{	
	unsigned short x = 0;
	do {
	    outString [x + 1] = (unsigned char) inString [x];
		x++;
	} while ((inString [x] != 0)  && (x < 256));
	outString [0] = x;									
}

// ---------------------------------

// error reporting as both window message and debugger string
void reportError (char * strError)
{
	Str255 strErr = "\p";

	gErrorTime = getElapsedTime ();
	sprintf (gErrorMessage, "Error: %s (at time: %0.1f secs)", strError, gErrorTime);
	 
	// out as debug string
	cstr2pstr (strErr, gErrorMessage);
	DebugStr (strErr);
	fflush (stderr);
}

// ---------------------------------

// if error dump agl errors to debugger string, return error
OSStatus aglReportError (void)
{
	GLenum err = aglGetError();
	if (AGL_NO_ERROR != err) {
		char errStr[256];
		sprintf (errStr, "AGL: %s",(char *) aglErrorString(err));
		reportError (errStr);
	}
	// ensure we are returning an OSStatus noErr if no error condition
	if (err == AGL_NO_ERROR)
		return noErr;
	else
		return (OSStatus) err;
}

// ---------------------------------

// if error dump gl errors to debugger string, return error
OSStatus glReportError (void)
{
	GLenum err = glGetError();
	if (GL_NO_ERROR != err) {
		char errStr[256];
		sprintf (errStr, "GL: %s",(char *) gluErrorString(err));
		reportError (errStr);
	}
	// ensure we are returning an OSStatus noErr if no error condition
	if (err == GL_NO_ERROR)
		return noErr;
	else
		return (OSStatus) err;
}


#pragma mark ---- OpenGL Utilities ----


// C string drawing function
void drawCStringGL (char * cstrOut, GLuint fontList)
{
	GLint i = 0;
	while (cstrOut [i])
		glCallList (fontList + cstrOut[i++]);
}

// ---------------------------------

// AGL bitmpp font setup
GLuint buildFontGL (AGLContext ctx, GLint fontID, Style face, GLint size)
{
	GLuint listBase = glGenLists (256);
	if (aglUseFont (ctx, fontID , face, size, 0, 256, (long) listBase)) {
		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
		glReportError ();
		return listBase;
	} else {
		reportError ("aglUseFont failed\n" );
		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
		glDeleteLists (listBase, 256);
		return 0;
	}
}

// ---------------------------------

// delete font list passed in
void deleteFontGL (GLuint fontList)
{
	if (fontList)
		glDeleteLists (fontList, 256);
}


// draw text info
// note: this bitmap technique is not the speediest and one should use textures for fonts in more proformance critical code
static void drawInfo (pRecContext pContextInfo)
{	
	static float msgPresistance = 10.0f;
	char cstr [256];
	GLint matrixMode, line = 1;
	GLboolean depthTest = glIsEnabled (GL_DEPTH_TEST);
	GLfloat height, width;
	
	if (!pContextInfo)
		return;
		
	height = pContextInfo->camera.viewHeight;
	width = pContextInfo->camera.viewWidth;

	glDisable (GL_DEPTH_TEST); // ensure text is not remove by depth buffer test.
	glEnable (GL_BLEND); // for text fading
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ditto
	
	// set orthograhic 1:1  pixel transform in local view coords
	glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
	glMatrixMode (GL_PROJECTION);
	glPushMatrix();
		glLoadIdentity ();
		glMatrixMode (GL_MODELVIEW);
		glPushMatrix();
			glLoadIdentity ();
			if (width && height)
				glScalef (2.0 / width, -2.0 /  height, 1.0);
			glTranslatef (-width / 2.0, -height / 2.0, 0.0);
			// output strings
			glColor3f (1.0, 1.0, 1.0);
			sprintf (cstr, "Camera at (%0.1f, %0.1f, %0.1f) looking at (%0.1f, %0.1f, %0.1f) with %0.1f aperture", 
					pContextInfo->camera.viewPos.x, pContextInfo->camera.viewPos.y, pContextInfo->camera.viewPos.z,
					pContextInfo->camera.viewDir.x, pContextInfo->camera.viewDir.y, pContextInfo->camera.viewDir.z,
					pContextInfo->camera.aperture);
			glRasterPos3d (10, line++ * 12, 0); 
			drawCStringGL (cstr, pContextInfo->regFontList);

#if kUseMultiSample
			switch (pContextInfo->modeFSAA) {
				case kFSAAOff:
					sprintf (cstr, "-- %d x FSAA: Disabled", kSamples);
					break;
				case kFSAAFast:
					sprintf (cstr, "-- %d x FSAA: Fastest hint", kSamples);
					break;
				case kFSAANice:
					sprintf (cstr, "-- %d x FSAA: Nicest hint", kSamples);
					break;
			}
#else
			sprintf (cstr, "-- FSAA: Off");
#endif
			drawCStringGL (cstr, pContextInfo->regFontList);

			// message string
			if (pContextInfo->message[0]) {
				float currDelta = getElapsedTime () - pContextInfo->msgTime;
				glColor4f (1.0, 1.0, 1.0, (msgPresistance - currDelta) * 0.1);
				glRasterPos3d (10, line++ * 12, 0); 
				drawCStringGL (pContextInfo->message, pContextInfo->boldFontList);
				if (currDelta > msgPresistance)
					pContextInfo->message[0] = 0;
			}
			// global error message
			if (gErrorMessage[0]) {
				float currDelta = getElapsedTime () - gErrorTime;
				glColor4f (1.0, 0.2, 0.2, (msgPresistance - currDelta) * 0.1);
				glRasterPos3d (10, line++ * 12, 0); 
				drawCStringGL (gErrorMessage, pContextInfo->boldFontList);
				if (currDelta > msgPresistance)
					gErrorMessage[0] = 0;
			}

			glColor3f (1.0, 1.0, 1.0);
			glRasterPos3d (10, height - 27, 0); 
			sprintf (cstr, "(%0.0f x %0.0f)", width, height);
			drawCStringGL (cstr, pContextInfo->boldFontList);
			// render and vendor info
			glRasterPos3d (10, height - 15, 0); 
			drawCStringGL ((char*) glGetString (GL_RENDERER), pContextInfo->boldFontList);
			glRasterPos3d (10, height - 3, 0); 
			drawCStringGL ((char*) glGetString (GL_VERSION), pContextInfo->regFontList);

		drawCaps (pContextInfo);
		
		glPopMatrix();
	glPopMatrix();
	glMatrixMode (matrixMode);
	
	if (depthTest)
		glEnable (GL_DEPTH_TEST);
	glReportError ();
}


// update the projection matrix based on camera and view info
// should be called when viewport size, eye z position, or camera aperture changes
// also call if far or near changes which is determined by shape size in this case
// current context should be set before calling
void updateProjection (GLdouble width, GLdouble height, GLdouble viewPosZ, GLdouble aperture)
{
	GLdouble xmin, xmax, ymin, ymax, aspect;
	// far frustum plane
	GLdouble zFar = 70000.0;				//this should be calculated from geometry, debug
	// near frustum plane (clamped at 1.0)
	GLdouble zNear = 1.0;
	// view aspect ratio
	if (height)
		aspect = width / height; 

	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();

	if (aspect > 1.0) {
		ymax = zNear * tan (aperture * 0.5 * DTOR);
		ymin = -ymax;
		xmin = ymin * aspect;
		xmax = ymax * aspect;
	} else {
		xmax = zNear * tan (aperture * 0.5 * DTOR);
		xmin = -xmax;
		if (aspect)
		{
			ymin = xmin / aspect;
			ymax = xmax / aspect;
		}
	}
	glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}

// ---------------------------------

// updates the model view matrix for camera moves
// we will call this every draw loop for simplicity
// current context should be set before calling
void updateModelView (recCamera *pCamera)
{
	// move view
	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();
	gluLookAt (	pCamera->viewPos.x, 
					pCamera->viewPos.y, 
					pCamera->viewPos.z,
					pCamera->viewDir.x,
					pCamera->viewDir.y,
					pCamera->viewDir.z,
					pCamera->viewUp.x, 
					pCamera->viewUp.y,
					pCamera->viewUp.z );		
}


// ---------------------------------
// handles resizing of GL need context update and if the window dimensions change, a
// a window dimension update, reseting of viewport and an update of the projection matrix
// this is a windowing system level routine so it handles setting context, etc.
// returns error if resize fails

OSStatus resizeGL (AGLContext aglContext, recCamera * pCamera, CGRect viewRect, pRecContext pContextInfo)
{
	OSStatus err = noErr;
	char cstr[256];

    if (!aglContext)
        return paramErr;

	// re-attach drawable to ensure context is updated
	if (!aglSetCurrentContext (aglContext))
		err = aglReportError ();
	if ((noErr == err) && !aglUpdateContext (aglContext))
		err = aglReportError ();
	
// Must go directly to the stored value to proper check for differences with window size
// Note, it is likely a toss up as to whether blinding setting the viewport is faster than
// testing for changes
	if (noErr == err) {
		GLint aViewportDims[4];
		glGetIntegerv (GL_VIEWPORT, aViewportDims);
		if ((viewRect.size.width != aViewportDims[2]) ||
			(viewRect.size.height != aViewportDims[3])) {
			pCamera->viewOriginX = viewRect.origin.x;
			pCamera->viewOriginY = viewRect.origin.y;
			pCamera->viewWidth = viewRect.size.width;
			pCamera->viewHeight = viewRect.size.height;
			glViewport (0, 0, pCamera->viewWidth, pCamera->viewHeight);
			
			sprintf (cstr, "(%0.0f x %0.0f)", viewRect.size.width, viewRect.size.height);
			PostMessage (cstr, pContextInfo);
			
			pContextInfo->clearFrameBufferOnce = TRUE;
		}
	}
    return err;
}

// ---------------------------------

// sets the camera data to initial conditions
static void resetCamera (recCamera * pCamera)
{
   pCamera->aperture = 44.2;			//equivalent to 30mm lens
   pCamera->rotPoint.x = 0.0;
	pCamera->rotPoint.y = 0.0;
	pCamera->rotPoint.z = 0.0;

   pCamera->viewPos.x = 0.0;
   pCamera->viewPos.y = 0.0;
   pCamera->viewPos.z = 12.3135;		//1 unit at origin equal to 1 grid space     12.3135
   pCamera->viewDir.x = -pCamera->viewPos.x; 
   pCamera->viewDir.y = -pCamera->viewPos.y; 
   pCamera->viewDir.z = -pCamera->viewPos.z;

   pCamera->viewUp.x = 0.0;  
   pCamera->viewUp.y = 1.0; 
   pCamera->viewUp.z = 0.0;

	// will be set in resize once the target view size and position is known
	pCamera->viewOriginY = 0;
	pCamera->viewOriginX = 0;
	pCamera->viewHeight = 0;
	pCamera->viewWidth = 0;
}

// ---------------------------------

void SetLighting(unsigned int mode)
{
	GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat mat_shininess[] = {90.0};

	GLfloat position[4] = {7.0,-7.0,12.0,0.0};
	GLfloat ambient[4]  = {0.2,0.2,0.2,1.0};
	GLfloat diffuse[4]  = {1.0,1.0,1.0,1.0};
	GLfloat specular[4] = {1.0,1.0,1.0,1.0};
	
	glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	
	glEnable(GL_COLOR_MATERIAL);
	glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);

	switch (mode) {
		case 0:
			break;
		case 1:
			glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE);
			glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE);
			break;
		case 2:
			glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE);
			glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
			break;
		case 3:
			glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
			glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE);
			break;
		case 4:
			glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
			glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
			break;
	}
	
	glLightfv(GL_LIGHT0,GL_POSITION,position);
	glLightfv(GL_LIGHT0,GL_AMBIENT,ambient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,specular);
	glEnable(GL_LIGHT0);
}


// ---------------------------------
OSStatus buildGL (WindowRef window)
{
	OSStatus err = noErr;
	Rect rectPort;
#if kUseMultiSample
	GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_NO_RECOVERY, 
	                   AGL_SAMPLE_BUFFERS_ARB, 1, AGL_SAMPLES_ARB, kSamples, AGL_NONE };
#else
	GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_NONE };
#endif
    pRecContext pContextInfo = GetCurrentContextInfo(window);
	ProcessSerialNumber psn = { 0, kCurrentProcess };
    
    if (NULL == pContextInfo)
        return paramErr;
	if (pContextInfo->aglContext)
		return noErr; // already built
		
	// build context
	pContextInfo->aglContext = NULL;
	pContextInfo->aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib);
	err = aglReportError ();
	if (pContextInfo->aglPixFmt) {
		pContextInfo->aglContext = aglCreateContext(pContextInfo->aglPixFmt, NULL);
		err = aglReportError ();
	}
#if kUseMultiSample
	// if multi-sample pixel format or context fails allocate a non-multi-sample one
	if ((noErr != err) || (NULL == pContextInfo->aglContext)) { // try non-multisample
		attrib[5] = attrib[6] = attrib[7] = attrib[8] = 0;
		if (pContextInfo->aglPixFmt)
			aglDestroyPixelFormat (pContextInfo->aglPixFmt);
		pContextInfo->aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib);
		err = aglReportError ();
		if (pContextInfo->aglPixFmt) {
			pContextInfo->aglContext = aglCreateContext (pContextInfo->aglPixFmt, NULL);
			err = aglReportError ();
		}
	}
#endif
	if (pContextInfo->aglContext) {
		short fNum;
		GLint swap = 1;
		CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}};

        GrafPtr portSave = NULL;
        GetPort (&portSave);
        SetPort ((GrafPtr) GetWindowPort (window));

		if(!aglSetDrawable(pContextInfo->aglContext, GetWindowPort (window)))
			err = aglReportError ();
		if (!aglSetCurrentContext(pContextInfo->aglContext))
			err = aglReportError ();
		pContextInfo->currentVS = aglGetVirtualScreen (pContextInfo->aglContext); // sync renderer

		// VBL SYNC
		if (!aglSetInteger (pContextInfo->aglContext, AGL_SWAP_INTERVAL, &swap))
			aglReportError ();

		// init GL stuff here
#if kUseMultiSample
		switch (pContextInfo->modeFSAA) {
			case kFSAAOff:
				glDisable (GL_MULTISAMPLE_ARB);
				break;
			case kFSAAFast:
				glEnable (GL_MULTISAMPLE_ARB);
				glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
				break;
			case kFSAANice:
				glEnable (GL_MULTISAMPLE_ARB);
				glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
				break;
		}
#endif
		glEnable(GL_DEPTH_TEST);
		glShadeModel(GL_SMOOTH);    
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glFrontFace(GL_CCW);
		glPolygonOffset (1.0, 1.0);
		glClearColor(0.0,0.0,0.0,0.0);

		GetFNum ("\pGeneva", &fNum); // build font
		pContextInfo->bigBoldFontList = buildFontGL (pContextInfo->aglContext, fNum, bold, 24);
		pContextInfo->boldFontList = buildFontGL (pContextInfo->aglContext, fNum, bold, 9);
		pContextInfo->regFontList = buildFontGL (pContextInfo->aglContext, fNum, normal, 9);

		// setup viewport and prespective
		GetWindowPortBounds (window, &rectPort);
		viewRect.size.width = (float) (rectPort.right - rectPort.left);
		viewRect.size.height = (float) (rectPort.bottom - rectPort.top);
		pContextInfo->camera.viewOriginX = viewRect.origin.x;
		pContextInfo->camera.viewOriginY = viewRect.origin.y;
		pContextInfo->camera.viewWidth = viewRect.size.width;
		pContextInfo->camera.viewHeight = viewRect.size.height;
		glViewport (0, 0, pContextInfo->camera.viewWidth, pContextInfo->camera.viewHeight);
		
		SetLighting (4);

        SetPort (portSave);
    }
    return err;
}

// ---------------------------------
// dump window data structures and OpenGL context and related data structures
OSStatus disposeGL (pRecContext pContextInfo)
{
	// release our data
    if ( pContextInfo != NULL )
    {
		aglSetDrawable (pContextInfo->aglContext, NULL);
		aglSetCurrentContext (NULL);
		if (pContextInfo->aglContext) {
			aglDestroyContext (pContextInfo->aglContext);
			pContextInfo->aglContext = NULL;
		}
		if (pContextInfo->aglPixFmt) {
			aglDestroyPixelFormat (pContextInfo->aglPixFmt);
			pContextInfo->aglPixFmt = NULL;
		}
		if (pContextInfo->timer) {
			RemoveEventLoopTimer(pContextInfo->timer);
			pContextInfo->timer = NULL;
		}
		// dump font display list
		if (pContextInfo->bigBoldFontList) {
			deleteFontGL (pContextInfo->bigBoldFontList);
			pContextInfo->bigBoldFontList = 0;
		}
		if (pContextInfo->boldFontList) {
			deleteFontGL (pContextInfo->boldFontList);
			pContextInfo->boldFontList = 0;
		}
		if (pContextInfo->regFontList) {
			deleteFontGL (pContextInfo->regFontList);
			pContextInfo->regFontList = 0;
		}
		
    }
    
    return noErr;
}


#pragma mark ---- Initialize ----


// intializes plot conditions for a single plot
void InitPlot (pRecPlot plot)
{
	plot->vertexCount = 3000;								//debug, tarzan
	plot->arrayIndex = 0;
	plot->vertexArray = NULL;
	plot->colorArray = NULL;
	
	plot->sampleInterval = 1;			//use 1 to not skip any samples
	plot->averageType = kAverage;

//	plot->previousRecentIndex = pContextInfo->quicktime.recentIndex				//debug
	
	plot->channel.x = kChannelOscilloscope;
	plot->channel.y = kChannelCycleCount;
	plot->channel.z = kChannelNull;

	plot->color.r = 255;					//yellow, fully opaque
	plot->color.g = 127;
	plot->color.b = 127;
	plot->color.a = 255;
	plot->colorIndex = 0;
	plot->colorFade = FALSE;
	plot->altColorUpdate = FALSE;
	
	plot->screenPosition.x = 0.0;		//center the plot in the middle of screen
	plot->screenPosition.y = 0.0 - (kGridCount / 2);
	plot->screenPosition.z = 0.0;
	
	plot->translate.x = 0.0;				//no translate
	plot->translate.y = 0.0;
	plot->translate.z = 0.0;
	
	plot->translateRate.x = 0.0;			//no change in translate
	plot->translateRate.y = 0.0;
	plot->translateRate.z = 0.0;

	plot->scale.x = kDefaultScale;	//scaler multiple of 1.0 is no scaling
	plot->scale.y = kDefaultScale;
	plot->scale.z = kDefaultScale; // 1.0 / 32768.0;									debug

	plot->scaleRate.x = 0.0;	//scaler multiple of 1.0 is no scaling
	plot->scaleRate.y = 0.0;
	plot->scaleRate.z = 0.0;

	plot->rotate.angle = 0.0;		//no rotation
	plot->rotate.x = 0.0;
	plot->rotate.y = 0.0;
	plot->rotate.z = 1.0;				//vector pointing in Z with 0 rotation

	plot->rotateRate.angle = 0.0;		//no change in rotation
	plot->rotateRate.x = 0.0;
	plot->rotateRate.y = 0.0;
	plot->rotateRate.z = 0.0;

	plot->center = FALSE;
	plot->centerPerGridSpace = FALSE;
	plot->scroll = FALSE;
	plot->hide = FALSE;					//hide the plot
	plot->freeze = FALSE;					//freeze current state of plot
	plot->triggerOn = FALSE;
	plot->triggerOneShot = FALSE;
	plot->triggerLevel = 0.0;
	
	plot->autoZoom.x = FALSE;
	plot->autoZoom.y = FALSE;
	plot->autoZoom.z = FALSE;
	
	plot->lineWidth = 0.0;
	plot->pointSize = 1.0;				//1.0 is one pixel on the screen
	plot->circle = 0.0;					//1/4 grid unit radius
	
	plot->drawSurface = FALSE;
	
	plot->drawVideo = FALSE;
	plot->bias = -0.55;
	plot->gain = 0.35;
	
	plot->drawCurrentValue.x = FALSE;
	plot->drawCurrentValue.y = FALSE;
	plot->drawCurrentValue.z = FALSE;
	
	plot->drawTranslate.x = FALSE;
	plot->drawTranslate.y = TRUE;
	plot->drawTranslate.z = FALSE;
	
	plot->drawScale.x = FALSE;
	plot->drawScale.y = TRUE;
	plot->drawScale.z = FALSE;	
	
	plot->drawRotate.x = FALSE;
	plot->drawRotate.y = TRUE;
	plot->drawRotate.z = FALSE;
	
	
	plot->drawPullLimits = FALSE;
}


//adds new plot if no plot, initializes the plot
pRecPlot NewPlot (pRecContext pContextInfo)
{
	int i = 0;

	//do not copy the grid (plotIndex == 0) or add plots if locked
	//except if we have not created the initial plots
	if ( (!pContextInfo->plotIndex || pContextInfo->lockPlots) &&
			pContextInfo->plotCount > 2)
		return;

	//select first available plotArray slot, exit if reached plot maximum
	while (pContextInfo->plotArray[i] != NULL)
	{
		i++;
		if (i >= kMaximumPlots)
		return;									//insert warning no more plots allowed, debug
	}

	if (pContextInfo->plotArray[pContextInfo->plotIndex] != NULL && i > 1)
	{
		pContextInfo->plotArray[i] = (pRecPlot) NewPtrClear (sizeof (recPlot));
		*pContextInfo->plotArray[i] = *pContextInfo->plotArray[pContextInfo->plotIndex];
	}
	else
	{	
		pContextInfo->plotArray[i] = (pRecPlot) NewPtrClear (sizeof (recPlot));
		InitPlot(pContextInfo->plotArray[i]);		//setup the first plot, plot[0]
		pContextInfo->plotArray[i]->previousRecentIndex = 
			pContextInfo->data->quicktime.recentIndex;				//belong in initPlot, debug
	}
		
	//allocate memory for vertex and color arrays to store plot data
	pContextInfo->plotArray[i]->vertexArray = 
			(GLfloat*) NewPtrClear (sizeof(GLfloat) * 
			(pContextInfo->plotArray[i]->vertexCount) * 3);
		
	pContextInfo->plotArray[i]->colorArray = 
			(GLubyte*) NewPtrClear (sizeof(GLubyte) * 
			(pContextInfo->plotArray[i]->vertexCount) * 4);

	//gives our new plot a color based on plotArray index value
	pContextInfo->plotArray[i]->colorIndex = i;
	SetIndexColor(&pContextInfo->plotArray[i]->color, 
					  &pContextInfo->plotArray[i]->colorIndex);
	
	pContextInfo->plotArray[i]->screenPosition.y += 1.0;
	
	pContextInfo->plotIndex = i;

	pContextInfo->plotCount++;
	
	pContextInfo->selectAll = FALSE;

	return pContextInfo->plotArray[pContextInfo->plotIndex];
}


//deletes current plot, frees memory 
void DeletePlot (pRecContext pContextInfo)
{
	int i;
	pRecPlot plot;
	
	//exit if grid is selected, plots are locked or there is only one plot left
	if (pContextInfo->plotCount < 3 || 
		 pContextInfo->lockPlots ||
		 pContextInfo->plotIndex == 0)
		return;
	
	if (pContextInfo->selectAll)
		pContextInfo->plotIndex = pContextInfo->plotCount - 1;
	
	i = pContextInfo->plotIndex;
	
	plot = pContextInfo->plotArray[i];
	
	DisposePtr(plot->vertexArray);
	DisposePtr(plot->colorArray);
	DisposePtr(plot);
	
	pContextInfo->plotArray[i] = NULL;
	
	pContextInfo->plotCount--;

	if (i == pContextInfo->plotCount)				//exit if last plot
		{
			pContextInfo->plotIndex--;
			return;
		}
		
	//collapse the gap if removing a plot other than the last plot
	while (i < pContextInfo->plotCount)
	{
		pContextInfo->plotArray[i] = pContextInfo->plotArray[i + 1];
		i++;
	}
	
	pContextInfo->plotArray[pContextInfo->plotCount] = NULL;
	
	AutoArrangePlots(pContextInfo);
}


// intializes grid conditions
void InitGrid (pRecContext pContextInfo)
{ 
	pRecPlot plot = NewPlot (pContextInfo);
	
	plot->color.r = 255;
	plot->color.g = 255;
	plot->color.b = 255;
	plot->color.a = 100;		//40 percent opaque, looks grey
	
	plot->scale.x = 1.0;		//no scaling
	plot->scale.y = 1.0;
	plot->scale.z = 1.0;
}


//sets up the specific parameters for different window types such as PSD, Lever
//force vs. position
void InitGraphType (pRecContext pContextInfo)
{
	int i;
	pRecPlot plot = NewPlot (pContextInfo);					//should start with 0 plots, debug
	
	switch (pContextInfo->graphType)
	{
		case kGraphForcePosition:
			plot->channel.x = kChannelTrapANormLeverY;
			plot->channel.y = kChannelNetForceY;
			plot->scale.x = 0.005;
			plot->scale.y = -0.1;
			plot->circle = 0.1;
			plot->sampleInterval = 128;
			plot->altColorUpdate = TRUE;
			plot->drawPullLimits = TRUE;
			pContextInfo->plotArray[0]->pointSize = 3.0;
			pContextInfo->drawIndicators = TRUE;
			pContextInfo->axes.x = TRUE;
			break;
		case kGraphDualForce:
			plot->channel.x = kChannelTrapANormLeverY;
			plot->channel.y = kChannelTrapAPsdY;
			plot->scale.x = 0.005;
			plot->scale.y = -0.001;
			plot->circle = 0.1;
			plot->sampleInterval = 128;
			plot->altColorUpdate = TRUE;
			plot->drawPullLimits = TRUE;
			pContextInfo->plotArray[0]->pointSize = 3.0;
			pContextInfo->drawIndicators = TRUE;
			pContextInfo->axes.x = TRUE;
			
			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelTrapBNormLeverY;
			plot->channel.y = kChannelTrapBPsdY;
			plot->scale.x = 0.005;
			plot->scale.y = -0.001;
			plot->circle = 0.1;
			plot->sampleInterval = 128;
			plot->altColorUpdate = TRUE;
			plot->drawPullLimits = TRUE;
			pContextInfo->plotArray[0]->pointSize = 3.0;
			pContextInfo->drawIndicators = TRUE;
			pContextInfo->axes.x = TRUE;
		//	plot->translate.y = 1000.0;
			
			pContextInfo->selectAll = TRUE;
			break;
			
		case kGraphForceVelocity:			// Stokes' Law test
			plot->channel.x = kChannelMotorVelocityX;
			plot->channel.y = kChannelNetForceX;
			plot->scale.x = 1.0/150.0;
			plot->scale.y = 1.0/3.0;
			plot->circle = 0.1;
			plot->translate.y = 7.5;
			plot->sampleInterval = 16;
			pContextInfo->drawIndicators = TRUE;
			
			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelMotorVelocityY;
			plot->channel.y = kChannelNetForceY;
			plot->circle = 0.1;
			plot->translate.y = 9.0;
			plot->sampleInterval = 16;

			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelMotorVelocityZ;
			plot->channel.y = kChannelNetForceZ;
			plot->circle = 0.1;
			plot->translate.y = 10.5;
			plot->sampleInterval = 16;
			
			pContextInfo->selectAll = TRUE;
			break;
		case kGraphMotorLever:	//SBS				
			plot->channel.x = kChannelMotorX;
			plot->channel.y = kChannelTrapANormLeverX;
			plot->scale.x = 1.0/2.0;
			plot->scale.y = 1.0/2000.0;
			plot->circle = 0.1;
			plot->sampleInterval = 256;
			pContextInfo->drawIndicators = TRUE;
			PlotCommand(kGraphCenter,pContextInfo); 

			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelMotorX;
			plot->channel.y = kChannelTrapBNormLeverX;
			plot->circle = 0.1;
			plot->sampleInterval = 256;

			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelMotorY;
			plot->channel.y = kChannelTrapANormLeverY;
			plot->circle = 0.1;
			plot->sampleInterval = 256;

			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelMotorY;
			plot->channel.y = kChannelTrapBNormLeverY;
			plot->circle = 0.1;
			plot->sampleInterval = 256;
			pContextInfo->selectAll = TRUE;
			GraphSelectAxes(pContextInfo);		//axis select = Y
			GraphSelectAxes(pContextInfo);		//axis select = X
			GraphSelectAxes(pContextInfo);		//axis select = XY for symmetrical zoom
			break;
			
		case kGraphLeverLever:		//SBS			
			plot->channel.x = kChannelTrapANormLeverX;
			plot->channel.y = kChannelTrapANormLeverY;
			plot->scale.x = 1.0/2000.0;
			plot->scale.y = 1.0/2000.0;
			plot->circle = 0.1;
			plot->sampleInterval = 256;
			pContextInfo->drawIndicators = TRUE;
			plot->translate.y = 12000;
			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelTrapBNormLeverX;
			plot->channel.y = kChannelTrapBNormLeverY;
			plot->circle = 0.1;
			plot->sampleInterval = 256; 
			plot->translate.y = 10000;
			pContextInfo->selectAll = TRUE;
			GraphSelectAxes(pContextInfo);		//axis select = Y
			GraphSelectAxes(pContextInfo);		//axis select = X
			GraphSelectAxes(pContextInfo);		//axis select = XY 
			break;

		case kGraphHopping:			
			PlotCommand(kGraphCenter,pContextInfo); // all cloned plots will have centering ON
			plot->channel.x = kChannelPICtime;
			plot->channel.y = kChannelTrapAPsdY;
			plot->scale.x = 1.0/10.0;
			plot->scale.y = 1.0/40.0;
			plot->circle = 0.1;
			plot->sampleInterval = 128;
			pContextInfo->drawIndicators = TRUE;
			plot = NewPlot (pContextInfo);
			plot->channel.y = kChannelTrapBPsdY;
			plot = NewPlot (pContextInfo);
			plot->channel.y = kChannelTrapANormLeverY;
			plot->scale.y = 1.0/1.0;
			plot = NewPlot (pContextInfo);
			plot->channel.y = kChannelTrapBNormLeverY;
//			plot = NewPlot (pContextInfo);
//			plot->channel.y = kChannelNetForceY;
//			plot->scale.y = 1.0/0.1;
			pContextInfo->selectAll = TRUE;
			break;
			
		case kGraphExtensionTime:		//SBS
			plot->channel.x = kChannelPICtime;
			plot->channel.y = kChannelTrapANormLeverY;
			plot->circle = 0.1;
			plot->sampleInterval = 256;
			plot->scale.x = 1.0/10.0;
			plot->scale.y = 1.0/50.0;
			plot->colorIndex = 2;
			SetIndexColor (&plot->color, &plot->colorIndex);
			pContextInfo->drawIndicators = TRUE;
			PlotCommand(kGraphCenter,pContextInfo); 
			break;
			
		case kGraphForceTime:
			plot->channel.x = kChannelTime;
			plot->channel.y = kChannelNetForceY;
			plot->circle = 0.1;
			plot->scale.x = 1.6 * pContextInfo->gridCount / (GLfloat)plot->vertexCount;
			plot->colorIndex = 4;
			SetIndexColor (&plot->color, &plot->colorIndex);
			pContextInfo->drawIndicators = TRUE;
			break;
		case kGraphOscilloscope:
			plot->channel.x = kChannelOscilloscope;
			plot->channel.y = kChannelTrapAPsdX;
			plot->scale.y = 0.01;
			plot->circle = 0.05;
			plot->center = TRUE;
			plot->centerPerGridSpace = TRUE;
			if (plot->vertexCount)
				plot->scale.x = pContextInfo->gridCount / (GLfloat)plot->vertexCount;
			if (plot->scale.x)
				plot->translate.x = pContextInfo->gridCount / (plot->scale.x * -2.0);
			
			for (i = 0; i < 8; i++)
			{
				plot = NewPlot (pContextInfo);
				plot->channel.y++;
			}
			
			pContextInfo->axes.x = FALSE;
			pContextInfo->selectAll = TRUE;
			pContextInfo->drawIndicators = TRUE;
			break;
		case kGraphFourier:
			break;
		case kGraphNetForce:
			plot->channel.x = kChannelNetForceX;
			plot->channel.y = kChannelNetForceY;
			plot->channel.z = kChannelNetForceZ;
			plot->color.r = 0;
			plot->color.g = 215;
			plot->color.b = 225;
			plot->color.a = 255;
			plot->circle = 0.25;
			plot->screenPosition.y = 0.0;
			
			pContextInfo->plotArray[0]->pointSize = 0.0;
			pContextInfo->plotArray[0]->lineWidth = 1.0;
			
			pContextInfo->lockPlots = TRUE;
			break;
		case kGraphPSD:
			plot->channel.x = kChannelTrapAPsdX;
			plot->channel.y = kChannelTrapAPsdY;
			plot->screenPosition.y = 0.0;
			plot->circle = 0.25;
			plot->autoZoom.x = TRUE;
			plot->autoZoom.y = TRUE;

			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelTrapBPsdX;
			plot->channel.y = kChannelTrapBPsdY;
			plot->colorIndex = 2;
			SetIndexColor (&plot->color, &plot->colorIndex);
			plot->screenPosition.y = 0.0;		
			
			pContextInfo->plotArray[0]->pointSize = 0.0;
			pContextInfo->plotArray[0]->lineWidth = 2.0;
			
			pContextInfo->lockPlots = TRUE;
			pContextInfo->selectAll = TRUE;
			break;
		case kGraphLever:
			plot->channel.x = kChannelTrapALeverX;
			plot->channel.y = kChannelTrapALeverY;
			plot->colorIndex = 3;
			SetIndexColor (&plot->color, &plot->colorIndex);
			plot->screenPosition.y = 0.0;
			plot->circle = 0.25;
			plot->autoZoom.x = TRUE;
			plot->autoZoom.y = TRUE;
			
			plot = NewPlot (pContextInfo);
			plot->channel.x = kChannelTrapBLeverX;
			plot->channel.y = kChannelTrapBLeverY;
			plot->colorIndex = 4;
			SetIndexColor (&plot->color, &plot->colorIndex);
			plot->screenPosition.y = 0.0;

			pContextInfo->plotArray[0]->pointSize = 0.0;
			pContextInfo->plotArray[0]->lineWidth = 2.0;
			
			pContextInfo->lockPlots = TRUE;
			pContextInfo->selectAll = 1;
			break;
		case kGraphMap:
			pContextInfo->plotArray[0]->lineWidth = 2.0;
			
			plot->channel.x = kChannelMotorX;
			plot->channel.y = kChannelMotorY;
			plot->channel.z = kChannelMotorZ;
			plot->averageType = kAverage;
			plot->sampleInterval = 64;
			plot->center = TRUE;
			plot->scale.x = 0.01;
			plot->scale.y = 0.01;
			plot->scale.z = 0.01;
			plot->screenPosition.y = 0.0;
			plot->circle = 0.1;
			plot->pointSize = 2.0;
			plot->colorIndex = 2;
			SetIndexColor (&plot->color, &plot->colorIndex);
			
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = TRUE;
			pContextInfo->enableZ = TRUE;
			
			break;
			
		case kGraphIntensityMap:		//SBS
					
			pContextInfo->camera.viewPos.z = pContextInfo->camera.viewPos.z *100;	//telephoto view. No paralax comparing traces at different heights
			pContextInfo->camera.aperture = pContextInfo->camera.aperture/100;
			pContextInfo->plotArray[0]->lineWidth = 1.0;
			plot->channel.x = kChannelMotorX;
			plot->channel.y = kChannelMotorY;
			plot->channel.z = kChannelTrapAPsdSum; 
			plot->averageType = kRawSample;
			plot->sampleInterval = 1024;
			plot->center = FALSE;
			plot->scale.x = 0.001;
			plot->scale.y = 0.001;
			plot->scale.z = 0.001;
			plot->screenPosition.y = 0.0;	

			plot->circle = 0.1;
			plot->pointSize = 2.0;
			plot->lineWidth = 1.0;
			plot->colorIndex = 2;
			SetIndexColor (&plot->color, &plot->colorIndex);
			
			pContextInfo->axes.x = FALSE;
			pContextInfo->axes.y = FALSE;
			pContextInfo->axes.z = TRUE;
			pContextInfo->enableZ = TRUE;
			
			plot->rotate.x = 1.0;	// rotate about vector pointing in X direction		
			plot->rotate.y = 0.0;
			plot->rotate.z = 0.0;		
			
			plot = NewPlot (pContextInfo);
			plot->channel.z = kChannelTrapBPsdSum;
			plot->screenPosition.y = 0.0;	// places second plot directly over first.  Cancels increment offset of NewPlot
			plot->colorIndex = 3;
			SetIndexColor (&plot->color, &plot->colorIndex);

			pContextInfo->selectAll = TRUE;
				
			break;

	}
}

///////////////////////////////////////// intializes context conditions for the entire window, the grid and all plots
void InitRecContext (int graphType, pRecContext pContextInfo, void* data)
{
	int i;
	pRecPlot plot = NULL;
	
	BlockZero (pContextInfo, sizeof (recContext));		//Mac memory optimization, debug
	
	pContextInfo->data = (pData) data;				//passed in when new GL window

	resetCamera (&pContextInfo->camera);
	
	pContextInfo->timer = NULL;
	
	pContextInfo->graphType = graphType;

	pContextInfo->drawMenu = FALSE;
	pContextInfo->drawIndicators = FALSE;
	pContextInfo->selectAll = FALSE;
	pContextInfo->lockPlots = FALSE;
	
	pContextInfo->clearFrameBuffer = TRUE;
	pContextInfo->clearFrameBufferOnce = TRUE;

	pContextInfo->gridCount = 10;
	pContextInfo->gridSpace = 10.0;		  //not actual value, updated by DrawGrid

	pContextInfo->enableZ = FALSE;

	pContextInfo->axes.x = TRUE;
	pContextInfo->axes.y = TRUE;
	pContextInfo->axes.z = FALSE;

	pContextInfo->plotIndex = 0;
	pContextInfo->plotCount = 0;	
	
	for (i = 0; i < kMaximumPlots; i++)
		pContextInfo->plotArray[i] = NULL;

	//init the grid overlay that displays the scale, translate, rotate, current value, etc
	InitGrid (pContextInfo);
	
	InitGraphType(pContextInfo);
}


#pragma mark ---- Draw Utilities ----
///////////////////////////////////////////////////////////////////////////////

void PostMessage (char message[256], pRecContext pContextInfo)
{
	sprintf (pContextInfo->message, message);
	pContextInfo->msgTime = getElapsedTime ();
}


/////////////////////////////updates a single plot with new data if not frozen and draws it if not hidden
void SetIndexColor (GLubyteRGBA *color, int *colorIndex)
{	
	int index = *colorIndex;
	
	GLubyte colorPallet[kColorIndexSize][4] = {	
											{255, 255, 255, 255},
											{0, 255, 0, 255},
											{255, 230, 0, 255},
											{0, 50, 255, 255},
											{255, 0, 0, 255},
											{152, 0, 255, 255},
											{255, 168, 0, 255},
											{0, 255, 255, 255},
											{255, 0, 255, 255},
											{0, 153, 0, 255},
											{173, 255, 202, 255},
											{255, 180, 255, 255},
											{0, 152, 255, 255},
											{185, 255, 0, 255},
											{152, 0, 0, 255},
											{50, 101, 101, 255},
											{127, 127, 255, 255},
											{185, 153, 102, 255},
											{197, 82, 0, 255},
											{127, 127, 127, 255}  };
											
	//make sure colorIndex is inbounds, if not, then put it inbounds																			
	if (index >= kColorIndexSize)
		while (index >= kColorIndexSize)
			index -= kColorIndexSize;		//subtracts pallete sized increments
	
	if (index < 0)
		while (index < 0)
			index += kColorIndexSize;		//adds pallete sized increments

	//now set the color
	color->r = colorPallet[index][0];
	color->g = colorPallet[index][1];
	color->b = colorPallet[index][2];
	color->a = colorPallet[index][3];		//perhaps better not to change alpha, debug

	*colorIndex = index;
}


///////////////////////////////////////////////calculates plot fading
void UpdatePlotColor (pRecContext pContextInfo, pRecPlot plot)
{
	static GLubyte count = 0;

	int  i, localIndex;
	GLfloat j, offset;

	//fade vertices preceding the arrayIndex;
	if (plot->vertexCount && plot->colorFade)
	{
		offset = plot->vertexCount - plot->arrayIndex;
		j = (GLfloat)plot->color.a / plot->vertexCount;		//use plot alpha to allow user control

		for (i = 0; i < plot->arrayIndex; i++)
			plot->colorArray[i * 4 + 3] = (offset + i) * j;

		for (i = plot->arrayIndex; i < plot->vertexCount; i++)
			plot->colorArray[i * 4 + 3] = (i - plot->arrayIndex) * j;
	}
	
	//to prevent retrace line draw endpoints transparent
	if (plot->arrayIndex >= plot->vertexCount - 1)	//end of the array
	{
		//put a transparent vertex at both ends
		plot->vertexArray[0] = plot->vertexArray[plot->arrayIndex * 3 + 0];
		plot->vertexArray[1] = plot->vertexArray[plot->arrayIndex * 3 + 1];
		plot->vertexArray[2] = plot->vertexArray[plot->arrayIndex * 3 + 2];
		plot->colorArray[3] = 0;
		plot->colorArray[4+3] = 0;
	}
	else 
	{
		plot->vertexArray[(plot->arrayIndex + 1) * 3 + 0] = 
				plot->vertexArray[plot->arrayIndex * 3 + 0];
		plot->vertexArray[(plot->arrayIndex + 1) * 3 + 1] = 
				plot->vertexArray[plot->arrayIndex * 3 + 1];
		plot->vertexArray[(plot->arrayIndex + 1) * 3 + 2] = 
				plot->vertexArray[plot->arrayIndex * 3 + 2];
			 
		plot->colorArray[(plot->arrayIndex + 1) * 4 + 3] = 0;
		
		if (plot->arrayIndex == plot->vertexCount - 2)	//second from the end of the array
			plot->colorArray[3] = 0;
		else
			plot->colorArray[(plot->arrayIndex + 2) * 4 + 3] = 0;
	}
	
	count++;
}


///////////////////////////////////re-orders the plots, called by newPlot and deletePlot
void AutoArrangePlots (pRecContext pContextInfo)
{
//																										debug
}


////////////////////////////////calculates translate, scale and screen position
void UpdatePlotPosition (pRecContext pContextInfo, pRecPlot plot)
{
	const GLfloat scaleLimitMax = 10.0;
	const GLfloat scaleLimitMin = 1.0 / 49995.0;					//rounding error, debug
	int i;
	GLfloat deltaX, deltaY, currentX, currentY;
	GLfloat width = pContextInfo->camera.viewWidth;
	GLfloat height = pContextInfo->camera.viewHeight;

	//rotation
	plot->rotate.angle += plot->rotateRate.angle;		//what coordinate type, debug
	plot->rotate.x += plot->rotateRate.x;
	plot->rotate.y += plot->rotateRate.y;
	plot->rotate.z += plot->rotateRate.z;


	//scale, zoom out if shift pressed, otherwise zoom in
	if (pContextInfo->data->control.shiftModifier)
	{
		if (plot->scaleRate.x)
			plot->scale.x *= 1.0 / (1.0 + 0.05 * plot->scaleRate.x);
		if (plot->scaleRate.y)
			plot->scale.y *= 1.0 / (1.0 + 0.05 * plot->scaleRate.y);
		if (plot->scaleRate.z)
			plot->scale.z *= 1.0 / (1.0 + 0.05 * plot->scaleRate.z);
	}
	else
	{
		plot->scale.x *= 1.0 + 0.05 * plot->scaleRate.x;
		plot->scale.y *= 1.0 + 0.05 * plot->scaleRate.y;
		plot->scale.z *= 1.0 + 0.05 * plot->scaleRate.z;
	}

	//scaling limits, catches out of bounds scale, also avoids divide by zero
	if (plot->scale.x >= 0.0)
	{
		if (plot->scale.x > scaleLimitMax)
			plot->scale.x = scaleLimitMax;
		else if (plot->scale.x < scaleLimitMin)
			plot->scale.x = scaleLimitMin;
	}
	else
	{
		if (plot->scale.x < -scaleLimitMax)
			plot->scale.x = -scaleLimitMax;
		else if (plot->scale.x > -scaleLimitMin)
			plot->scale.x = -scaleLimitMin;
	}
	
	if (plot->scale.y >= 0.0)
	{
		if (plot->scale.y > scaleLimitMax)
			plot->scale.y = scaleLimitMax;
		else if (plot->scale.y < scaleLimitMin)
			plot->scale.y = scaleLimitMin;
	}
	else
	{
		if (plot->scale.y < -scaleLimitMax)
			plot->scale.y = -scaleLimitMax;
		else if (plot->scale.y > -scaleLimitMin)
			plot->scale.y = -scaleLimitMin;
	}
	
	if (plot->scale.z >= 0.0)
	{
		if (plot->scale.z > scaleLimitMax)
			plot->scale.z = scaleLimitMax;
		else if (plot->scale.z < scaleLimitMin)
			plot->scale.z = scaleLimitMin;
	}
	else
	{
		if (plot->scale.z < -scaleLimitMax)
			plot->scale.z = -scaleLimitMax;
		else if (plot->scale.z > -scaleLimitMin)
			plot->scale.z = -scaleLimitMin;
	}


	//update the translation, previous check assures no divide by zero
	plot->translate.x += 0.05 * plot->translateRate.x / plot->scale.x;
	plot->translate.y += 0.05 * plot->translateRate.y / plot->scale.y;
	plot->translate.z += 0.05 * plot->translateRate.z / plot->scale.z;
	
	
	//calculate the screen position in XY plane with Offset;
	if (plot->center)
		{
			if (plot->channel.x == kChannelOscilloscope)
				plot->translate.x = 0.0 - plot->vertexCount * 0.5;
			else if (plot->channel.x >= 0)
				plot->translate.x = 0.0 - plot->vertexArray[plot->arrayIndex * 3 + 0];
			
			if (plot->centerPerGridSpace)
			{
				deltaY = (plot->translate.y + 
							plot->vertexArray[plot->arrayIndex * 3 + 1]) *
							plot->scale.y;
			
				if (deltaY < -1.0 || deltaY > 1.0)
					plot->translate.y = 0.0 - 
						plot->vertexArray[plot->arrayIndex * 3 + 1];
			}
			else 
			plot->translate.y = 0.0 - plot->vertexArray[plot->arrayIndex * 3 + 1];
				
			plot->translate.z = 0.0 - plot->vertexArray[plot->arrayIndex * 3 + 2];
		}
		
	//scroll data from right to left, overides the center command on the x-axis
	if (plot->scroll && !plot->freeze)
	{
		if (pContextInfo->gridSpace && plot->scale.x)
			plot->translate.x = ( pContextInfo->camera.viewWidth
				/ (pContextInfo->gridSpace * plot->scale.x * 2.0) )
				- plot->vertexArray[plot->arrayIndex * 3 + 0];
	}

	//autoZoom
	if (plot->autoZoom.x)
	{
		if (pContextInfo->gridSpace)
			deltaX = -width * 0.5 / pContextInfo->gridSpace;
		
		currentX = (plot->vertexArray[plot->arrayIndex * 3 + 0] + 
						plot->translate.x) * plot->scale.x;

		if (currentX > 0)
			currentX = -currentX;

		if ( deltaX > currentX)
			if (pContextInfo->lockPlots)		//if true, apply to all plots and axes
			{
				for ( i = 1; i < pContextInfo->plotCount; i++)
				{
					pContextInfo->plotArray[i]->scale.x *= 0.5;
					pContextInfo->plotArray[i]->scale.y *= 0.5;
				}
			}
			else
				plot->scale.x *= 0.5;
	}

	if (plot->autoZoom.y)
	{
		if (pContextInfo->gridSpace)
			deltaY = -height * 0.5 / pContextInfo->gridSpace;
		
		currentY = (plot->vertexArray[plot->arrayIndex * 3 + 1] + 
						plot->translate.y) * plot->scale.y + plot->screenPosition.y;

		if (currentY > 0)
			currentY = -currentY;

		if ( deltaY > currentY)
			if (pContextInfo->lockPlots)		//if true, apply to all plots and axes
			{
				for ( i = 1; i < pContextInfo->plotCount; i++)
				{
					pContextInfo->plotArray[i]->scale.x *= 0.5;
					pContextInfo->plotArray[i]->scale.y *= 0.5;
				}
			}
			else
				plot->scale.y *= 0.5;
	}
}


//updates the gridSpace value by using the window size
void UpdateGridSpace (pRecContext pContextInfo)
{
	if (!pContextInfo)
		return;
	
	if (kGridCount)
	{
		if (pContextInfo->camera.viewWidth > pContextInfo->camera.viewHeight)
			pContextInfo->gridSpace = (GLfloat)pContextInfo->camera.viewHeight / (GLfloat)kGridCount;
		else
			pContextInfo->gridSpace = (GLfloat)pContextInfo->camera.viewWidth / (GLfloat)kGridCount;
	}
}


//updates a single plot with new data if not frozen and draws it if not hidden
static void GetRecentPlotData (pRecContext pContextInfo, pRecPlot plot)
{	
	int i, j, localRecentIndex, rmsLocalRecentIndex, localDeltaTime;
	GLfloatXYZ vertex, sum, mean, difference;

	
	localRecentIndex = pContextInfo->data->quicktime.recentIndex;
	
	// updates our time, deals with roll over of the quicktime recent buffer 
	if (localRecentIndex >= plot->previousRecentIndex)
		localDeltaTime = localRecentIndex - plot->previousRecentIndex;
	else
		localDeltaTime = localRecentIndex + kRecentSize - plot->previousRecentIndex;
	
	localRecentIndex = plot->previousRecentIndex;
	
	if (plot->altColorUpdate)
	{
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
		if (pContextInfo->data->control.altColor)
		{
			plot->color.r = 255 - plot->color.r;
			plot->color.g = 255 - plot->color.g;
			plot->color.b = 255 - plot->color.b;
		}
	}
	
	for (i = 0; 
		  i + plot->sampleInterval <= localDeltaTime;
		  i += plot->sampleInterval)
	{
		if (plot->sampleInterval > 1)	//skip if raw sample, & prevents divide by 0
		{
			sum.x = 0.0;
			sum.y = 0.0;
			sum.z = 0.0;
			rmsLocalRecentIndex = localRecentIndex;
			
			for (j = 0; j < plot->sampleInterval; j++)
			{
				//loop the source data recent index
				if (localRecentIndex >= kRecentSize)
					localRecentIndex = 0;
			
				sum.x += pContextInfo->data->quicktime.recent 
						[localRecentIndex][plot->channel.x];
							  
				sum.y += pContextInfo->data->quicktime.recent
						[localRecentIndex][plot->channel.y];
							  
				sum.z += pContextInfo->data->quicktime.recent
						[localRecentIndex][plot->channel.z];
			
				localRecentIndex++;
			}

			mean.x = sum.x / (float)plot->sampleInterval;
			mean.y = sum.y / (float)plot->sampleInterval;
			mean.z = sum.z / (float)plot->sampleInterval;
			
			if (plot->averageType == kRootMeanSquare)
			{
				sum.x = 0.0;
				sum.y = 0.0;
				sum.z = 0.0;
				
				for (j = 0; j < plot->sampleInterval; j++)
				{
					//loop the source data recent index
					if (rmsLocalRecentIndex >= kRecentSize)
						rmsLocalRecentIndex = 0;
			
					difference.x = pContextInfo->data->quicktime.recent 
										[rmsLocalRecentIndex][plot->channel.x] - mean.x;
			
					sum.x += difference.x * difference.x;
					
					difference.y = pContextInfo->data->quicktime.recent 
										[rmsLocalRecentIndex][plot->channel.y] - mean.y;
			
					sum.y += difference.y * difference.y;
					
					difference.z = pContextInfo->data->quicktime.recent 
										[rmsLocalRecentIndex][plot->channel.z] - mean.z;
			
					sum.z += difference.z * difference.z;
			
				rmsLocalRecentIndex++;
			}
				vertex.x = sqrtf (sum.x / (float)plot->sampleInterval);
				vertex.y = sqrtf (sum.y / (float)plot->sampleInterval);
				vertex.z = sqrtf (sum.z / (float)plot->sampleInterval);
			}
			else
			{
				vertex.x = mean.x;
				vertex.y = mean.y;
				vertex.z = mean.z;
			}
		}
		else
		{
			vertex.x = pContextInfo->data->quicktime.recent 
						[localRecentIndex][plot->channel.x];
			vertex.y = pContextInfo->data->quicktime.recent 
						[localRecentIndex][plot->channel.y];
			vertex.z = pContextInfo->data->quicktime.recent 
						[localRecentIndex][plot->channel.z];
			
			localRecentIndex++;
			//loop the source data recent index
			if (localRecentIndex >= kRecentSize)
				localRecentIndex = 0;
		}
		
		if (plot->channel.x == kChannelTime)
			plot->scroll = TRUE;
		else
			plot->scroll = FALSE;

		plot->arrayIndex++;
		
		//loop the vertex array data
		if (plot->arrayIndex >= plot->vertexCount)
			plot->arrayIndex = 0;
		
		if (plot->channel.x < 0)
			vertex.x = plot->arrayIndex;	//for oscillscope and scroll with time
				
		plot->vertexArray[plot->arrayIndex * 3 + 0] = vertex.x;
		plot->vertexArray[plot->arrayIndex * 3 + 1] = vertex.y;
		plot->vertexArray[plot->arrayIndex * 3 + 2] = vertex.z;
	
		plot->colorArray[plot->arrayIndex * 4 + 0] = plot->color.r;
		plot->colorArray[plot->arrayIndex * 4 + 1] = plot->color.g;
		plot->colorArray[plot->arrayIndex * 4 + 2] = plot->color.b;
		plot->colorArray[plot->arrayIndex * 4 + 3] = plot->color.a;
	}
	
	if (plot->altColorUpdate)
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
		
		
	plot->previousRecentIndex = localRecentIndex;
}


#pragma mark ---- OpenGL Drawing ----


// really should be part of GL, glCircle!, lol
void DrawCircle (GLfloat radius, int segments)
{
	GLfloat x, y, angle, deltaAngle = 0.1f;
	
	if (segments)
		deltaAngle = (2.0 * kPI) / segments;
	
	glBegin(GL_LINE_LOOP);
	
	for (angle = 0.0f; angle <= 2.0f * kPI; angle += deltaAngle)
		glVertex2f(radius * sin(angle), radius * cos(angle));
	
	glEnd();
}

//cstr is the string that contains the text which appears in the box.
//fontSize uses defined constants to select which font.
//justify uses defined constants to justify right, left or center.
//color is used to draw the box with transparency.
//highLight TRUE makes the background of the box the assigned color with border.
//width can be set to 0 for automatically calculating the box width, or it can
//be assinged a value to make the box a fixed width specified in character width.
static void DrawTextBox (char cstr[256], int fontSize, int justify, 
								 GLubyteRGBA color, Boolean highLight, int width,
								 pRecContext pContextInfo)
{
	GLfloat fontWidth, fontHeight, textWidth, textBoxWidth, textBoxHeight;
	GLuint font;
	int stringLength = strlen (cstr);
	
	glPushMatrix();
	
	switch (fontSize)
	{
		case kLargeFont:
			font = pContextInfo->bigBoldFontList;
			fontWidth = 15.0;
			fontHeight = 19.0;
			break;
		case kMediumFont:
			font = pContextInfo->boldFontList;
			fontWidth = 5.5;
			fontHeight = 7.0;
			break;
		default:													//use small font for default
			font = pContextInfo->regFontList;
			fontWidth = 6.0;
			fontHeight = 6.0;
			break;
	}
	
	textWidth = stringLength * fontWidth;
	textBoxHeight = fontHeight + 6.0;

	//autocalculate the width if less then 1 is specified
	if (width < 1)
		textBoxWidth = textWidth + 6.0;
	else
		textBoxWidth = width * fontWidth + 6.0;

	switch (justify)
	{
		case kRightJustify:
			glTranslatef (-textBoxWidth, 0.0, 0.0);
			break;
		case kCenterJustify:
			glTranslatef (textBoxWidth * -0.5, 0.0, 0.0);
			break;
	}
	
	//draw the box shadow
	glColor4ub (0, 0, 0, 180);
	glRectf( 0.0, 0.0, textBoxWidth, textBoxHeight);
	
	//draw transparent rectangle with outline using specified color
	if (highLight)
	{
		glColor4ub (color.r, color.g, color.b, color.a * 0.25);
		glRectf( 2.0, 2.0, textBoxWidth - 2.0, textBoxHeight - 2.0);

		glColor4ub (color.r , color.g, color.b, color.a);
		glLineWidth (1.0);
		glBegin(GL_LINE_LOOP);
			glVertex2d ( 1.0, 1.0);
			glVertex2d ( 1.0, textBoxHeight - 2.0);
			glVertex2d ( textBoxWidth - 2.0, textBoxHeight - 2.0);
			glVertex2d ( textBoxWidth - 2.0, 1.0);
		glEnd();
	}
	else
		glColor4ub (color.r , color.g, color.b, color.a);

	switch (justify)
	{
		case kRightJustify:
			glRasterPos2d (textBoxWidth - textWidth + 3.0, 2.0);
			break;
		case kCenterJustify:
			glRasterPos2d (textBoxWidth * 0.5 - textWidth * 0.5, 2.0);
			break;
		default:														//default to left justify
			glRasterPos2d (3.0, 2.0);
			break;
	}
	
	drawCStringGL (cstr, font);								//draw the text
	
	glPopMatrix();
}

//draws the feeback mode and trap selection indicators
static void DrawIndicators (pRecContext pContextInfo)
{
	Boolean highlightTrapA, highlightTrapB;
	char cstrTrapA[8], cstrTrapB[8];
	GLfloat width, height;
	GLubyteRGBA colorTrapA, colorTrapB;
	
	//==================================ENS
	GLubyteRGBA colorRecordIndicator;
	char cstrRIndicator[8];
	char cstrFileCycle[2];
	//==================================ENS
	
	
	width = pContextInfo->camera.viewWidth;
	height = pContextInfo->camera.viewHeight;

	if (pContextInfo->data->instrument.trapAB)
	{
		highlightTrapA = TRUE;
		highlightTrapB = TRUE;
	}
	else if (pContextInfo->data->instrument.trapA)
	{
		highlightTrapA = TRUE;
		highlightTrapB = FALSE;
	}
	else if (pContextInfo->data->instrument.trapB)
	{
		highlightTrapA = FALSE;
		highlightTrapB = TRUE;
	}
	
	switch (pContextInfo->data->quicktime.byteFromTrapA[mode])
	{
		case kManual:
			colorTrapA.r = 255;
			colorTrapA.g = 255;
			if (pContextInfo->data->instrument.hostPositionFeedbackOn) colorTrapA.b = 0; else colorTrapA.b = 255; //SBS
			colorTrapA.a = 127;
			sprintf (cstrTrapA, "N");
			break;
		case kConstantForce:
			colorTrapA.r = 0;
			colorTrapA.g = 255;
			colorTrapA.b = 0;
			colorTrapA.a = 255;
			sprintf (cstrTrapA, "F");
			break;
		case kConstantPosition:
			colorTrapA.r = 255;
			colorTrapA.g = 230;
			colorTrapA.b = 0;
			colorTrapA.a = 255;
			sprintf (cstrTrapA, "P");
			break;
		case kSiblingConstantForce:
			colorTrapA.r = 255;
			colorTrapA.g = 0;
			colorTrapA.b = 255;
			colorTrapA.a = 255;
			sprintf (cstrTrapA, "A");
			break;
		case kSiblingConstantPosition:
			colorTrapA.r = 255;
			colorTrapA.g = 127;
			colorTrapA.b = 0;
			colorTrapA.a = 255;
			sprintf (cstrTrapA, "SP");
			break;
		default:
			colorTrapA.r = 255;
			colorTrapA.g = 0;
			colorTrapA.b = 0;
			colorTrapA.a = 255;
			sprintf (cstrTrapA, "er");
	}
	
	switch (pContextInfo->data->quicktime.byteFromTrapB[mode])
	{
		case kManual:
			colorTrapB.r = 255;
			colorTrapB.g = 255;
			if (pContextInfo->data->instrument.hostPositionFeedbackOn) colorTrapB.b = 0; else colorTrapB.b = 255; 
			colorTrapB.a = 127;
			sprintf (cstrTrapB, "N");
			break;
		case kConstantForce:
			colorTrapB.r = 0;
			colorTrapB.g = 255;
			colorTrapB.b = 0;
			colorTrapB.a = 255;
			sprintf (cstrTrapB, "F");
			break;
		case kConstantPosition:
			colorTrapB.r = 255;
			colorTrapB.g = 230;
			colorTrapB.b = 0;
			colorTrapB.a = 255;
			sprintf (cstrTrapB, "P");
			break;
		case kSiblingConstantForce:
			colorTrapB.r = 255;
			colorTrapB.g = 0;
			colorTrapB.b = 255;
			colorTrapB.a = 255;
			sprintf (cstrTrapB, "A");
			break;
		case kSiblingConstantPosition:
			colorTrapB.r = 255;
			colorTrapB.g = 127;
			colorTrapB.b = 0;
			colorTrapB.a = 255;
			sprintf (cstrTrapB, "SP");
			break;
		default:
			colorTrapB.r = 255;
			colorTrapB.g = 0;
			colorTrapB.b = 0;
			colorTrapB.a = 255;
			sprintf (cstrTrapB, "er");
	}


	//===============================================================ENS
	switch( pContextInfo->data->dFileData.currentWriteState )
	{
		case kWritingFast:
		{
			colorRecordIndicator.r = 255;
			colorRecordIndicator.g = 0;
			colorRecordIndicator.b = 0;
			colorRecordIndicator.a  = 255;
			
//			memset( cstrRIndicator, 0, strlen( cstrRIndicator ) );
			sprintf( cstrRIndicator, "F" );
			
//			memset( cstrFileCycle, 0, strlen( cstrFileCycle ) );
			sprintf( cstrFileCycle, "%c\0", pContextInfo->data->dFileData.posFileCycle );
			
			break;
		}
		case kWritingSlow:
		{
			colorRecordIndicator.r = 255;
			colorRecordIndicator.g = 255;
			colorRecordIndicator.b = 255;
			colorRecordIndicator.a = 127;
			
//			memset( cstrRIndicator, 0, strlen( cstrRIndicator ) );
			sprintf( cstrRIndicator, "S" );
			
//			memset( cstrFileCycle, 0, strlen( cstrFileCycle ) );
			sprintf( cstrFileCycle, "%c\0", pContextInfo->data->dFileData.posFileCycle );
			
			break;
		}
		default:
		{
			colorRecordIndicator.r = 255;
			colorRecordIndicator.g = 255;
			colorRecordIndicator.b = 255;
			colorRecordIndicator.a = 127;
			
//			memset( cstrRIndicator, 0, strlen( cstrRIndicator ) );
			sprintf( cstrRIndicator, "" );
			
//			memset( cstrFileCycle, 0, strlen( cstrFileCycle ) );
			sprintf( cstrFileCycle, "" );
			
			break;
		}
	}
	//===============================================================ENS


	glPushMatrix();
		glTranslatef (width * -0.5 + 27.0, height * 0.5 - 32.0, 0.0);
		DrawTextBox (cstrTrapA, kLargeFont, kCenterJustify,
						 colorTrapA, highlightTrapA, 2, pContextInfo);
		
		glTranslatef (40.0, 0.0, 0.0);
		DrawTextBox (cstrTrapB, kLargeFont, kCenterJustify,
						 colorTrapB, highlightTrapB, 2, pContextInfo);
						 
		//==========================================================================================ENS				 				 
		glTranslatef( width - 135.0, 0.0, 0.0 );
		DrawTextBox( cstrRIndicator, kLargeFont, kCenterJustify, colorRecordIndicator, TRUE, 2, pContextInfo );
		
		glTranslatef( 40.0, 0.0, 0.0 );
		DrawTextBox( cstrFileCycle, kLargeFont, kCenterJustify, colorRecordIndicator, TRUE, 2, pContextInfo );
		//==========================================================================================ENS
			
	glPopMatrix();
}

// ---------------------------------
void DrawGridDefault(pRecContext pContextInfo)
{
	char titleString[128], unitString[16];
	static float msgPresistance = 10.0f;
	int i, j;
	GLfloat x, y, xScale;
	
	pRecPlot plot = pContextInfo->plotArray[0];
	
	char cstr [256];
	GLint matrixMode, line = 1;
	GLboolean depthTest = glIsEnabled (GL_DEPTH_TEST);
	GLfloat width = pContextInfo->camera.viewWidth;
	GLfloat height = pContextInfo->camera.viewHeight;
	
	if (!pContextInfo)
		return;

	//Draw plot specific label parameters
	for ( i = 1; i < pContextInfo->plotCount; i++)
	{
		plot = pContextInfo->plotArray[i];
	
	if (!plot->hide && !plot->drawVideo || pContextInfo->plotIndex == i)
		{
	GLfloat textBoxWidth, textBoxHeight, fontWidth;
	
	textBoxWidth = 53.0;
	textBoxHeight = 14.0;
	fontWidth = 7.0;
	
	glPushMatrix();
	
	glTranslatef(	(width / -2.0), 
						(plot->screenPosition.y * pContextInfo->gridSpace),
						0.0	);
	
	glColor4ub	(	plot->color.r,
						plot->color.g,
						plot->color.b,
						
						pContextInfo->plotArray[0]->color.a	);			//use grid transparency
	glLineWidth(1.0);
	
	if (!plot->hide)
	{
		glBegin(GL_LINES);
			glVertex2f(0.0, 0.0);												//make into a static array, debug
			glVertex2f(width, 0.0);
		glEnd();
	}
	
	//draws the scale
	if (plot->drawScale.y)
	{
		//draw text background boxes
		glColor4ub	(	0, 0, 0, 180);
		glRectf( fontWidth + 1.0, 0.0, fontWidth + textBoxWidth, textBoxHeight);
	
		glColor4ub	(	plot->color.r,
						plot->color.g,
						plot->color.b,
						plot->color.a	);
	
		if (plot->scale.y)
			sprintf (cstr, "%0.1f", 1.0001 / plot->scale.y);
		glRasterPos3d (fontWidth + textBoxWidth - (strlen (cstr) * fontWidth), 2, 0); 
		drawCStringGL (cstr, pContextInfo->boldFontList);
		
		glRasterPos3d (10, -12, 0); 
		drawCStringGL (
				pContextInfo->data->quicktime.channelString[plot->channel.y], 
				pContextInfo->boldFontList);
	}
	
	//draws the translate
	if (plot->drawTranslate.y)
	{
		glColor4ub	(	0, 0, 0, 180);
		glRectf( (width - fontWidth) - textBoxWidth, 
					0.0, 
					(width - fontWidth) - 1.0, 
					textBoxHeight);
	
		glColor4ub	(	plot->color.r,
						plot->color.g,
						plot->color.b,
						plot->color.a	);

		sprintf (cstr, "%0.1f", -plot->translate.y);
		glRasterPos3d ((width - fontWidth) - (strlen (cstr) * fontWidth), 2, 0); 
		drawCStringGL (cstr, pContextInfo->boldFontList);
	}
	
	
	//Highlight active plot, draws plot colored rectangle around both text boxes
	if ( i == pContextInfo->plotIndex || pContextInfo->selectAll)
	{
	glColor4ub	(	plot->color.r,
						plot->color.g,
						plot->color.b,
						pContextInfo->plotArray[0]->color.a * 0.7	);
	
	glRectf( fontWidth + 1.0 + 1.0, 1.0,
				fontWidth + textBoxWidth - 2.0, textBoxHeight - 2.0 );
				
	glRectf( (width - fontWidth) - (3.0), 1.0,			
				(width - fontWidth) - (textBoxWidth - 2.0), textBoxHeight - 2.0 );
	
	glColor4ub	(	plot->color.r,
						plot->color.g,
						plot->color.b,
						plot->color.a	);
	
	glBegin(GL_LINE_LOOP);
		glVertex2f(fontWidth + 2.0, 1.0);	
		glVertex2f(fontWidth + 2.0, textBoxHeight - 2.0);
		glVertex2f(fontWidth + textBoxWidth - 2.0, textBoxHeight - 2.0);
		glVertex2f(fontWidth + textBoxWidth - 2.0, 1.0);
	glEnd();
	
	glBegin(GL_LINE_LOOP);
		glVertex2f((width - fontWidth) - 3.0, 1.0);	
		glVertex2f((width - fontWidth) - 3.0, textBoxHeight - 2.0);
		glVertex2f((width - fontWidth) -(textBoxWidth - 2.0), textBoxHeight - 2.0);
		glVertex2f((width - fontWidth) -(textBoxWidth - 2.0), 1.0);
	glEnd();
	}	
		
	glPopMatrix();
		}
	}
	
	//draw the x scale
	plot = pContextInfo->plotArray[pContextInfo->plotIndex];
	
	glPushMatrix();	
		glTranslatef (0.0, height * -0.5 + 9.0, 0.0);

		if (plot->scale.x && plot->channel.x == kChannelOscilloscope)
		{
			xScale = (1.0 / pContextInfo->data->quicktime.mainFrequency) * 
						plot->sampleInterval * 1.0001 / plot->scale.x;
			sprintf (titleString, "Oscilloscope");
			sprintf (unitString, "sec");
		}
		else if (plot->scale.x && plot->channel.x == kChannelTime)
		{
			xScale = (1.0 / pContextInfo->data->quicktime.mainFrequency) * 
						plot->sampleInterval * 1.0001 / plot->scale.x;
			sprintf (titleString, "Time");
			sprintf (unitString, "sec");
		}
		else if (plot->scale.x && plot->channel.x == kChannelCycleCount ||
					plot->scale.x && plot->channel.x == kChannelTrapACycleCount ||
					plot->scale.x && plot->channel.x == kChannelTrapBCycleCount)
		{
			xScale = (1.0 / pContextInfo->data->quicktime.mainFrequency) * 
						plot->sampleInterval * 1.0001 / plot->scale.x;
			sprintf (titleString, 
						pContextInfo->data->quicktime.channelString[plot->channel.x]);
			sprintf (unitString,
						pContextInfo->data->quicktime.channelUnits[plot->channel.x]);
		}
		else
		{
			xScale = 1.0001 / plot->scale.x;
			sprintf (titleString, 
						pContextInfo->data->quicktime.channelString[plot->channel.x]);
			sprintf (unitString,
						pContextInfo->data->quicktime.channelUnits[plot->channel.x]);
		}
		
		if (xScale >= 10.0)
			sprintf (cstr, "%s  %0.0f %s/div", titleString, xScale, unitString);
		else
			sprintf (cstr, "%s  %0.4f %s/div", titleString, xScale, unitString);
		
		DrawTextBox (cstr, kMediumFont, kCenterJustify,
						 plot->color, FALSE, 0, pContextInfo);
	glPopMatrix();
}

// ---------------------------------
void DrawPullLimits(pRecContext pContextInfo)
{	//unless frameBuffer is NOT being cleared
	GLfloat minForce, maxForce, minPosition, maxPosition;
	GLfloat left = -100000.0, right = 100000.0;
	GLfloat bottom = -100000.0, top = 100000.0;
		
	pRecPlot plot = pContextInfo->plotArray[1];
	if (!plot)
		return;

	minForce = pContextInfo->data->instrument.minForce;
	maxForce = pContextInfo->data->instrument.maxForce;
	minPosition = pContextInfo->data->instrument.minPosition;
	maxPosition = pContextInfo->data->instrument.maxPosition;
	
	if (plot->altColorUpdate)
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
	
	if ((pContextInfo->data->instrument.runMode == kAutoPull1)||(pContextInfo->data->instrument.runMode == kAutoPull2))
		glColor4ub (plot->color.r, plot->color.g, plot->color.b, 200);
	else
		glColor4ub (255, 255, 255, 127);
		
	glLineWidth (1.0);

	glBegin (GL_LINES);
		glVertex2f (left, -maxForce);
		glVertex2f (right, -maxForce);
		
		glVertex2f (left, -minForce);
		glVertex2f (right, -minForce);
	
		if ((pContextInfo->data->instrument.runMode == kAutoPull1)||(pContextInfo->data->instrument.runMode == kAutoPull2))
			glColor4ub (0, 255, 255, 200);
		else
			glColor4ub (255, 255, 255, 127);
	
		if (pContextInfo->data->instrument.maxPositionOn)
		{
			glVertex2f (maxPosition, top);
			glVertex2f (maxPosition, bottom);
		}
		
		if (pContextInfo->data->instrument.minPositionOn)
		{	
			glVertex2f (minPosition, top);
			glVertex2f (minPosition, bottom);
		}
	glEnd();
	
	if (plot->altColorUpdate)
	{
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
		if (pContextInfo->data->control.altColor)
		{
			plot->color.r = 255 - plot->color.r;
			plot->color.g = 255 - plot->color.g;
			plot->color.b = 255 - plot->color.b;
		}
	}
		
/*	

	
	glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a);
	
	width = pContextInfo->camera.viewWidth;
	height = pContextInfo->camera.viewHeight;

	//set graph specific values
	switch (pContextInfo->graphType)
	{
		case kGraphForcePosition:
			break;
	}
	*/
}
		
// ---------------------------------
void DrawForcePsdLever(pRecContext pContextInfo)
{
	char cstr [256];
	GLfloat width, height;
	GLfloat fontWidth = 15.25;
	int line;
	int leftX, leftY, leftSum, leftZ;
	int rightX, rightY, rightSum, rightZ;
	GLfloat leftIrisRatio, rightIrisRatio;
		
	pRecPlot plot = pContextInfo->plotArray[1];
	if (!plot)
		return;
	
	glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a);
	
	width = pContextInfo->camera.viewWidth;
	height = pContextInfo->camera.viewHeight;

	//set graph specific values
	switch (pContextInfo->graphType)
	{
		case kGraphPSD:
			leftX = pContextInfo->data->quicktime.latest[kChannelTrapAPsdX];
			leftY = pContextInfo->data->quicktime.latest[kChannelTrapAPsdY];
			leftSum = pContextInfo->data->quicktime.latest[kChannelTrapAPsdSum];
			if (pContextInfo->data->quicktime.latest[kChannelTrapAPsdSum])
				leftIrisRatio = (GLfloat)pContextInfo->data->quicktime.latest[kChannelTrapAIris] /
							(GLfloat)pContextInfo->data->quicktime.latest[kChannelTrapAPsdSum];
			else
				leftIrisRatio = 0.0;
			rightX = pContextInfo->data->quicktime.latest[kChannelTrapBPsdX];
			rightY = pContextInfo->data->quicktime.latest[kChannelTrapBPsdY];
			rightSum = pContextInfo->data->quicktime.latest[kChannelTrapBPsdSum];
			if (pContextInfo->data->quicktime.latest[kChannelTrapBPsdSum])
				rightIrisRatio = (GLfloat)pContextInfo->data->quicktime.latest[kChannelTrapBIris] /
							(GLfloat)pContextInfo->data->quicktime.latest[kChannelTrapBPsdSum];
			else
				rightIrisRatio = 0.0;
			break;
		case kGraphLever:
			leftX = pContextInfo->data->quicktime.latest[kChannelTrapALeverX];
			leftY = pContextInfo->data->quicktime.latest[kChannelTrapALeverY];
			leftSum = pContextInfo->data->quicktime.latest[kChannelTrapALeverSum];
			rightX = pContextInfo->data->quicktime.latest[kChannelTrapBLeverX];
			rightY = pContextInfo->data->quicktime.latest[kChannelTrapBLeverY];
			rightSum = pContextInfo->data->quicktime.latest[kChannelTrapBLeverSum];
			break;
	}
	
	if (pContextInfo->graphType == kGraphNetForce)
	{
		glPushMatrix();
			glTranslatef( width * 0.5, height * 0.5 - 28.0, 0.0 );
		
			line = 1;		
				
			sprintf (cstr, "%0.1f X", 
						pContextInfo->data->quicktime.latest[kChannelNetForceX]);
			glRasterPos2d ( -(7 + strlen (cstr) * 7.0), -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);

			sprintf (cstr, "%0.1f Y", 
						pContextInfo->data->quicktime.latest[kChannelNetForceY]);
			glRasterPos2d ( -(7 + strlen (cstr) * 7.0), -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
	
			sprintf (cstr, "%0.1f Z", 
						pContextInfo->data->quicktime.latest[kChannelNetForceZ]);
			glRasterPos2d ( -(7 + strlen (cstr) * 7.0), -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
	
			sprintf (cstr, "%0.1f", 
						pContextInfo->data->quicktime.latest[kChannelTension]);
			glRasterPos2d ( -(10 + strlen (cstr) * fontWidth), 0);
			drawCStringGL (cstr, pContextInfo->bigBoldFontList);
			
		glPopMatrix();
	}
	else
	{
		glPushMatrix();
			glTranslatef (width * -0.5, height * 0.5 - 28.0, 0.0);

			line = 1;

			sprintf (cstr, "%d X", leftX);
			glRasterPos2d ( 69 - strlen (cstr) * 7.0, -14 * line++);	
			drawCStringGL (cstr, pContextInfo->boldFontList);

			sprintf (cstr, "%d Y", leftY);
			glRasterPos2d ( 69 - strlen (cstr) * 7.0, -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
				
			sprintf (cstr, "%d", leftSum);
			glRasterPos2d (fontWidth * 6.8 - strlen (cstr) * fontWidth, 0);
			drawCStringGL (cstr, pContextInfo->bigBoldFontList);
		glPopMatrix();
	}
	
	//draw TrapA values for PSD only
	if (pContextInfo->graphType == kGraphPSD)
	{
		glPushMatrix();
			glTranslatef (width * -0.5, height * -0.5 + 28.0, 0.0);
		
			line = 1;
	
			sprintf (cstr, "Iris/Sum");
			glRasterPos2d ( 69 - strlen (cstr) * 7.0, 14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
		
			sprintf (cstr, "%0.3f", leftIrisRatio);
			glRasterPos2d ( 49 - strlen (cstr) * 7.0, 14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
		glPopMatrix();
	}
	

	//draw TrapB values for PSD and Lever graphs
	if (pContextInfo->graphType == kGraphPSD || 
		 pContextInfo->graphType == kGraphLever)
	{
		//set color for right side values and begin drawing
		plot = pContextInfo->plotArray[2];
		if (!plot)
			return;
			
		glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a);
	
		glPushMatrix();
			glTranslatef(	width * 0.5, height * 0.5 - 28.0, 0.0 );
		
			line = 1;

			sprintf (cstr, "%d X", rightX);
			glRasterPos2d ( -(7 + strlen (cstr) * 7.0), -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);

			sprintf (cstr, "%d Y", rightY);
			glRasterPos2d ( -(7 + strlen (cstr) * 7.0), -14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
	
			sprintf (cstr, "%d", rightSum);
			glRasterPos2d ( -(10 + strlen (cstr) * fontWidth), 0);
			drawCStringGL (cstr, pContextInfo->bigBoldFontList);
		glPopMatrix();
	}
	
	//draw TrapB values for PSD only
	if (pContextInfo->graphType == kGraphPSD)
	{
		glPushMatrix();
			glTranslatef (width * 0.5, height * -0.5 + 28.0, 0.0);
	
			line = 1;
					
			sprintf (cstr, "Iris/Sum");
			glRasterPos2d ( -(4 + strlen (cstr) * 7.0), 14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
		
			sprintf (cstr, "%0.3f", rightIrisRatio);
			glRasterPos2d ( -(8 + strlen (cstr) * 7.0), 14 * line++);
			drawCStringGL (cstr, pContextInfo->boldFontList);
		glPopMatrix();
	}
}


// ---------------------------------
void DrawMap(pRecContext pContextInfo)
{
	char cstr [256];
	GLfloat width, height;
	int plotSelected = FALSE;
	GLubyteRGBA color;
	
	color.r = 255;
	color.g = 0;
	color.b = 0;
	color.a = 255;
	
	pRecPlot plot = pContextInfo->plotArray[1];
	if (!plot)
		return;
	
	if (pContextInfo->plotIndex == 1)
		plotSelected = TRUE;
	
	glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a);
	
	width = pContextInfo->camera.viewWidth;
	height = pContextInfo->camera.viewHeight;

	glPushMatrix();
	
		sprintf (cstr, "%0.1f X  ", 
					pContextInfo->data->quicktime.latest[kChannelMotorX]);
		glTranslatef(	width * 0.5 - 8.0, height * 0.5 - 20.0, 0.0 );
		DrawTextBox (cstr, kMediumFont, kRightJustify,
						 plot->color, plotSelected, 10, pContextInfo);
						 
		sprintf (cstr, "%0.1f Y  ", 
					pContextInfo->data->quicktime.latest[kChannelMotorY]);
		glTranslatef(	0.0, -14.0, 0.0 );
		DrawTextBox (cstr, kMediumFont, kRightJustify,
						 plot->color, plotSelected, 10, pContextInfo);

		sprintf (cstr, "%0.1f Z  ", 
					pContextInfo->data->quicktime.latest[kChannelMotorZ]);
		glTranslatef(	0.0, -14.0, 0.0 );
		DrawTextBox (cstr, kMediumFont, kRightJustify,
						 plot->color, plotSelected, 10, pContextInfo);
						 
	glPopMatrix();

/*	
	if (pContextInfo->data.instrument.limits)
	{
		glPushMatrix();
	
			sprintf (cstr, " LIMITS!", 
						pContextInfo->data->quicktime.latest[kChannelMotorX]);
			glTranslatef(	0.0, -32.0, 0.0 );
			DrawTextBox (cstr, kLargeFont, kCenterJustify,
							color, plotSelected, 0, pContextInfo);
		glPopMatrix();
	}
*/
}

// ---------------------------------
void DrawGrid (pRecContext pContextInfo)
{
	char cstr [256];
	GLfloat msgPresistance = 10.0;
	GLint matrixMode, line = 1;
	GLboolean depthTest = glIsEnabled (GL_DEPTH_TEST);
	GLfloat x, y, width, height, gridLeftEdge, gridBottomEdge;
	
	if (!pContextInfo)
		return;
	
	width = pContextInfo->camera.viewWidth;
	height = pContextInfo->camera.viewHeight;
		
	//calculate the left bottom grid point, not the window corner
	if (pContextInfo->gridSpace)
	{
		gridLeftEdge = (float)(pContextInfo->gridSpace * 
							(int)(width / (-2.0 * pContextInfo->gridSpace)));
		gridBottomEdge = (float)(pContextInfo->gridSpace * 
							  (int)(height / (-2.0 * pContextInfo->gridSpace)));
	}
	
	glDisable (GL_DEPTH_TEST); // ensures that draw items not removed
	glEnable (GL_BLEND); // for transparency
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ditto
	
	// set orthograhic 1:1  pixel transform in local view coords
	glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
	glMatrixMode (GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity ();
	glMatrixMode (GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity ();
	if (width && height)
		glScalef (2.0 / width, 2.0 /  height, 1.0);


	switch (pContextInfo->graphType)
	{
		case kGraphLever:
			DrawForcePsdLever (pContextInfo);
			break;
		case kGraphPSD:
			DrawForcePsdLever (pContextInfo);
			break;
		case kGraphNetForce:
			DrawForcePsdLever (pContextInfo);
			break;
		case kGraphMap:
			DrawMap (pContextInfo);
			break;
		default:
			DrawGridDefault(pContextInfo);
			break;
	}
		
	glColor4ub	(	pContextInfo->plotArray[0]->color.r,
						pContextInfo->plotArray[0]->color.g,
						pContextInfo->plotArray[0]->color.b,
						pContextInfo->plotArray[0]->color.a		);
			
	//window border
	glLineWidth(2.0);
	glBegin(GL_LINES);

		glVertex2f(width * -0.5, height * 0.5);												//make into a static array, debug
		glVertex2f(width * 0.5, height * 0.5);
		
		glVertex2f(width * 0.5, height * 0.5);
		glVertex2f(width * 0.5, height * -0.5);
		
		glVertex2f(width * 0.5, height * -0.5);
		glVertex2f(width * -0.5, height * -0.5);
		
		glVertex2f(width * -0.5, height * -0.5);
		glVertex2f(width * -0.5, height * 0.5);	
		
	glEnd();
		
	//draw grid points
	if (pContextInfo->plotArray[0]->pointSize > 0.0)
	{
		glPointSize (pContextInfo->plotArray[0]->pointSize);
		
		//draw our grid
		glBegin(GL_POINTS);
		
		for (y = gridBottomEdge; y < (height * 0.5); y += pContextInfo->gridSpace)
		{
			for (x = gridLeftEdge; x < (width * 0.5); x += pContextInfo->gridSpace)
				glVertex2f(x, y);												//make into a static array, debug
		}		

		glEnd();
	}
	
	//draws border, circle, and crosshair according to lineWidth
	if (pContextInfo->plotArray[0]->lineWidth)
	{
		glLineWidth(1.0);
		
		DrawCircle (pContextInfo->gridSpace * 3.0, (int)(width * 0.2));

		//crosshair
		if (pContextInfo->plotArray[0]->lineWidth > 1.0)
		{
			glBegin (GL_LINES);
		
			glVertex2f (width * -0.5, 0.0);
			glVertex2f (width * 0.5, 0.0);
		
			glVertex2f (0.0, height * -0.5);
			glVertex2f (0.0, height * 0.5);

			glEnd();
		}
		
		//draw radius scale based on y scale of currently selected plot
		glPushMatrix();	
			glTranslatef (width * 0.5, height * -0.5, 0.0);
			
			if (pContextInfo->plotArray[pContextInfo->plotIndex]->scale.y)
				sprintf (cstr, "%d Radius", (int)(3.00001 / 
							pContextInfo->plotArray[pContextInfo->plotIndex]->scale.y));
			glRasterPos2d (strlen (cstr) * -7.0, 10);
			drawCStringGL (cstr, pContextInfo->boldFontList);
		glPopMatrix();
	}
	
	
	//set origin to lower left corner of window
	glPushMatrix();
		glTranslatef (width * -0.5, height * -0.5, 0.0);


	// message string
		if (pContextInfo->message[0]) 
		{
			float currDelta = getElapsedTime () - pContextInfo->msgTime;
		
			glColor4f (1.0, 1.0, 1.0, (msgPresistance - currDelta) * 0.1);
			glRasterPos3d (10, 10, 0); 
			drawCStringGL (pContextInfo->message, pContextInfo->boldFontList);
				
		if (currDelta > msgPresistance)
			pContextInfo->message[0] = 0;
		}
	glPopMatrix();

	if (pContextInfo->drawIndicators)
		DrawIndicators (pContextInfo);			//draw feedback mode trap selection


	//draws the window resize tab (3 diaganol lines,) in the lower right corner
	glPushMatrix();
		glTranslatef (width * 0.5, height * -0.5, 0.0);
		glColor4f (1.0, 1.0, 1.0, 0.5);
		glLineWidth (1.0);
		glBegin(GL_LINES);
			glVertex2d (-12, 2);
			glVertex2d (-2, 12);
			glVertex2d (-8, 2);
			glVertex2d (-2, 8);
			glVertex2d (-4, 2);
			glVertex2d (-2, 4);
		glEnd();
	glPopMatrix();


	// reset orginal matrices
	glPopMatrix(); // GL_MODELVIEW
	glMatrixMode (GL_PROJECTION);
	glPopMatrix();
	glMatrixMode (matrixMode);
	if (depthTest)
		glEnable (GL_DEPTH_TEST);
	glReportError ();
	
	if (pContextInfo->drawMenu)
		drawInfo (pContextInfo);			//draw help and stats
}


///////////////////////////////////////////////updates a single plot with new data if not frozen and draws it if not hidden
void DrawPlot (pRecContext pContextInfo, pRecPlot plot)
{		
	glPushMatrix();

	glRotatef ( plot->rotate.angle, 
					plot->rotate.x, 
					plot->rotate.y, 
					plot->rotate.z );

		//screen position is done at one unit per grid
	glTranslatef ( plot->screenPosition.x, 
						plot->screenPosition.y, 
						plot->screenPosition.z );
		
	glScalef (plot->scale.x, plot->scale.y, plot->scale.z);

	glTranslatef (plot->translate.x, plot->translate.y, plot->translate.z);

	glDisable (GL_LIGHTING);
	glDisable (GL_DEPTH_TEST); // ensures that draw items not removed
	glEnable (GL_BLEND); // for transparency
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ditto
	
	if (plot->altColorUpdate)
	{
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
		if (pContextInfo->data->control.altColor)
		{
			plot->color.r = 255 - plot->color.r;
			plot->color.g = 255 - plot->color.g;
			plot->color.b = 255 - plot->color.b;
		}
	}
		
	glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a); //sets default color
	

	glEnableClientState (GL_VERTEX_ARRAY);
	glEnableClientState (GL_COLOR_ARRAY);
		
	glVertexPointer (3, GL_FLOAT, 0, plot->vertexArray);
	glColorPointer (4, GL_UNSIGNED_BYTE, 0, plot->colorArray);
		
	if (plot->pointSize > 0.0)
	{
		glPointSize(plot->pointSize);
		glDrawArrays(GL_POINTS, 0, plot->vertexCount);
	}
			
	if (plot->lineWidth)
	{
		glLineWidth (1.0);
			
		if (plot->channel.x < 0)
			glDrawArrays (GL_LINE_STRIP, 0, plot->vertexCount);
		else
			glDrawArrays (GL_LINE_LOOP, 0, plot->vertexCount);
	}
			
	glDisableClientState (GL_VERTEX_ARRAY);
	glDisableClientState (GL_COLOR_ARRAY);


	if (plot->drawPullLimits)
		DrawPullLimits (pContextInfo);
	
	//if radius is positive draw a circle around the current data point 
	//unless frameBuffer is NOT being cleared
	if (plot->circle > 0.0 && pContextInfo->clearFrameBuffer)
	{
		if (plot->scale.x && plot->scale.y && plot->scale.z)		//avoids divide by zero
			glScalef (1.0 / plot->scale.x, 1.0 / plot->scale.y, 1.0 / plot->scale.z);
			
		glTranslatef ( plot->vertexArray[plot->arrayIndex * 3 + 0] * plot->scale.x, 
							plot->vertexArray[plot->arrayIndex * 3 + 1] * plot->scale.y, 
							plot->vertexArray[plot->arrayIndex * 3 + 2] * plot->scale.z);
		
		//first, draw background circle black with transparency
		glColor4ub (0, 0, 0, 127);
		glLineWidth (3.0);
		DrawCircle (plot->circle, 18);
				
		//second, draw foreground circle the same color as the plot
		glColor4ub (plot->color.r, plot->color.g, plot->color.b, plot->color.a);
		glLineWidth (1.0);
		DrawCircle (plot->circle, 18);
	}

	glPopMatrix();
	
	if (plot->altColorUpdate)
		SetIndexColor (&plot->color, &plot->colorIndex);					//excess variables and operations
}


////////////////////////////////////////////////////updates and draws the active plots
void DrawPlots (pRecContext pContextInfo)
{
	int i;
	pRecPlot plot = NULL;

	UpdateGridSpace (pContextInfo);

	//iterate through all plots and draw if not hidden
	for (i = 1; i < pContextInfo->plotCount; i++)
	{
		plot = pContextInfo->plotArray[i];

		if (plot != NULL)											//should not be necessary, debug
		{
			//unless frozen, copy the latest plot data to the vertex array
			if (!plot->freeze)
			{
				GetRecentPlotData (pContextInfo, plot);
				UpdatePlotColor (pContextInfo, plot);
			}
			
			//unless hidden, draw the plot
			if (!plot->hide)
			{
				UpdatePlotPosition (pContextInfo, plot);
				DrawPlot(pContextInfo, plot);
			}
		}
	}

	if (!pContextInfo->plotArray[0]->hide)
		DrawGrid(pContextInfo);				//draw the grid,text and window border	
}


///////////////////////////////////////////////////// updates data and draws using OpenGL 
void DrawGL (WindowRef window, Boolean swap)
{
	pRecContext pContextInfo = NULL;
	Rect rectPort;
	CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}};
	
	if (window)
		pContextInfo = GetCurrentContextInfo (window);
	if (!pContextInfo)
        return;
	if (!pContextInfo->aglContext)
		buildGL(window);
   
	// ensure the context is current	
	if (!aglSetCurrentContext (pContextInfo->aglContext))
		aglReportError ();

	GetWindowPortBounds (window, &rectPort);
	viewRect.size.width = (float) (rectPort.right - rectPort.left);
	viewRect.size.height = (float) (rectPort.bottom - rectPort.top);
	resizeGL (pContextInfo->aglContext, &pContextInfo->camera, viewRect, pContextInfo);
	updateProjection (pContextInfo->camera.viewWidth, pContextInfo->camera.viewHeight, pContextInfo->camera.viewPos.z,
					  pContextInfo->camera.aperture);
	updateModelView (&pContextInfo->camera);

	// clear our drawable, similar to old clearscreen command but window only
	// typically we redraw everything every frame, sometimes we don't clear
	// the framebuffer to allow data to persist or for video processing
	if (pContextInfo->clearFrameBuffer || pContextInfo->clearFrameBufferOnce)
	{
		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		pContextInfo->clearFrameBufferOnce = FALSE;
	}

	DrawPlots (pContextInfo);			//update and draw all active plots

	if (swap) 
	{
		aglSwapBuffers (pContextInfo->aglContext);
	}
	else
		glFlush();
}


#pragma mark ---- Command Processing ----


////////////////////////////////////////////////////draw the channels according to which axes are selected
void PostCommandAxes (pRecContext pContextInfo)
{
	char buildString[256] = "";
	
	pRecPlot plot = pContextInfo->plotArray[pContextInfo->plotIndex];
	
	if (pContextInfo->selectAll)
	{
		PostMessage ("All Plots selected", pContextInfo);
		return;
	}
	
	//start building the string with the X axis
	if (pContextInfo->plotArray[pContextInfo->plotIndex]->channel.x >= 0)
		sprintf (buildString, "X: %s    ", 
			pContextInfo->data->quicktime.channelString[plot->channel.x]);
	else if (plot->channel.x == kChannelTime)
		sprintf (buildString, "X: Time    ");
	else if (plot->channel.x == kChannelOscilloscope)
		sprintf (buildString, "X: Oscilloscope    ");

	//add the y axis to the string
	if (plot->channel.y >= 0)
		sprintf (buildString, "%sY: %s    ", buildString, 
			pContextInfo->data->quicktime.channelString[plot->channel.y]);
	else if (plot->channel.x == kChannelTime)
			sprintf (buildString, "%sY: Time    ", buildString);
	else if (plot->channel.x == kChannelOscilloscope)
			sprintf (buildString, "%sY: Oscilloscope    ", buildString);
	
	//add on the z axis unless it is Null
	if (plot->channel.z != kChannelNull || pContextInfo->axes.z)
		sprintf (buildString, "%sZ: %s", buildString, 
			pContextInfo->data->quicktime.channelString[plot->channel.z]);

	PostMessage (buildString, pContextInfo);
}

///////////////////////////////////////////////////////////////////////
void PostCommand(int command, void* userData)
{
	int i, temp;
	char buildString[256] = "";

	pRecContext pContextInfo = (pRecContext) userData;
	if (pContextInfo == NULL) 
		return;
			
	pRecPlot plot = pContextInfo->plotArray[pContextInfo->plotIndex];
	if (plot == NULL) 
		return;
	
	//graph window commands
	switch (command) 
	{
		case kGraphSampleInterval:
			if (plot->sampleInterval > 1)
			{
				if (plot->averageType >= kRootMeanSquare)
					sprintf (buildString, "RMS noise using %d samples", 
								plot->sampleInterval);
				else
					sprintf (buildString, "Averaged signal using %d samples", 
								plot->sampleInterval);
			}
			else
				sprintf (buildString, "Sample interval is %d, plot raw data", 
							plot->sampleInterval);
			PostMessage (buildString, pContextInfo);
			break;
	
		case kGraphNew:
			if (pContextInfo->lockPlots)
				PostMessage ("New Plot unavailable", pContextInfo);
			else
				PostMessage ("New Plot Created", pContextInfo);
			break;
							
		case kGraphDelete:
			if (pContextInfo->lockPlots)
				PostMessage ("Plots locked, delete unavailable", pContextInfo);
			else if (pContextInfo->plotIndex == 0)
				PostMessage ("Cannot delete the grid", pContextInfo);
			else
				PostMessage ("Plot Deleted", pContextInfo);
			break;
						
		case kGraphNext:
			PostCommandAxes (pContextInfo);
			break;
		
		case kGraphGrid:
			PostMessage ("Grid Selected", pContextInfo);
			break;

		//scaling up and down toggles
		case kGraphSelectAxes:			//zoom mode selection, options XY, X or Y
			if (pContextInfo->axes.x)
				sprintf (buildString, "X");
			if (pContextInfo->axes.y)
				sprintf (buildString, "%sY", buildString);
			if (pContextInfo->axes.z)
				sprintf (buildString, "%sZ", buildString);
			
			if (strlen(buildString) > 1)
				sprintf (buildString, "%s axes", buildString);
			else
				sprintf (buildString, "%s axis", buildString);
			
			if (pContextInfo->data->control.altModifier)
			{
				if (pContextInfo->enableZ)
					sprintf (buildString, "Z axis enabled");
				else
					sprintf (buildString, "Z axis disabled");
			}
			
			PostMessage (buildString, pContextInfo);
			break;
								
		case kGraphMenu:						//the menu doesn't need to be announced
			break;
	}

	//plot specific commands
	switch (command) 
	{
		case kGraphCenter:		   //center on current data point toggle
			if (pContextInfo->lockPlots)
				PostMessage ("Center Unavailable", pContextInfo);
			else
			{
				if (pContextInfo->data->control.altModifier)
				{
					if (plot->centerPerGridSpace)
						PostMessage ("Center per grid space", pContextInfo);
					else
						PostMessage ("Center per data point", pContextInfo);
				}
				else
				{
					if (plot->center)
						PostMessage ("Center On", pContextInfo);
					else
						PostMessage ("Center Off", pContextInfo);
				}
			}
			break;
						
		case kGraphScroll:		   //scroll on current data point toggle
			if (plot->scroll)
				PostMessage ("Scroll On", pContextInfo);
			else
				PostMessage ("Scroll Off", pContextInfo);
			break;	
						
		case kGraphLines:
			if (plot->lineWidth)
				PostMessage ("Lines On", pContextInfo);
			else
				PostMessage ("Lines Off", pContextInfo);
			break;
				
		case kGraphPoints:
			sprintf (buildString, "Point Size %d", (int)plot->pointSize);
			PostMessage (buildString, pContextInfo);
			break;
		
		case kGraphFreeze:
			if (plot->freeze)
				PostMessage ("Freeze", pContextInfo);
			else
				PostMessage ("Unfreeze", pContextInfo);
			break;	
				
		case kGraphHide:
			if (plot->hide)
				PostMessage ("Hide", pContextInfo);
			else
				PostMessage ("Unhide", pContextInfo);
			break;

		case kGraphChannelDown:					//previous channel
			PostCommandAxes (pContextInfo);
			break;
		case kGraphChannelUp:					//next channel
			PostCommandAxes (pContextInfo);
			break;

		case kGraphRotateIncreaseOn:
		sprintf (buildString, "bead_dia %0.3f", tan(plot->rotate.angle / 57.296) * plot->scale.x / plot->scale.y 
			* 106.1 / (1.4379 - 0.0219 * pContextInfo-> data->quicktime.latest[kChannelTrapATemp]));
			PostMessage (buildString, pContextInfo);
			break;
		// 57.296 degrees in a radian,  106.1 = 1000/(3*Pi),  1.4379-0.0219*temp. is viscosity change of water near room temperature
		case kGraphRotateIncreaseOff:
		sprintf (buildString, "bead_dia %0.3f", tan(plot->rotate.angle / 57.296) * plot->scale.x / plot->scale.y 
			* 106.1 / (1.4379 - 0.0219 * pContextInfo-> data->quicktime.latest[kChannelTrapATemp]));
			PostMessage (buildString, pContextInfo);
			break;
		case kGraphRotateDecreaseOn:
		sprintf (buildString, "Angle %0.1f", plot->rotate.angle);
			PostMessage (buildString, pContextInfo);
			break;
		case kGraphRotateDecreaseOff:
		sprintf (buildString, "Angle %0.1f", plot->rotate.angle);
			PostMessage (buildString, pContextInfo);
			break;
								
		case kGraphZoomOn:
			if (pContextInfo->data->control.altModifier)
			{
				if (plot->autoZoom.x || plot->autoZoom.y || plot->autoZoom.z)
				{
					sprintf (buildString, "Auto Zoom ");
					
					if (plot->autoZoom.x)
						sprintf (buildString, "%sX", buildString);
					if (plot->autoZoom.y)
						sprintf (buildString, "%sY", buildString);
					if (plot->autoZoom.z)
						sprintf (buildString, "%sZ", buildString);
			
					if (strlen(buildString) > 11)
						sprintf (buildString, "%s axes", buildString);
					else
						sprintf (buildString, "%s axis", buildString);

					PostMessage (buildString, pContextInfo);
				}
				else
					PostMessage ("Auto Zoom Off", pContextInfo);
			}
			break;
	
	}
}

//////////////////////////////////////////////////////////////////////

void GraphChannelUpDown (int channelDirection, pRecContext pContextInfo)
{
	pRecPlot plot = pContextInfo->plotArray[pContextInfo->plotIndex];

	if (channelDirection == kGraphChannelDown  && !pContextInfo->lockPlots)
	{
		if (pContextInfo->axes.x)
		{
			if (plot->channel.x <= -2)
				plot->channel.x = kChannelCount - 1;
			else
				plot->channel.x--;
		}
						
		if (pContextInfo->axes.y)
		{
			if (plot->channel.y <= 0)
				plot->channel.y = kChannelCount - 1;
			else
				plot->channel.y--;
		}
	
		if (pContextInfo->axes.z)
		{
			if (plot->channel.z <= 0)
				plot->channel.z = kChannelCount - 1;
			else
				plot->channel.z--;
		}
	}
						
	if (channelDirection == kGraphChannelUp && !pContextInfo->lockPlots)
	{
		if (pContextInfo->axes.x)
		{
			if (plot->channel.x >= kChannelCount - 1)
				plot->channel.x = -2;
			else
				plot->channel.x++;
		}
						
		if (pContextInfo->axes.y)
		{
			if (plot->channel.y >= kChannelCount - 1)
				plot->channel.y = 0;
			else
				plot->channel.y++;
		}
						
		if (pContextInfo->axes.z)
		{
			if (plot->channel.z >= kChannelCount - 1)
				plot->channel.z = 0;
			else
				plot->channel.z++;
		}
	}
	
	//if in selectAll change all the x channels together
	if (pContextInfo->selectAll && pContextInfo->axes.x)
		plot->channel.x = pContextInfo->plotArray[1]->channel.x;

	//update the color if changing the y axis
	if (pContextInfo->axes.y)
	{
		plot->colorIndex = plot->channel.y;
		SetIndexColor (&plot->color, &plot->colorIndex);		
	}
	
	//set the oscilloscope scaling when changing to oscillscope mode
	if (plot->channel.x == kChannelOscilloscope && plot->vertexCount)
	{
		if (plot->vertexCount)
			plot->scale.x = pContextInfo->gridCount / (GLfloat)plot->vertexCount;
		if (plot->scale.x)
			plot->translate.x = pContextInfo->gridCount / (plot->scale.x * -2.0);
	}
}

//changes the current selection
void GraphNext (pRecContext pContextInfo)
{
	if (pContextInfo->lockPlots)
		pContextInfo->selectAll = TRUE;
	else if (pContextInfo->selectAll)
	{
		pContextInfo->selectAll = FALSE;
		if (pContextInfo->data->control.shiftModifier)
			pContextInfo->plotIndex = pContextInfo->plotCount - 1;
		else
			pContextInfo->plotIndex = 1;
	}
	else if (pContextInfo->plotIndex == pContextInfo->plotCount - 1)
	{
		if (pContextInfo->data->control.shiftModifier)
			pContextInfo->plotIndex = pContextInfo->plotIndex--;
		else
			pContextInfo->selectAll = TRUE;
	}
	else if (pContextInfo->data->control.shiftModifier)
	{
		if (pContextInfo->plotIndex == 1)
			pContextInfo->selectAll = TRUE;
		else	
			pContextInfo->plotIndex--;
	}
	else
		pContextInfo->plotIndex++;
	
			
	if (pContextInfo->selectAll)
		pContextInfo->plotIndex = pContextInfo->plotCount - 1;
		
	//error correction to avoid really bad pointer problems, should report error, debug
	if (pContextInfo->plotArray[pContextInfo->plotIndex] == NULL ||
		 pContextInfo->plotIndex < 0 ||
		 pContextInfo->plotIndex >= pContextInfo->plotCount)
		 pContextInfo->plotIndex = 0;
}

//selects which axes to operate on, used by rotate, scale and channel selection
void GraphSelectAxes (pRecContext pContextInfo)
{
	if (pContextInfo->lockPlots)
		return;
	
	//used for disabling the Z axis while toggling
	if (pContextInfo->data->control.altModifier)
	{
		if (pContextInfo->enableZ)
		{
			pContextInfo->enableZ = FALSE;
			pContextInfo->axes.z = FALSE;
		}
		else
			pContextInfo->enableZ = TRUE;
			
		return;
	}
		
	if (pContextInfo->axes.x && pContextInfo->axes.y && pContextInfo->axes.z)
	{
		if (pContextInfo->data->control.shiftModifier)
		{
			pContextInfo->axes.x = FALSE;
			pContextInfo->axes.y = FALSE;
			pContextInfo->axes.z = TRUE;
		}
		else
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = FALSE;
			pContextInfo->axes.z = FALSE;
		}
	}
	else if (pContextInfo->axes.x && pContextInfo->axes.y)
	{
		if (pContextInfo->data->control.shiftModifier)
		{
			pContextInfo->axes.x = FALSE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = FALSE;
		}
		else
		{
			if (pContextInfo->enableZ)
			{
				pContextInfo->axes.x = FALSE;
				pContextInfo->axes.y = FALSE;
				pContextInfo->axes.z = TRUE;
			}
			else
			{
				pContextInfo->axes.x = TRUE;
				pContextInfo->axes.y = FALSE;
				pContextInfo->axes.z = FALSE;
			}
		}
	}
	else if (pContextInfo->axes.x)
	{
		if (pContextInfo->data->control.shiftModifier)
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = TRUE;
		}
		else
		{
			pContextInfo->axes.x = FALSE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = FALSE;
		}
	}
	else if (pContextInfo->axes.y)
	{
		if (pContextInfo->data->control.shiftModifier)
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = FALSE;
			pContextInfo->axes.z = FALSE;
		}
		else
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = FALSE;
		}
	}
	else
	{	
		if (pContextInfo->data->control.shiftModifier)
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = FALSE;
		}
		else
		{
			pContextInfo->axes.x = TRUE;
			pContextInfo->axes.y = TRUE;
			pContextInfo->axes.z = TRUE;
		}
	}
}

void PlotCommand(int command, void* userData)
{
	int i;
	pRecContext pContextInfo = (pRecContext) userData;
	
	if (pContextInfo == NULL) 
		return;
			
	pRecPlot plot = pContextInfo->plotArray[pContextInfo->plotIndex];
	if (plot == NULL) 
		return;

	//if all selected, don't modify video and the hidden ones, unless unhiding
	if (pContextInfo->selectAll && 
		 ((plot->hide && command != kGraphHide) || plot->drawVideo) )
		return;
		
	switch (command) 
	{
		case kGraphSampleInterval:
			if (pContextInfo->data->control.altModifier)
			{
				if (plot->averageType >= kRootMeanSquare)
					plot->averageType = kAverage;
				else
					plot->averageType = kRootMeanSquare;
			}
			else
			{
				if (pContextInfo->data->control.shiftModifier)
					plot->sampleInterval /= 2;
				else
					plot->sampleInterval *= 2;
				if (plot->sampleInterval < 1)
					plot->sampleInterval = kMaximumSampleInterval;
			}
			if (plot->sampleInterval > kMaximumSampleInterval)
				plot->sampleInterval = 1;
			break;
	
		case kGraphColorUp:
			if (pContextInfo->data->control.altModifier)
				plot->colorFade = 1 - plot->colorFade;
			else
			{
				plot->colorIndex++;
				SetIndexColor (&plot->color, &plot->colorIndex);
			}
			break;
		case kGraphColorDown:
			if (pContextInfo->data->control.altModifier)
				plot->altColorUpdate = 1 - plot->altColorUpdate;
			else
			{
				plot->colorIndex--;
				SetIndexColor (&plot->color, &plot->colorIndex);
			}
			break;			
			
		case kGraphHide:						//toggle hiding the plot
			if(pContextInfo->selectAll)
				plot->hide = 0;
			else 
				plot->hide = 1 - plot->hide;
			break;
		
		case kGraphFreeze:						//toggle freezing the plot
			plot->freeze = 1 - plot->freeze;
			break;
			
		case kGraphClear:						//toggle freezing the plot
			pContextInfo->clearFrameBufferOnce = TRUE;
			for (i = 0; i < plot->vertexCount; i++)
				plot->colorArray[i*4+3] = 0;			//erases by making transparent
			break;
				
		case kGraphCenter:		   //center on current data point toggle
			if (!pContextInfo->lockPlots)
			{
				if (pContextInfo->data->control.altModifier)
				{
					plot->center = TRUE;
					plot->centerPerGridSpace = 1 - plot->centerPerGridSpace;
				}
				else
					plot->center = 1 - plot->center;
			}
			break;
						
		case kGraphScroll:		   //scroll on current data point toggle
			if (!pContextInfo->lockPlots)
				plot->scroll = 1 - plot->scroll;
			break;	
		
		case kGraphLines:							//draw lines toggle
			plot->lineWidth += 1.0;
			if (plot->lineWidth > 2.0)			//grid has more possible values
				plot->lineWidth = 0.0;
			if (pContextInfo->plotIndex && plot->lineWidth > 1.0)	//plot has on/off
				plot->lineWidth = 0.0;
			break;
						
		case kGraphPoints:						//point size selection
			if (plot->pointSize == 0.0)
				plot->pointSize = 1.0;
			else if (plot->pointSize == 1.0)
				plot->pointSize = 3.0;
			else if (pContextInfo->plotIndex > 0)
				plot->pointSize = 1.0;			//always draw points for plots
			else
				plot->pointSize = 0.0;			//turns off grid points
			break;
		
		case kGraphDrawPullLimits:
				plot->drawPullLimits = 1 - plot->drawPullLimits;
			break;
			
		case kGraphXincrease:					//move along X axis
			if (!pContextInfo->lockPlots)
				plot->translateRate.x = 1.0;
			break;
		case kGraphXincreaseOff:
			if (plot->translateRate.x > 0.0)
				plot->translateRate.x = 0.0;
			break;
		case kGraphXdecrease:
			if (!pContextInfo->lockPlots)
				plot->translateRate.x = -1.0;
			break;
		case kGraphXdecreaseOff:
			if (plot->translateRate.x < 0.0)
				plot->translateRate.x = 0.0;
			break;
		case kGraphYincrease:					//move along Y axis
			if (!pContextInfo->lockPlots)
				plot->translateRate.y = 1.0;
			break;
		case kGraphYincreaseOff:
			if (plot->translateRate.y > 0.0)
				plot->translateRate.y = 0.0;
			break;
		case kGraphYdecrease:
			if (!pContextInfo->lockPlots)
				plot->translateRate.y = -1.0;
			break;
		case kGraphYdecreaseOff:
			if (plot->translateRate.y < 0.0)
				plot->translateRate.y = 0.0;
			break;
		case kGraphZincrease:					//move along Z axis
			if (!pContextInfo->lockPlots)
				plot->translateRate.z = 1.0;
			break;
		case kGraphZincreaseOff:
			if (plot->translateRate.z > 0.0)
				plot->translateRate.z = 0.0;
			break;
		case kGraphZdecrease:
			if (!pContextInfo->lockPlots)
				plot->translateRate.z = -1.0;
			break;
		case kGraphZdecreaseOff:
			if (plot->translateRate.z < 0.0)
				plot->translateRate.z = 0.0;
			break;
					
		case kGraphRotateIncreaseOn:
			if (pContextInfo->data->control.shiftModifier)
				plot->rotateRate.angle = 1.0;
			else
				plot->rotateRate.angle = 0.2;
			break;

		case kGraphRotateIncreaseOff:
			if (plot->rotateRate.angle > 0.0)
				plot->rotateRate.angle = 0.0;
			break;
					
		case kGraphRotateDecreaseOn:
			if (pContextInfo->data->control.shiftModifier)
				plot->rotateRate.angle = -1.0;
			else
				plot->rotateRate.angle = -0.2;
			break;
			
		case kGraphRotateDecreaseOff:
			if (plot->rotateRate.angle < 0.0)
				plot->rotateRate.angle = 0.0;
			break;
		
		case kGraphZoomOn:					//zoom scales the selected axes
			if (pContextInfo->data->control.altModifier)
			{
				//true if more then one axis selected
				if ( (pContextInfo->axes.x + pContextInfo->axes.y + 
						pContextInfo->axes.z) > 1 )
				{
					if (plot->autoZoom.x == TRUE && plot->autoZoom.y == TRUE)
					{
						plot->autoZoom.x = FALSE;
						plot->autoZoom.y = FALSE;
						plot->autoZoom.z = FALSE;
					}
					else
					{
						if (pContextInfo->axes.x)
							plot->autoZoom.x = TRUE;
						if (pContextInfo->axes.y)
							plot->autoZoom.y = TRUE;
						if (pContextInfo->axes.z)
							plot->autoZoom.z = TRUE;
					}
				}
				else
				{
					if (pContextInfo->axes.x)
						plot->autoZoom.x = 1 - plot->autoZoom.x;
					if (pContextInfo->axes.y)
						plot->autoZoom.y = 1 - plot->autoZoom.y;
					if (pContextInfo->axes.z)
						plot->autoZoom.z = 1 - plot->autoZoom.z;
				}
			}
			else
			{
				if (pContextInfo->axes.x)
					plot->scaleRate.x = 1.0;
				if (pContextInfo->axes.y)
					plot->scaleRate.y = 1.0;
				if (pContextInfo->axes.z)
					plot->scaleRate.z = 1.0;
			}
			break;
		case kGraphZoomOff:
			plot->scaleRate.x = 0.0;
			plot->scaleRate.y = 0.0;
			plot->scaleRate.z = 0.0;
			break;
		
		case kGraphAlphaUp:
			if (plot->color.a > 254)
				plot->color.a = 255;
			else
				plot->color.a++;
			break;
		case kGraphAlphaDown:
			if (plot->color.a < 1)
				plot->color.a = 0;
			else
				plot->color.a--;
			break;
		case kGraphBiasUp:
			plot->bias += 1.0 / 256.0;
			if (plot->bias > 0.0)
				plot->bias = 0.0;
			break;
		case kGraphBiasDown:
			plot->bias -= 1.0 / 256.0;
			if (plot->bias < -1.0)
				plot->bias = -1.0;
			break;
		case kGraphGainUp:
			plot->gain += 1.0 / 256.0;
			if (plot->gain > 1.0)
				plot->gain = 1.0;
			break;
		case kGraphGainDown:
			plot->gain -= 1.0 / 256.0;
			if (plot->gain < 0.0)
				plot->gain = 0.0;
			break;
		
		case kGraphChannelDown:					//previous channel
			GraphChannelUpDown (kGraphChannelDown, pContextInfo);
			break;
		case kGraphChannelUp:					//next channel
			GraphChannelUpDown (kGraphChannelUp, pContextInfo);
			break;

			}
}

void GraphCommand(int command, void* userData)
{
	int i, temp;

	pRecContext pContextInfo = (pRecContext) userData;
	if (pContextInfo == NULL)
		return;
			
	pRecPlot plot = pContextInfo->plotArray[pContextInfo->plotIndex];		
			
	switch (command)
	{
		case kGraphMenu:						//toggle menu info display
			pContextInfo->drawMenu = 1 - pContextInfo->drawMenu;
			break;
			
		case kGraphDrawIndicators:						//toggle menu info display
			pContextInfo->drawIndicators = 1 - pContextInfo->drawIndicators;
			break;

		case kGraphNew:									//add a new plot
			NewPlot(pContextInfo);
			break;
							
		case kGraphDelete:								//delete current plot
			DeletePlot(pContextInfo);
			break;
						
		case kGraphNext:									//next plot
			GraphNext (pContextInfo);
			break;
				
		case kGraphGrid:
			pContextInfo->plotIndex = 0;
			pContextInfo->selectAll = FALSE;
			pContextInfo->plotArray[0]->hide = FALSE;
			break;
		
		case kGraphSelectAxes:
			GraphSelectAxes(pContextInfo);				//select which axes to control
			break;
			
		case kGraphClearFrameBuffer:
			pContextInfo->clearFrameBuffer = 1 - pContextInfo->clearFrameBuffer;
			pContextInfo->clearFrameBufferOnce = TRUE;
			break;
	
		default:
			if (pContextInfo->selectAll)
			{
				temp = pContextInfo->plotIndex;
				for ( pContextInfo->plotIndex = 1;
						pContextInfo->plotIndex < pContextInfo->plotCount;
						pContextInfo->plotIndex++ )
					PlotCommand(command, pContextInfo);
				pContextInfo->plotIndex = temp;
			}
			else
				PlotCommand(command, pContextInfo);
			break;
	}

	PostCommand(command, pContextInfo);
}


#pragma mark ---- Carbon Event Handling ----

pRecContext GetCurrentContextInfo (WindowRef window)
{
	if (NULL == window)  // HID use this path
		window = GetUserFocusWindow();				// FrontWindow ();		//changed from FrontWindow(), maybe ActiveNonFloatingWindow, etc... debug
	if (window)
		return (pRecContext) GetWRefCon (window);
	else
		return NULL;
}

