/****************************************************************************

  main.cpp

****************************************************************************/

#include <string.h>
#include "glut.h"
#include <time.h>
#include "glui.h"


#define MAX_N_PROJS 64
#define MAX_N_CLASSES 64


unsigned char randomshow[100000];
unsigned char currenttrainmask[100000];
unsigned char currenttestmask[100000];
unsigned char currentcuemask[100000];
unsigned char *currentmask;

signed char obj_clusters[500000];

#include "pca.h"
#include "clustering.h"

int testtop;
int testcompare;
int show_test;
int show_train;
int test_translucent;
int train_translucent;

PCA P1;
int npcarows;
int npcacols;
int ncols;
int use_pca;
int pcaflags[MAX_N_PROJS];

Clustering C1;

float zoomaspect = 1.0; // width/height

int conditional = 0;
int cond_projection = 0;
int cond_region = 0;

int hide_category;
int linethickness;
float faderegions;

int zoomparmode;
int redblueflag;

int zoomaxis;
float parzoomstart, parzoomend, zoomoff;
int zoommode;

int showlabels;

int grow_region_flag;

#include "rules.h"
#include "hierarchy.h"
#include "radviz.h"



Rules Rooles;
RadViz R;
Group *gptr;


int region = 0;
int setreg_mode = 0;
#include "projections.h"

int Projection::currentlegend = 8;
int Projection::cue = -1;
int cue_onoff = 1;
int Projection::region_growing[4096][2];
int Projection::begin_grow = 0;
int Projection::end_grow = 0;

int currentProj = 0;

#define MAX_N_CHILDREN 16
GLuint projTexName[MAX_N_PROJS];
int numProjs = 0;
Axis *projAxes[MAX_N_PROJS];
float expans[MAX_N_PROJS];
Projection Parray[MAX_N_PROJS]; // the regions
unsigned char projImages[MAX_N_PROJS][512][512][4]; // images of training data
int projConditions[MAX_N_PROJS][2];
float decisionTreeCoords[MAX_N_PROJS][2]; // x,y
int mainChainFlags[MAX_N_PROJS];
int children[MAX_N_PROJS][MAX_N_CHILDREN];
int numChildren[MAX_N_PROJS];
int projCoords[MAX_N_PROJS]; // 0:Star, 1:Parallel
#define MAX_CATEGORIES 256
int projParallel[MAX_N_PROJS][MAXDIMS][MAX_CATEGORIES]; // 0:Blue, 1:Red


#include "correlation.h"
Correlation COR;

#include "shuttle.h"
Shuttle S;

#include "parcoords.h"
ParCoords P;

int side_window;
int zoom_window;
int projs_window;
int legend_window;
int parcoords_window;

int last_x, last_y;

// graphics canvas dimensions
int sidewidth, sideheight;
int zoomwidth, zoomheight;
int projswidth, projsheight;
int legendwidth, legendheight;
int parwidth, parheight;
int sidex, sidey, controlx, controly, zoomx, zoomy, projsx, projsy, legendx, legendy;
int parx, pary;

#define TEX_WIDTH 512
#define TEX_HEIGHT 512

unsigned char imageBufferRGBA_ptr[2000000];
GLuint texName;
GLubyte TextureSlice[TEX_WIDTH][TEX_HEIGHT][4];


void sideGlutDisplay( void );
void zoomGlutDisplay( void );
void projsGlutDisplay( void );
void legendGlutDisplay( void );


double d_modelView_matrix[16];
double d_projection_matrix[16];
int d_viewport[4];


enum mouse_action { MOUSE_ROTATE , MOUSE_ZOOM , MOUSE_MOVE_LIGHT , MOUSE_MOVE_MARKER , 
					MOUSE_ROTATE_TIME_LOG , MOUSE_ZOOM_TIME_LOG , MOUSE_SELECT_WELL };

mouse_action action;


enum buttonstates { LEFTDOWN, MIDDLEDOWN, NODOWN , LEFTZOOMDOWN };
int button_state = NODOWN;

int playing;

extern int currentfile;
extern int numfiles;


GLUI *glui;
GLUI_Panel *mode_panel;
GLUI_Spinner *highclass_spinner;
GLUI_Spinner *specialruleclass_spinner;
GLUI_Spinner *corclass_spinner;
GLUI_Checkbox *mode_box1, *mode_box2, *mode_box3;
GLUI_Checkbox *coords_box1, *coords_box2, *coords_box3;
GLUI_Checkbox *show_train_box, *show_axes_box;
int coords_mode = 0;
int dummy_mode;
int mode = 0;
GLUI_Panel *setregion_panel;
GLUI_Spinner *region_spinner, *regproj_spinner, *brushsize_spinner;
int regproj = -1;
GLUI_Checkbox *setreg_box1, *setreg_box2;
int brushsize = 6;
float normbrushsize;
GLUI_Spinner *expansion_spinner;
float expansion = 5.0;
GLUI_Spinner *zoomshrink_spinner;
float zoomshrink = 0.02;
GLUI_Spinner *axis_spinner;
int selected_axis = 0;
GLUI_Spinner *frequency_spinner;
float frequency = 0.6;
int showcues = 0;
GLUI_Panel *setprojection_panel;
int projection = 0;
GLUI_Spinner *projection_spinner, *cond_projection_spinner, *cond_region_spinner;
GLUI_Checkbox *showcues_box;
GLUI_Panel *io_panel;
GLUI_Spinner *linethickness_spinner, *faderegions_spinner;
GLUI_Spinner *pcreg_spinner, *pcproj_spinner;
int pc_region, pc_projection;
GLUI_Checkbox *grow_box;
GLUI_Checkbox *use_pca_box;
GLUI *correlation_glui;
GLUI_Panel *correlation_panel1;
GLUI_Spinner *small_spinner;
GLUI *interdim_glui;
GLUI_Panel *interdim_panel1;

float delay;
clock_t last_time;

int correlation_class;
	  
#define TWO_PI 6.28304

float from_x, from_y, to_x, to_y;
int prev_x, prev_y;
int zoom_prev_x, zoom_prev_y;


void tgaOut(char * fileName);
void coords_box_cb(int id);
void DisplayHelper(int flag, int p);
void SetDisplayMask(int p);
void normal_display();
void normal_motion(int x, int y);
void normal_mouse(int button, int state, int x, int y );

void output_cb(int id) { tgaOut("image.tga"); }


void decisionTreeSides(int p) {

	int i;
	float csize;
	int c;

	// calculate the size of the projection for each child
	if (numChildren[p]>1) {
		csize = Parray[p].size/(float)numChildren[p];
	} else {
		csize = Parray[p].size * 0.5;
	}
	if (projConditions[p][0]==currentProj) {
		if (csize > Parray[currentProj].size * 0.1) {
			csize = Parray[currentProj].size * 0.1;
		}
	}

	// set the dimensions of each child
	for (i=0;i<numChildren[p];i++) {
		c = children[p][i];
		if (mainChainFlags[c]==0) {
			Parray[c].x = Parray[p].x + (float)i * csize;
			Parray[c].y = Parray[p].y - csize;
			Parray[c].size = 0.9 * csize;
		}
		// recursively call function on each child
		decisionTreeSides(children[p][i]);
	}

}

void setCurrentProjection(int p) {

	float m;
	float off;
	int i;

	for (i=0;i<numProjs;i++) {
		mainChainFlags[i] = 0;
	}

	currentProj = p;

	Parray[currentProj].x = 450.0;
	Parray[currentProj].y = sideheight - 10.0 - 512.0;
	Parray[currentProj].size = 512.0;
	
	
	mainChainFlags[p] = 1;
	m = 2.0;
	off = 260.0;
	// set the coordinates of the main chain
	while (projConditions[p][0]>=0) {
		p = projConditions[p][0];
		mainChainFlags[p] = 1;
		Parray[p].x = 450.0 - off;
		Parray[p].size = 500.0/m;
		Parray[p].y = sideheight - 10.0 - Parray[p].size;
		m = m * 3.0;
		off = off + 520.0/m;
	}

	// depth first search to set the coordinates of the periphery
	decisionTreeSides(0);


	nSGA = nRSGA;
	SGA = RSGA;
	currentmask = currenttrainmask;
	SetDisplayMask(currentProj);
	nSGA = nTSGA;
	SGA = TSGA;
	currentmask = currenttestmask;
	SetDisplayMask(currentProj);

	coords_box_cb(projCoords[currentProj]);


}

void printnames_cb(int id) {
	R.OutputAxesNames();
}

void correlate_cb(int id) {

	int i, ind1, ind2;
	int cond_projection, cond_region;
	int px, py;
	float x, y;
	int r;
	int p = currentProj;

	COR.Clear();
	cond_projection = projConditions[p][0];
	if (cond_projection >= 0) {
		// find the region by half classification
		for (i=0;i<nSGA;i++) {
			if (obj_clusters[i]<(signed char)0) {
				ind1 = i/8;
				ind2 = i%8;
				if (((currentmask[ind1] >> ind2) & 1) == 1) {
					// projection it and check region				
					R.Project(&(SGA[i]),projAxes[p],expans[p],&x,&y,p,NULL); // project using radviz
					// compare radviz projection to projected regions
					// returned x,y value are on a -1.2 to 1.2 square
					// convert that to a 0 to 512 square					
					px = (int)(((x+1.2)/2.4)*512.0);
					py = (int)(((y+1.2)/2.4)*512.0);
					if ((px>=0)&&(px<512)&&(py>=0)&&(py<512)) {
						r = (int)(Parray[p].mat[px][py]); // region
					} else {
						r = -1;
					}
					if (r==region) {
						if (SGA[i].category==correlation_class) {
							COR.Insert(&(SGA[i]),i,x,y);
							printf("correlation class found %d\n", i);
						}
					}
				}
			}
		}
	} else {
		for (i=0;i<nSGA;i++) {
			if (obj_clusters[i]<(signed char)0) {
				// projection it and check region
				R.Project(&(SGA[i]),projAxes[p],expans[p],&x,&y,p,NULL); // project using radviz
				// compare radviz projection to projected regions
				// returned x,y value are on a -1.2 to 1.2 square
				// convert that to a 0 to 512 square					
				px = (int)(((x+1.2)/2.4)*512.0);
				py = (int)(((y+1.2)/2.4)*512.0);
				if ((px>=0)&&(px<512)&&(py>=0)&&(py<512)) {
					r = (int)(Parray[p].mat[px][py]); // region
				} else {
					r = -1;
				}
				if (r==region) {
					if (SGA[i].category==correlation_class) {
						COR.Insert(&(SGA[i]),i,x,y);
						printf("correlation class found %d\n", i);
					}
				}
			}
		}
	}
	printf("num %d aux %d\n", COR.ng, COR.naux);
	COR.Sort();
	COR.Correlate();
	COR.PreRemove();
	COR.correlation_mode = 1;
	correlation_glui->show();
	glutSetWindow(side_window);
	glutPostRedisplay();
}

void printcor_cb(int id) {
	FILE *fptr;
	fptr = fopen("correlations.txt","wt");
	COR.Print(fptr);
	fclose(fptr);
}

void savecor_cb(int id) {
	FILE *fptr;
	fptr = fopen("../saved/correlations.txt","wt");
	COR.SaveAll(fptr);
	fclose(fptr);
}

void readcor_cb(int id) {
	FILE *fptr;
	fptr = fopen("../saved/correlations.txt","rt");
	COR.ReadAll(fptr, RSGA, nRSGA);
	fclose(fptr);
}

void correlation_remove_cb(int id) {
	COR.Remove();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void correlation_test_cb(int id) {
	int i, r;
	int truepositive = 0;
	int falsepositive = 0;
	
	COR.Finalize();
	COR.SelfTest();
	
	for (i=0;i<nSGA;i++) {
		r = COR.Test(&(SGA[i]));
		if (r==1) {
			if (SGA[i].category==correlation_class) {
				truepositive++;
			} else {
				falsepositive++;
			}
		}
	}
	printf("true positive %d, false positive %d\n", truepositive, falsepositive);
	
}

void correlation_commit_cb(int id) {
	COR.Finalize();
	COR.Commit(correlation_class);
	COR.correlation_mode=0;
	COR.TestSaved(RSGA, nRSGA);
	correlation_glui->hide();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void correlation_cancel_cb(int id) {
	COR.correlation_mode=0;
	correlation_glui->hide();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void correlation_margin_cb(int id) {
	COR.Correlate();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void interdim_cb(int id) {
	COR.interdim_mode = 1;
	correlation_glui->hide();
	interdim_glui->show();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void interdim_commit_cb(int id) {
	COR.SaveInterdim();
	COR.interdim_mode=0;
	interdim_glui->hide();
	correlation_glui->show();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}

void interdim_cancel_cb(int id) {
	COR.interdim_mode=0;
	interdim_glui->hide();
	correlation_glui->show();
	glutSetWindow(side_window);  
	glutPostRedisplay();
}



void pca_cb(int id) {

	int i;
	if (use_pca==1) {
		// run pca on current projection
		pcaflags[currentProj] = 1;
		// create the file ../temp/data.txt
		DisplayHelper(4,currentProj);
		// store means, stddevs and symmat into temp files
		P1.RunPCA("../temp/data.txt",npcarows,R.numaxes,npcacols);
		// read means, stddevs and symmat into memory
		P1.ReadProj(currentProj);
		// re-initialize Radviz axes to make invalid axes far far away
		R.InitAxes(ncols);
		for (i=npcacols;i<R.numaxes;i++) R.axes[i].x = R.axes[i].y = 2000.0;
	} else {
		pcaflags[currentProj] = 0;
	}


}

void special_rule_cb(int id) {
	Rooles.AddSpecialRule(currentProj,region);
}

void grow_region_cb(int id) {

	if (grow_region_flag==1) {
		grow_region_flag = 0;
		grow_box->set_int_val(0);
	} else {
		grow_region_flag = 1;
		grow_box->set_int_val(1);
	}


}

void proj_cb(int id) {
	
	int i,j;

	conditional = 1;

	if ((coords_mode==0)||(coords_mode==2)) { // if old projection is in star or cluster coordinates mode
		projConditions[numProjs][0] = cond_projection = currentProj;
		projConditions[numProjs][1] = cond_region = region;
		decisionTreeCoords[numProjs][0] = 0.5;
		decisionTreeCoords[numProjs][1] = 0.5;
	} else if (coords_mode==1) { // if old projection is in parallel coordinates mode
		projConditions[numProjs][0] = cond_projection = currentProj;
		projConditions[numProjs][1] = cond_region = redblueflag;
		if (redblueflag==0) {
			decisionTreeCoords[numProjs][0] = 0.75;
			decisionTreeCoords[numProjs][1] = 0.05;
		} else {
			decisionTreeCoords[numProjs][0] = 0.25;
			decisionTreeCoords[numProjs][1] = 0.05;
		}
	}
	children[currentProj][numChildren[currentProj]] = numProjs;
	numChildren[currentProj]++;
	mode = 2;	
	setCurrentProjection(numProjs);
	numProjs++;
	Projection::currentlegend = 9;
	expans[currentProj] = R.expansion;
	R.InitAxes(ncols); // TEOH SET FOR EVERY DATA SET
	if (projAxes[currentProj]!=NULL) delete projAxes[currentProj];
	projAxes[currentProj] = R.axes;
	for (i=0;i<MAXDIMS;i++) {
		for (j=0;j<MAX_CATEGORIES;j++) {
			projParallel[currentProj][i][j] = 0; // set all to blue
		}
	}

	glutSetWindow(side_window);  
	glutPostRedisplay();	

}

void coords_box_cb(int id) {
	if (id==0) {
		coords_box1->set_int_val(1);
		coords_box2->set_int_val(0);
	} else if (id==1) {
		coords_box1->set_int_val(0);
		coords_box2->set_int_val(1);
	}
	coords_mode = id;
	projCoords[currentProj] = id;
	
}

void mode_cb(int id) {
	if (id==0) {
		mode_box1->set_int_val(1);
		mode_box2->set_int_val(0);
		mode_box3->set_int_val(0);
		glutSetWindow(side_window);  
		glutPostRedisplay();
	} else if (id==1) {
		mode_box1->set_int_val(0);
		mode_box2->set_int_val(1);
		mode_box3->set_int_val(0);
		glutSetWindow(side_window);  
		glutPostRedisplay();	
	} else if (id==2) {
		mode_box1->set_int_val(0);
		mode_box2->set_int_val(0);
		mode_box3->set_int_val(1);
		glutSetWindow(side_window);  
		glutPostRedisplay();	
	}
	mode = id;

}

void expansion_cb(int id) {

	expans[currentProj] = R.expansion;
    glutSetWindow(side_window);  
	glutPostRedisplay();

}

void setreg_cb(int id) {
	if (id==0) {
		setreg_box1->set_int_val(1);
		setreg_box2->set_int_val(0);
	} else if (id==1) {
		setreg_box1->set_int_val(0);
		setreg_box2->set_int_val(1);
	}
	setreg_mode = id;
}

void brushsize_cb(int id) {

	normbrushsize = (float)brushsize/512.0;
    glutSetWindow(side_window);  
	glutPostRedisplay();

}


void boost_cb(int id) {
	
	R.lastaxis = selected_axis;
	if (id==0) {
		R.Boost(1.2);
	} else {
		R.Boost(0.5);
	}
	glutSetWindow(side_window);  
	glutPostRedisplay();
}


void redraw_func(int id) {
    glutSetWindow(side_window);  
	glutPostRedisplay();
}

void redraw_parcoords(int id) {
    glutSetWindow(side_window);  
}

void random_cb(int id) {
	R.RandomAxes();
    glutSetWindow(side_window);  
	glutPostRedisplay();
}

void io_cb(int id) {
	int i,j,k;
	//int s,t;
	FILE *fptr;
	if (id==0) { //load

		readcor_cb(id); // first read the correlations, the saved clusters from painting

		fptr = fopen("../saved/classify.prj","rb");

		fread(S.classes,sizeof(int),MAX_N_CLASSES,fptr);
		fread(S.numclustersinclass,sizeof(int),MAX_N_CLASSES,fptr);
		for (i=0;i<MAX_N_CLASSES;i++) {
			for (j=0;j<S.numclustersinclass[i];j++) {
				S.clusters[i][j] = new float[ncols];
				fread(S.clusters[i][j],sizeof(float),ncols,fptr);
			}
		}

		fread(&numProjs,sizeof(int),1,fptr); // number of projections
		for (i=0;i<numProjs;i++) { // for each projection
			// 0. read expansion
			fread(&(expans[i]),sizeof(float),1,fptr);
			// 1. read axes
			projAxes[i] = new Axis[R.numaxes];
			fread(projAxes[i],sizeof(Axis),R.numaxes,fptr);
			// 2. read projection conditions
			fread(projConditions[i],sizeof(int),2,fptr);
			// 3. read region
			fread(Parray[i].mat,sizeof(signed char),512*512,fptr);
			// 4. read decision tree coordinates
			fread(decisionTreeCoords[i],sizeof(float),2,fptr);
			// 5. read proj, star or parallel or cluster
			fread(&(projCoords[i]),sizeof(int),1,fptr);
			// 6. read parallel coordinates projections
			for (j=0;j<Group::ncatdims;j++) {
				k = S.catrange[j];
				fread(projParallel[i][j],sizeof(int),k,fptr);
			}
			// 7. read cues
			fread(Parray[i].cuefriends,sizeof(unsigned char),512*512,fptr);
			// 8. read PCA flags
			fread(&(pcaflags[i]),sizeof(int),1,fptr);
			fread(P1.allmeans[i],sizeof(float),R.numaxes,fptr);
			fread(P1.allstddevs[i],sizeof(float),R.numaxes,fptr);
			for (j=0;j<R.numaxes;j++) {
				fread(P1.allsymmats[i][j],sizeof(float),R.numaxes,fptr);
			}
			for (j=0;j<S.numclasses;j++) {
				for (k=0;k<S.numclustersinclass[j];k++) {
					// 9. read centroid on/off flags
					fread(&(S.clusterflags[i][j][k]),sizeof(int),1,fptr);
					// 10. read centroid screen coordinates
					fread(S.allcentroidscreencoords[i][j][k],sizeof(float),2,fptr);
				}
			}
		}
		for (i=0;i<numProjs;i++) {
			if (projConditions[i][0]>=0) {
				children[projConditions[i][0]][numChildren[projConditions[i][0]]] = i;
				numChildren[projConditions[i][0]]++;
			}
		}
		setCurrentProjection(0);
		fclose(fptr);
	    glutSetWindow(side_window);  
		glutPostRedisplay();
		printf("Done loading\n");
	} else if (id==1) { // save

		savecor_cb(id); // first save the correlations, the saved clusters from painting

		fptr = fopen("../saved/classify.prj","wb");

		fwrite(S.classes,sizeof(int),MAX_N_CLASSES,fptr);
		fwrite(S.numclustersinclass,sizeof(int),MAX_N_CLASSES,fptr);
		for (i=0;i<MAX_N_CLASSES;i++) {
			for (j=0;j<S.numclustersinclass[i];j++) {
				fwrite(S.clusters[i][j],sizeof(float),ncols,fptr);
			}
		}


		fwrite(&numProjs,sizeof(int),1,fptr); // number of projections
		for (i=0;i<numProjs;i++) { // for each projection
			// 0. output expansion
			fwrite(&(expans[i]),sizeof(float),1,fptr);
			// 1. output axes
			fwrite(projAxes[i],sizeof(Axis),R.numaxes,fptr);
			// 2. save projection conditions
			fwrite(projConditions[i],sizeof(int),2,fptr);
			// 3. output region
			fwrite(Parray[i].mat,sizeof(signed char),512*512,fptr);
			// 4. read decision tree coordinates
			fwrite(decisionTreeCoords[i],sizeof(float),2,fptr);
			// 5. read proj star or parallel
			fwrite(&(projCoords[i]),sizeof(int),1,fptr);
			// 6. read parallel coordinates projections
			for (j=0;j<Group::ncatdims;j++) {
				k = S.catrange[j];
				fwrite(projParallel[i][j],sizeof(int),k,fptr);
			}
			// 7. write cues
			fwrite(Parray[i].cuefriends,sizeof(unsigned char),512*512,fptr);
			// 8. write PCA flags
			fwrite(&(pcaflags[i]),sizeof(int),1,fptr);
			fwrite(P1.allmeans[i],sizeof(float),R.numaxes,fptr);
			fwrite(P1.allstddevs[i],sizeof(float),R.numaxes,fptr);
			for (j=0;j<R.numaxes;j++) {
				fwrite(P1.allsymmats[i][j],sizeof(float),R.numaxes,fptr);
			}
			for (j=0;j<S.numclasses;j++) {
				for (k=0;k<S.numclustersinclass[j];k++) {
					// 9. read centroid on/off flags
					fwrite(&(S.clusterflags[i][j][k]),sizeof(int),1,fptr);
					// 10. read centroid screen coordinates
					fwrite(S.allcentroidscreencoords[i][j][k],sizeof(float),2,fptr);
				}
			}
		}
		fclose(fptr);
	}
}

void classify_cb(int id) {

	S.BuildRules();
	S.readVeryStandardTestAndClassify();

}

/**************************************** myGlutKeyboard() **********/

void myGlutKeyboard(unsigned char Key, int x, int y)
{
  switch(Key)
  {
  case 27: 
  case 'q':
    exit(0);
    break;
  };
  
  glutPostRedisplay();
}


/***************************************** myGlutMenu() ***********/

void myGlutMenu( int value )
{
  myGlutKeyboard( value, 0, 0 );
}


/***************************************** myGlutIdle() ***********/

void myGlutIdle( void )
{
/*
	if (((double)(clock()-last_time)/(double)CLOCKS_PER_SEC)>delay) { 
		S.showall = 1;
		last_time = clock();
		if (button_state==NODOWN) {
		    glutSetWindow(side_window);  
			glutPostRedisplay();
		}
	}
*/

/*	
	if (((double)(clock()-last_time)/(double)CLOCKS_PER_SEC)>delay) { 
		cue_onoff = -cue_onoff;
		last_time = clock();
	    glutSetWindow(side_window);  
		glutPostRedisplay();
	}
*/	
}




void tgaOut(char * fileName) {
	// create the TGA header
	unsigned char header[18];


    glutSetWindow(side_window);  
	glutPostRedisplay();	
	glReadBuffer(GL_FRONT);
	glReadPixels(0,0,(GLsizei)sidewidth,(GLsizei)sideheight,GL_RGBA, GL_UNSIGNED_BYTE, imageBufferRGBA_ptr);
	
	for(int counter=0;counter<18;counter++)
	{
		header[counter]=0;
	}
	header[2]=2;
	header[12]=sidewidth%256;
	header[13]=sidewidth/256;
	header[14]=sideheight%256;
	header[15]=sideheight/256;
	header[16]=24;
	header[17]=24;

	unsigned char RGB[3];
	FILE *file_ptr;
	file_ptr=fopen(fileName,"wb");
	if(file_ptr==NULL)
	{
		printf("unable to write to %s\n",fileName);
		return ;
	}
	fwrite(header,18,1,file_ptr);
	int x,y;
	int actualWidth;
	actualWidth = sidewidth;
	// for each pixel
	for(y=0;y<sideheight;y++) {
		for(x=0;x<sidewidth;x++) {
			RGB[0]=*(imageBufferRGBA_ptr+(x+y*actualWidth)*4+0);
			RGB[1]=*(imageBufferRGBA_ptr+(x+y*actualWidth)*4+1);
			RGB[2]=*(imageBufferRGBA_ptr+(x+y*actualWidth)*4+2);
			// have to swap the R and B as you save
			fwrite(RGB+2,1,1,file_ptr);
			fwrite(RGB+1,1,1,file_ptr);
			fwrite(RGB+0,1,1,file_ptr);
		}
	}
	fclose(file_ptr);
	return;
}

void SetDisplayMask(int p) {


	int i, r, px, py, col;
	float x, y;
	int cp, cr;
	int cuepass;

	cond_projection = projConditions[p][0];
	cond_region = projConditions[p][1];	

	if (cond_projection >= 0) {
		for (i=0;i<nSGA;i++) {
			currentmask[i/8] = 0;
		}
		// find the region by half classification
		for (i=0;i<nSGA;i++) {
			if (projCoords[cond_projection]==0) { // if parent projection is using star coordinates
				R.Project(&(SGA[i]),projAxes[cond_projection],expans[cond_projection],&x,&y,cond_projection,NULL); // project using radviz
				// compare radviz projection to projected regions
				// returned x,y value are on a -1.2 to 1.2 square
				// convert that to a 0 to 512 square					
				px = (int)(((x+1.2)/2.4)*512.0);
				py = (int)(((y+1.2)/2.4)*512.0);
				// index into the regions projection to find the region
				if (cond_region>=0) {
					cuepass = (int)(showcues&&(((Parray[cond_projection].cuefriends[px][py]>>cond_region)&1)==1));
				} else {
					cuepass = (int)showcues;
				}
				if ((px>=0)&&(px<512)&&(py>=0)&&(py<512)) {
					r = (int)(Parray[cond_projection].mat[px][py]); // region
				} else {
					r = -1;
				}				
			} else if (projCoords[cond_projection]==1) { // if parent projection is using parallel coordinates
				// find out if blue or red
				r = findredblue(cond_projection,&(SGA[i]));
			}
			if ((r==cond_region)||cuepass) {
				cp = cond_projection; cr = cond_region;
				while ((projConditions[cp][0]>=0)&&((r==cr)||cuepass)) {
					cuepass = 0;
					cr = projConditions[cp][1]; cp = projConditions[cp][0];
					if (projCoords[cp]==0) {
						R.Project(&(SGA[i]),projAxes[cp],expans[cp],&x,&y,cp,NULL); // project using radviz
						// compare radviz projection to projected regions
						// returned x,y value are on a -1.2 to 1.2 square
						// convert that to a 0 to 512 square
						px = (int)(((x+1.2)/2.4)*512.0);
						py = (int)(((y+1.2)/2.4)*512.0);
						// index into the regions projection to find the region
						r = (int)(Parray[cp].mat[px][py]); // region
					} else if (projCoords[cp]==1) {
						// find out if blue or red
						r = findredblue(cp,&(SGA[i]));
					}
				}
				if (r==cr) {
					currentmask[i/8] |= (1 << (i%8));
				} else if (cuepass > 0) {
					currentcuemask[i/8] |= (1 << (i%8));
				}
			}
		}
	}


}


// flag: 0-star, 1-parallel, 2-zoom-star, 3-zoom-parallel, 4-pca, 5-cluster
void DisplayHelper(int flag, int p) {

	int i, r, px, py, col;
	float x, y;
	int cp, cr;
	int cuepass;
	FILE *fptr;
	int ind1, ind2;


	if (flag==4) {
		npcarows = 0;
		fptr = fopen("../temp/data.txt","wt");
	}

	cond_projection = projConditions[p][0];
	cond_region = projConditions[p][1];	
	if (cond_projection >= 0) {
		// find the region by half classification

		for (i=0;i<nSGA;i++) {
			ind1 = i/8;
			ind2 = i%8;
			if ((obj_clusters[i]<(signed char)0)&&
				((R.alloneclass&&(SGA[i].category==R.highlightclass))||S.showall||(((randomshow[ind1] >> ind2) & 1) == 1))) {
				if ((R.alloneclass&&(SGA[i].category==R.highlightclass))||(((currentmask[ind1] >> ind2) & 1) == 1)) {

					if (flag==0) {
						R.Display(&(SGA[i]),1.0,p,0.1*(float)i/(float)nSGA);
					} else if (flag==1) {
						if (findredblue(p,&(SGA[i])) == redblueflag) { P.Render(&(SGA[i])); P.RenderZoom(zoomaxis, parzoomstart, parzoomend); }
					} else if (flag==2) {	
						if (SGA[i].category!=hide_category) R.DisplayZoom(&(SGA[i]),1.0,projAxes[p],&(expans[p]),p,0.1*(float)i/(float)nSGA);
					} else if (flag==3) {
						if (findredblue(p,&(SGA[i])) == redblueflag) { P.RenderAuxiliary(&(SGA[i]),zoomaxis,parzoomstart,parzoomend); }
					} else if (flag==4) { // TEOH PCA
						npcarows++;
						for (col=0;col<SGA[i].ndims;col++) {
							fprintf(fptr, "%f ", SGA[i].startingcoords[col]);
						}
						fprintf(fptr,"\n");
					}
				} else if (((currentcuemask[ind1] >> ind2) & 1) == 1) {		
					if (flag==0) {
						if (cue_onoff > 0) R.Display(&(SGA[i]),1.0,p,0.1*(float)i/(float)nSGA);
					} else if (flag==1) {
						if ((cue_onoff > 0) && (findredblue(p,&(SGA[i])) == redblueflag)) { P.Render(&(SGA[i])); P.RenderZoom(zoomaxis, parzoomstart, parzoomend); }
					} else if (flag==2) {
						if (SGA[i].category!=hide_category) R.DisplayZoom(&(SGA[i]),1.0,projAxes[p],&(expans[p]),p,0.1*(float)i/(float)nSGA);
					} else if (flag==3) {
						if ((cue_onoff > 0) && (findredblue(p,&(SGA[i])) == redblueflag)) { P.RenderAuxiliary(&(SGA[i]),zoomaxis,parzoomstart,parzoomend); }
					}
				}
			}
		}
	} else {
		if (flag==0) {
			S.Display(p);
		} else if (flag==1) {
			for (i=0;i<nSGA;i++) {
				if (findredblue(p,&(SGA[i])) == redblueflag) P.Render(&(SGA[i]));
			}
			P.RenderZoom(zoomaxis, parzoomstart, parzoomend);
		} else if (flag==2) {
			S.DisplayZoom(projAxes[p],&(expans[p]),p);
		} else if (flag==3) {
			for (i=0;i<nSGA;i++) {
				if (findredblue(p,&(SGA[i])) == redblueflag) { P.RenderAuxiliary(&(SGA[i]),zoomaxis,parzoomstart,parzoomend); }
			}
		} else if (flag==4) { // TEOH PCA
			npcarows++;
			for (i=0;i<nSGA;i++) {
				for (col=0;col<SGA[i].ndims;col++) {
					fprintf(fptr, "%f ", SGA[i].startingcoords[col]);
				}
				fprintf(fptr,"\n");
			}
		}
	}	

	if (flag==0) R.DisplayAxes(p);
	if (flag==2) R.DisplayAxes(p);

	if (flag==4) fclose(fptr);

}


void sideGlutDisplay( void )
{

	printf("begin display\n");

	glClearColor(0.0,0.0,0.0,1.0);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	if (COR.correlation_mode==0) {
		normal_display();
	} else {
		COR.Display(sidewidth,sideheight);
	}

}

void normal_display() {

	int i, r, px, py;
	float x, y;
	int cp, cr;
	int p;
	int pointsize;
	int cuepass;

	for (p=0;p<numProjs;p++) {

		if ((projCoords[p]==0)||(projCoords[p]==2)) { // star or cluster coordinates mode

			// display the regions overlay
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(0.0,512.0,0.0,512.0);
					// left, right, bottom, top
			glViewport(Parray[p].x, Parray[p].y, Parray[p].size, Parray[p].size);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glLoadIdentity();	
			Parray[p].Display(1);
			// fade the regions
			
			glColor4f(0.0,0.0,0.0,faderegions);
			glBegin(GL_QUADS);
			glVertex3f(0.0,0.0,-0.1);
			glVertex3f(0.0,512.0,-0.1);
			glVertex3f(512.0,512.0,-0.1);
			glVertex3f(512.0,0.0,-0.1);
			glEnd();
			
		}

		if (projCoords[p]==0) { // star coordinates mode
			R.axes = projAxes[p];
			R.expansion = expans[p];
			// display the axes and data points		
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(-1.2,1.2,-1.2,1.2);
					// left, right, bottom, top			
			glViewport(Parray[p].x,Parray[p].y,Parray[p].size,Parray[p].size);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glDisable(GL_TEXTURE_2D);
			glDisable(GL_LIGHTING);	
			// display bounding box around the projection
			glColor3f(0.5,0.5,0.5);
			glLineWidth(2);
			glBegin(GL_LINES);
			glVertex2f(-1.2,-1.2); glVertex2f(-1.2,1.2);
			glVertex2f(-1.2,-1.2); glVertex2f(1.2,-1.2);
			glVertex2f(1.2,1.2); glVertex2f(-1.2,1.2);
			glVertex2f(1.2,1.2); glVertex2f(1.2,-1.2);
			glEnd();	
			if (Parray[p].size > 500) {
				
				if (show_test) {
					nSGA = nTSGA;
					SGA = TSGA;
					R.nocolor = 1;
					R.background = 1 - testtop;
					currentmask = currenttestmask;
					if (test_translucent) {
						R.transparent_on = 1;
					} else {
						R.transparent_on = 0;
					}
					DisplayHelper(0,p);
				}
				if (show_train) {
					R.background = 1 - R.background;
					R.nocolor = 0;
					nSGA = nRSGA;
					SGA = RSGA;
					currentmask = currenttrainmask;
					if (testcompare) R.alloneclass = 1;
					if (train_translucent) {
						R.transparent_on = 1;
					} else {
						R.transparent_on = 0;
					}
					DisplayHelper(0,p);
					R.alloneclass = 0;
				}
			}
			if (p == currentProj) {
				// display the zoom rectangle
				if (button_state==MIDDLEDOWN) {
					R.DisplayZoomRectangle();
				}
			}
		} else if (projCoords[p]==1) { // parallel coordinates mode

			glMatrixMode(GL_PROJECTION);		
			glLoadIdentity();
			gluOrtho2D(-0.02,1.02,-0.3,1.0);
					// left, right, bottom, top
			glViewport(Parray[p].x, Parray[p].y, Parray[p].size, Parray[p].size);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();	
			// display bounding box around the projection
			glColor3f(0.0,0.0,0.0);
			glLineWidth(2);
			glBegin(GL_LINES);
			glVertex2f(-0.02,-0.3); glVertex2f(-0.02,1.0);
			glVertex2f(1.02,-0.3); glVertex2f(1.02,1.0);
			glVertex2f(-0.02,-0.3); glVertex2f(1.02,-0.3);
			glVertex2f(-0.02,1.0); glVertex2f(1.02,1.0);
			glEnd();
			// display a red box and a blue box
			glColor3f(1.0,0.0,0.0);
			glBegin(GL_QUADS);
			glVertex3f(0.2,-0.2,0.1);
			glVertex3f(0.2,-0.1,0.1);
			glVertex3f(0.3,-0.1,0.1);
			glVertex3f(0.3,-0.2,0.1);
			glEnd();
			glColor3f(0.0,0.0,1.0);
			glBegin(GL_QUADS);
			glVertex3f(0.7,-0.2,0.1);
			glVertex3f(0.7,-0.1,0.1);
			glVertex3f(0.8,-0.1,0.1);
			glVertex3f(0.8,-0.2,0.1);
			glEnd();
			glColor3f(0.5,0.5,0.5);
			glLineWidth(2);
			glBegin(GL_LINES);
			if (redblueflag==0) { // in blue mode
				glVertex3f(0.69,-0.21,0.1); glVertex3f(0.69,-0.09,0.1); 
				glVertex3f(0.81,-0.21,0.1); glVertex3f(0.81,-0.09,0.1); 
				glVertex3f(0.69,-0.21,0.1); glVertex3f(0.81,-0.21,0.1); 
				glVertex3f(0.69,-0.09,0.1); glVertex3f(0.81,-0.09,0.1); 
			} else { // in red mode
				glVertex3f(0.19,-0.21,0.1); glVertex3f(0.19,-0.09,0.1); 
				glVertex3f(0.31,-0.21,0.1); glVertex3f(0.31,-0.09,0.1); 
				glVertex3f(0.19,-0.21,0.1); glVertex3f(0.31,-0.21,0.1); 
				glVertex3f(0.19,-0.09,0.1); glVertex3f(0.31,-0.09,0.1); 
			}
			glEnd();
			// display parallel coordinates
			if (Parray[p].size > 500) {


				nSGA = nTSGA;
				SGA = TSGA;
				R.nocolor = 1;
				R.background = 1 - testtop;
				currentmask = currenttestmask;
				DisplayHelper(1,p);
				R.background = 1 - R.background;
				R.nocolor = 0;
				nSGA = nRSGA;
				SGA = RSGA;
				currentmask = currenttrainmask;
				DisplayHelper(1,p);


				P.RenderAxes(p);
			}

		} else if (projCoords[p]==2) { // cluster coordinates mode

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(0.0,1.0,0.0,1.0);
					// left, right, bottom, top
			glViewport(Parray[p].x, Parray[p].y, Parray[p].size, Parray[p].size);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);

			if (Parray[p].size > 160) {
				DisplayHelper(5,p);
			}
			
		}

	
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0,sidewidth,0.0,sideheight);
				// left, right, bottom, top			
		glViewport(0.0,0.0,sidewidth,sideheight);
				// startx, starty, xsize, ysize
				// coordinates begin from lower left corner of window	
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);

		// draw the decision tree edge from this projection to its corresponding region in parent node
		if (projConditions[p][0] >= 0) {
			glColor3f(0.5,0.5,0.5);
			glLineWidth(2);
			glBegin(GL_LINES);
			if (mainChainFlags[p]==0) {
				glVertex2f(Parray[p].x+0.5*Parray[p].size,Parray[p].y+Parray[p].size);
			} else {
				glVertex2f(Parray[p].x,Parray[p].y+0.5*Parray[p].size);
			}
			glVertex2f(Parray[projConditions[p][0]].x + decisionTreeCoords[p][0] * Parray[projConditions[p][0]].size,
				Parray[projConditions[p][0]].y + decisionTreeCoords[p][1] * Parray[projConditions[p][0]].size);
			glEnd();
		}
		
		glColor3f(0.5,0.5,0.5);
		glBegin(GL_LINES);
		glLineWidth(1);
		if (mainChainFlags[p]==1) {
			glVertex2f(Parray[p].x, Parray[p].y); glVertex2f(Parray[p].x - 5.0, Parray[p].y - 5.0);
			glVertex2f(Parray[p].x - 5.0, Parray[p].y - 5.0); glVertex2f(Parray[p].x, Parray[p].y - 5.0);
			glVertex2f(Parray[p].x - 5.0, Parray[p].y - 5.0); glVertex2f(Parray[p].x - 5.0, Parray[p].y);
		} else {
			glVertex2f(Parray[p].x + Parray[p].size, Parray[p].y + Parray[p].size); glVertex2f(Parray[p].x + Parray[p].size + 5.0, Parray[p].y + Parray[p].size + 5.0);
			glVertex2f(Parray[p].x + Parray[p].size + 5.0, Parray[p].y + Parray[p].size + 5.0); glVertex2f(Parray[p].x + Parray[p].size, Parray[p].y + Parray[p].size + 5.0);
			glVertex2f(Parray[p].x + Parray[p].size + 5.0, Parray[p].y + Parray[p].size + 5.0); glVertex2f(Parray[p].x + Parray[p].size + 5.0, Parray[p].y + Parray[p].size);
		}
		glEnd();


	}

	cond_projection = projConditions[currentProj][0];
	cond_region = projConditions[currentProj][1];
	R.axes = projAxes[currentProj];
	R.expansion = expans[currentProj];




	// display the legend
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();	
	gluOrtho2D(0.0,24.0,0.0,24.0);
			// left, right, bottom, top
	glViewport(0.0,0.0,legendwidth,legendheight);
			// startx, starty, xsize, ysize
			// coordinates begin from lower left corner of window	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glColor3f(0.9,0.9,1.0);
	glBegin(GL_QUADS);
	glVertex3f(0.0,0.0,-0.1);
	glVertex3f(0.0,2.0,-0.1);
	glVertex3f(10.0,2.0,-0.1);
	glVertex3f(10.0,0.0,-0.1);
	glEnd();
	glBegin(GL_QUADS);
	glVertex3f(10.0,0.0,-0.1);
	glVertex3f(10.0,2.0,-0.1);
	glColor3f(1.0,1.0,1.0);
	glVertex3f(16.0,2.0,-0.1);
	glVertex3f(16.0,0.0,-0.1);
	glEnd();
	glColor3f(0.9,0.9,1.0);
	glBegin(GL_QUADS);
	glVertex3f(0.0,0.0,-0.1);
	glVertex3f(0.0,16.0,-0.1);
	glVertex3f(2.0,16.0,-0.1);
	glVertex3f(2.0,0.0,-0.1);
	glEnd();
	glBegin(GL_QUADS);
	glVertex3f(2.0,16.0,-0.1);
	glVertex3f(0.0,16.0,-0.1);
	glColor3f(1.0,1.0,1.0);
	glVertex3f(0.0,20.0,-0.1);
	glVertex3f(2.0,20.0,-0.1);
	glEnd();
	glColor3f(0.9,0.9,1.0);
	glBegin(GL_QUADS);
	glVertex3f(2.0,16.0,-0.1);
	glVertex3f(2.0,13.0,-0.1);
	glColor3f(1.0,1.0,1.0);
	glVertex3f(6.0,13.0,-0.1);
	glVertex3f(6.0,16.0,-0.1);
	glEnd();
	glColor3f(0.9,0.9,1.0);
	glBegin(GL_TRIANGLES);
	glVertex3f(2.0,16.0,-0.1);
	glColor3f(1.0,1.0,1.0);
	glVertex3f(2.0,20.0,-0.1);
	glVertex3f(6.0,16.0,-0.1);
	glEnd();
	
	Parray[0].DisplayLegend();
	S.DisplayLegend();
	R.DisplayLegend();




	// display zoom window
	
	if ((R.zoomey-R.zoomsy)>0.01) {
		zoomaspect = (R.zoomex-R.zoomsx)/(R.zoomey-R.zoomsy);
	} else {
		zoomaspect = 1.0;
	}


	if ((projCoords[currentProj]==0)||(projCoords[currentProj]==2)) { // star or cluster coordinates mode

		if (zoomparmode==0) { // display zoom window
			// ******************* display regions **********************
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluOrtho2D(512.0*(R.zoomsx+1.2)/2.4,
						512.0*(R.zoomex+1.2)/2.4,
						512.0*(R.zoomsy+1.2)/2.4,
						512.0*(R.zoomey+1.2)/2.4);
					// left, right, bottom, top
			glViewport(zoomx,zoomy,zoomaspect*zoomheight,zoomheight);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glLoadIdentity();	

			glColor3f(0.5,0.5,0.5);
			glLineWidth(2);
			glBegin(GL_LINES);
			glVertex2f(512.0*(R.zoomsx+1.2)/2.4,512.0*(R.zoomsy+1.2)/2.4);
			glVertex2f(512.0*(R.zoomsx+1.2)/2.4,512.0*(R.zoomey+1.2)/2.4);
			glVertex2f(512.0*(R.zoomex+1.2)/2.4,512.0*(R.zoomsy+1.2)/2.4);
			glVertex2f(512.0*(R.zoomex+1.2)/2.4,512.0*(R.zoomey+1.2)/2.4);
			glVertex2f(512.0*(R.zoomsx+1.2)/2.4,512.0*(R.zoomey+1.2)/2.4);
			glVertex2f(512.0*(R.zoomex+1.2)/2.4,512.0*(R.zoomey+1.2)/2.4);
			glVertex2f(512.0*(R.zoomsx+1.2)/2.4,512.0*(R.zoomsy+1.2)/2.4);
			glVertex2f(512.0*(R.zoomex+1.2)/2.4,512.0*(R.zoomsy+1.2)/2.4);
			glEnd();

			pointsize = (int) (((float)(zoomaspect*zoomheight))/(512.0*(R.zoomex-R.zoomsx)/2.4));
			if (pointsize>2) pointsize--;
			Parray[currentProj].Display(pointsize);
			
			// fade the regions			
			glColor4f(0.0,0.0,0.0,faderegions);
			glBegin(GL_QUADS);
			glVertex3f(0.0,0.0,-0.1);
			glVertex3f(0.0,512.0,-0.1);
			glVertex3f(512.0,512.0,-0.1);
			glVertex3f(512.0,0.0,-0.1);
			glEnd();


			// *********** display data points ***************************
			if (projCoords[currentProj]==0) {
				glMatrixMode(GL_PROJECTION);		
				glLoadIdentity();
				gluOrtho2D(R.zoomsx,R.zoomex,R.zoomsy,R.zoomey);
						// left, right, bottom, top
				glViewport(zoomx,zoomy,zoomaspect*zoomheight,zoomheight);
						// startx, starty, xsize, ysize
						// coordinates begin from lower left corner of window	
				glMatrixMode(GL_MODELVIEW);
				glLoadIdentity();

				if (show_test) {				
					nSGA = nTSGA;
					SGA = TSGA;
					R.nocolor = 1;
					R.background = 1 - testtop;
					currentmask = currenttestmask;
					
					if (test_translucent) {
						R.transparent_on = 1;
					} else {
						R.transparent_on = 0;
					}
					DisplayHelper(2,currentProj);
				} 
				
				if (show_train) {
					R.background = 1 - R.background;
					R.nocolor = 0;
					nSGA = nRSGA;
					SGA = RSGA;
					currentmask = currenttrainmask;
					
					if (train_translucent) {
						R.transparent_on = 1;
					} else {
						R.transparent_on = 0;
					}
					DisplayHelper(2,currentProj);
				}
			} else if (projCoords[currentProj]==2) {
				glMatrixMode(GL_PROJECTION);		
				glLoadIdentity();
				gluOrtho2D(R.zoomsx,R.zoomex,R.zoomsy,R.zoomey);
						// left, right, bottom, top
				glViewport(zoomx,zoomy,zoomaspect*zoomheight,zoomheight);
						// startx, starty, xsize, ysize
						// coordinates begin from lower left corner of window	
				glMatrixMode(GL_MODELVIEW);
				glLoadIdentity();
				DisplayHelper(5,currentProj);
			}
		} else if (zoomparmode==1) { // display parallel coordinates
			glMatrixMode(GL_PROJECTION);		
			glLoadIdentity();
			gluOrtho2D(0.0,1.0,-0.02,1.02);
					// left, right, bottom, top
			glViewport(parx,pary,parwidth,parheight);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			
			DisplayHelper(1,currentProj);
			P.RenderAxes(currentProj);
		}
	} else if (projCoords[currentProj]==1) { // if parallel coordinates mode in main decision tree
			glMatrixMode(GL_PROJECTION);		
			glLoadIdentity();
			gluOrtho2D(-0.02,1.22,-0.02,1.02);
					// left, right, bottom, top
			glViewport(parx,pary,parwidth,parheight);
					// startx, starty, xsize, ysize
					// coordinates begin from lower left corner of window	
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			
			DisplayHelper(3,currentProj);
			P.RenderZoomAxes(currentProj, zoomaxis, parzoomstart, parzoomend);
	}


	S.showall = 0;
	printf("end display\n");

	glutSwapBuffers(); 

}



void sideGlutMotion(int x, int y )
{
	if (COR.correlation_mode==0) {
		normal_motion(x,y);
	} else {
		COR.MouseMotion(brushsize,x,sideheight-y);
		glutSetWindow(side_window);  
		glutPostRedisplay();
	}
}

void normal_motion(int x, int y )
{

	int zx, zy;
	int px, rx, py, ry, bsize;
	float ox, oy; 
	int p;
	float pax, pay;
	float temp;

	zx = x;
	zy = y;

	ox = (float)x;
	oy = sideheight - (float)y;

	x = x - (int)Parray[currentProj].x;
	y = y - ((int)sideheight - ((int)Parray[currentProj].y+Parray[currentProj].size));

	from_x = -1.0 + 2.0*(float)x/Parray[currentProj].size;
	from_y = -1.0 + 2.0*(float)(Parray[currentProj].size-y)/Parray[currentProj].size;
	from_x = from_x * 1.2;
	from_y = from_y * 1.2;

	if (button_state==LEFTDOWN) {
		if (mode==0) { // edit axis mode
			if (coords_mode==0) { // star coordinates mode
				R.MoveAxis(from_x, from_y);
				glutSetWindow(side_window);  
				glutPostRedisplay(); 
			} else if (coords_mode==1) {
				from_x = (float)(ox - (int)Parray[currentProj].x)/(float)Parray[currentProj].size;
				from_y = (float)(oy - (int)Parray[currentProj].y)/(float)Parray[currentProj].size;	
			}
		} else if (mode==1) { // set region mode
			if (grow_region_flag == 0) {
				if (currentProj>=0) {
					glutSetWindow(side_window);
					glutSwapBuffers(); 
					glMatrixMode(GL_PROJECTION);
					glLoadIdentity();  
					gluOrtho2D(0.0,512.0,0.0,512.0);
							// left, right, bottom, top
						//glViewport(0.0,0.0,sideheight,sideheight);
					glViewport(Parray[currentProj].x,Parray[currentProj].y,Parray[currentProj].size,Parray[currentProj].size);
							// startx, starty, xsize, ysize
							// coordinates begin from lower left corner of window	
					glMatrixMode(GL_MODELVIEW);
					glLoadIdentity();		
					glEnable(GL_BLEND);
					glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
					Parray[currentProj].MarkAndDisplay(prev_x, 512-prev_y, x, 512-y, brushsize, region);
					glDisable(GL_BLEND);
					glutSwapBuffers(); 
				}
			}
		} else if (mode==2) { // edit decision tree coords mode
			p = projConditions[currentProj][0];
			if (p >= 0) {
				if ((ox > Parray[p].x) && (ox < Parray[p].x + Parray[p].size) &&
					(oy > Parray[p].x) && (oy < Parray[p].y + Parray[p].size)) {
					decisionTreeCoords[currentProj][0] = (ox - Parray[p].x) / Parray[p].size;
					decisionTreeCoords[currentProj][1] = (oy - Parray[p].y) / Parray[p].size;
				}
			}
			glutSetWindow(side_window);  
			glutPostRedisplay();
		}
	} else if (button_state==MIDDLEDOWN) {	// zooming
		if (coords_mode==0) {
			R.zoomend(from_x, from_y);	
			glutSetWindow(side_window);  
			glutPostRedisplay();
		} else if (coords_mode==1) {
			pay = -0.3 + 1.3*(oy - Parray[currentProj].y)/Parray[currentProj].size;
			if (zoommode==0) {
				parzoomstart = pay;
			} else if (zoommode==1) {
				parzoomend = pay;
			} else if (zoommode==2) {
				parzoomend = (parzoomend - parzoomstart);
				parzoomstart = pay - zoomoff;
				parzoomend = parzoomstart + parzoomend;
			}
			if (parzoomend < parzoomstart) {
				temp = parzoomstart;
				parzoomstart = parzoomend;
				parzoomend = temp;
			}
			if (parzoomend > 1.0) parzoomend = 1.0;
			if (parzoomstart < 0.0) parzoomstart = 0.0;
			glutSetWindow(side_window);  
			glutPostRedisplay();
		}
	} else if (button_state==LEFTZOOMDOWN) {
		if (mode==1) { // set region mode
			if (currentProj>=0) {
				px = (int)(512.0*((R.zoomsx+((float)zoom_prev_x/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);
				rx = (int)(512.0*((R.zoomsx+((float)zx/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);		
				py = (int)(512.0*((R.zoomsy+((float)(zoomheight-zoom_prev_y)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);
				ry = (int)(512.0*((R.zoomsy+((float)(zoomheight-zy)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);			
				
				px = (int)(512.0*((R.zoomsx+(((float)zoom_prev_x-zoomx)/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);
				rx = (int)(512.0*((R.zoomsx+(((float)zx-zoomx)/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);		
				py = (int)(512.0*((R.zoomsy+((float)(sideheight-zoom_prev_y-zoomy)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);
				ry = (int)(512.0*((R.zoomsy+((float)(sideheight-zy-zoomy)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);	

				bsize = 1;
				
				glutSetWindow(side_window);
				glutSwapBuffers(); 
				glMatrixMode(GL_PROJECTION);
				glLoadIdentity();  
				gluOrtho2D(0.0,512.0,0.0,512.0);
						// left, right, bottom, top
				glViewport(Parray[currentProj].x,Parray[currentProj].y,Parray[currentProj].size,Parray[currentProj].size);
						// startx, starty, xsize, ysize
						// coordinates begin from lower left corner of window	
				glMatrixMode(GL_MODELVIEW);
				glLoadIdentity();		
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				Parray[currentProj].MarkAndDisplay(px, py, rx, ry, bsize, region);
				glDisable(GL_BLEND);
				glutSwapBuffers(); 

				glutSetWindow(side_window); 
				glutPostRedisplay();		
			}
		}
	}

	zoom_prev_x = zx;
	zoom_prev_y = zy;

	prev_x = x;
	prev_y = y;


}


void sideGlutMouse(int button, int state, int x, int y )
{
	if (COR.correlation_mode==0) {
		normal_mouse(button,state,x,y);
	} else {
		COR.MouseButton(brushsize,state,x,sideheight-y);
		glutSetWindow(side_window);  
		glutPostRedisplay();
	}
}

void normal_mouse(int button, int state, int x, int y )
{
	float ox, oy, pax, pay; 
	int p, i, done, Ly;
	int px, rx, py, ry, bsize;
	float conversion;
	int a, c;

	conversion = legendheight/24.0;

	if (x < 50) {
		Ly = sideheight - y;
		if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
			if ((Ly>2.0*conversion) && (Ly<10.0*conversion)) { // if clicked on a region
				mode = 1;
				region = Ly/(int)conversion - 2;
				Projection::currentlegend = Ly/(int)conversion - 2;
			} else if ((Ly>10.0*conversion) && (Ly<11.0*conversion)) { // if clicked on edit axes
				mode = 0;
				Projection::currentlegend = Ly/(int)conversion - 2;
			} else if ((Ly>11.0*conversion) && (Ly<12.0*conversion)) { // if clicked on edit tree endpoint
				mode = 2;
				Projection::currentlegend = Ly/(int)conversion - 2;
			} else if ((Ly>12.0*conversion) && (Ly<13.0*conversion)) { // if clicked on blank
				Projection::currentlegend = Ly/(int)conversion - 2;
				mode = 1;
				region = -1;
			} else if ((Ly>13.0*conversion) && (Ly<14.0*conversion)) { // if clicked on cue
				Projection::cue = -Projection::cue;
			}  
		}
	} else if (sideheight-y<50) {
		if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
			c = (int)(((float)x/(float)legendwidth)*24.0*2.0)-8;
			//printf("cluster class %d\n", c);
			i = (int)(((float)(sideheight-y)/(float)legendheight)*24.0*2.0);
			//printf("cluster num %d\n", i);
			if ((c>=0) && (i>=0) && (c<MAX_N_CLASSES) && (i<MAX_N_CLUSTERS)) {
				for (a=0;a<S.numclasses;a++) {
					if (S.classes[a]==c) {
						S.clusterflags[currentProj][a][i] = 1 - S.clusterflags[currentProj][a][i];
					}
				}
			}
		}
	} else if ((x > zoomx) && (x < zoomx + 50) && (sideheight - y > zoomheight + zoomy) && (sideheight - y < zoomheight + zoomy + 20)) {
	
		zoomparmode = 0;

	} else if ((x > zoomx + 50) && (x < zoomx + 100) && (sideheight - y > zoomheight + zoomy) && (sideheight - y < zoomheight + zoomy + 20)) {
	
		zoomparmode = 1;
	
	} else if ((x > zoomx + 100) && (x < zoomx + 150) && (sideheight - y > zoomheight + zoomy) && (sideheight - y < zoomheight + zoomy + 20)) {
	
		zoomparmode = 2;
	
	} else if ((x > parx) && (x < parx + parwidth) && (sideheight-y > pary) && (sideheight-y < pary + parheight)) {
		if (projCoords[currentProj]==0) { // star coordinates mode
			if (mode==1) { // set region mode
				if ( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) {
					if (currentProj>=0) {	
						px = (int)(512.0*((R.zoomsx+(((float)zoom_prev_x-zoomx)/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);
						rx = (int)(512.0*((R.zoomsx+(((float)x-zoomx)/(float)(zoomaspect*zoomheight))*(R.zoomex-R.zoomsx)) + 1.2)/2.4);		
						py = (int)(512.0*((R.zoomsy+((float)(sideheight-zoom_prev_y-zoomy)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);
						ry = (int)(512.0*((R.zoomsy+((float)(sideheight-y-zoomy)/(float)zoomheight)*(R.zoomey-R.zoomsy)) + 1.2)/2.4);			
						bsize = 1;	
						glutSetWindow(side_window);  
						glutSwapBuffers(); 
						glMatrixMode(GL_PROJECTION);
						glLoadIdentity();  
						gluOrtho2D(0.0,512.0,0.0,512.0);
								// left, right, bottom, top
						glViewport(Parray[currentProj].x,Parray[currentProj].y,Parray[currentProj].size,Parray[currentProj].size);
								// startx, starty, xsize, ysize
								// coordinates begin from lower left corner of window	
						glMatrixMode(GL_MODELVIEW);
						glLoadIdentity();		
						glEnable(GL_BLEND);
						glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
							Parray[currentProj].MarkAndDisplay(px, py, rx, ry, bsize, region);
						glDisable(GL_BLEND);
						glutSwapBuffers(); 
						
						glutSetWindow(side_window);		
		
					}	
					button_state = NODOWN;
				} else if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
					button_state = LEFTZOOMDOWN;
				}
			}	 
	 		zoom_prev_x = x;
			zoom_prev_y = y;
		} else { // mode is parallel coordinates mode, set zoom in parallel coordinates
			if (state == GLUT_DOWN ) {
					pax = -0.02 + 1.24*((float)x - parx)/parwidth;
					pay = -0.02 + 1.04*((sideheight - (float)y) - pary)/parheight;
					printf("clicked in auxiliary %f %f\n", parx, pary);
					if (pay > -0.1) { // user wants to select a category
						// find out which axis
						pax = pax + 0.5/((float)Group::ncatdims - 1);
						pax = pax * ((float)Group::ncatdims - 1);
						a = (int)pax;
						// find out which category
						if (a==zoomaxis) {
							pay = (pay*(parzoomend-parzoomstart) + parzoomstart) * (float)S.catrange[a];
							c = (int)pay;
							// toggle the red/blue flag
							if ((c>=0)&&(c<S.catrange[a])) {
								if (projParallel[currentProj][a][c]==0) {
									projParallel[currentProj][a][c]=1;
								} else {
									projParallel[currentProj][a][c]=0;
								}
							}
						}
					}
			}
		}
	
	} else { // clicked on the current projection window
		ox = (float)x;
		oy = sideheight - (float)y;	
		if ((coords_mode==0)||(coords_mode==2)) { // star or cluster coordinates mode
			x = x - (int)Parray[currentProj].x;
			y = y - ((int)sideheight - ((int)Parray[currentProj].y+Parray[currentProj].size));
			from_x = -1.0 + 2.0*(float)x/Parray[currentProj].size;
			from_y = -1.0 + 2.0*(Parray[currentProj].size-(float)y)/Parray[currentProj].size;
			from_x = from_x * 1.2;
			from_y = from_y * 1.2;
			if (mode==0) { // edit axis mode
				if (coords_mode==0) {		
					if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
						R.SelectAxis(from_x, from_y);
						button_state = LEFTDOWN;
					} else if ( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) {
						R.MoveAxis(from_x, from_y);
						R.ResetAxis();
						axis_spinner->set_int_val(R.lastaxis);
						selected_axis = R.lastaxis;
						button_state = NODOWN;
						S.showall = 1;
					}
				}
				glutSetWindow(side_window);  
				glutPostRedisplay();
			} else if (mode==1) { // set region mode
				if ( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) {
					if (grow_region_flag==0) {
						if (currentProj>=0) {
							glutSetWindow(side_window);  
							glutSwapBuffers(); 
							glMatrixMode(GL_PROJECTION);
							glLoadIdentity();  
							gluOrtho2D(0.0,512.0,0.0,512.0);
									// left, right, bottom, top
							glViewport(Parray[currentProj].x,Parray[currentProj].y,Parray[currentProj].size,Parray[currentProj].size);
									// startx, starty, xsize, ysize
									// coordinates begin from lower left corner of window	
							glMatrixMode(GL_MODELVIEW);
							glLoadIdentity();		
							glEnable(GL_BLEND);
							glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
							Parray[currentProj].MarkAndDisplay(prev_x, 512-prev_y, x, 512-y, brushsize, region);
							glDisable(GL_BLEND);
							glutSwapBuffers(); 
						}
					} else {
						Projection::begin_grow = Projection::end_grow = 0;
						Parray[currentProj].GrowRegion(x,512-y,region);
						grow_box->set_int_val(0);
						grow_region_flag = 0;
					}
					button_state = NODOWN;
					S.showall = 1;
				} else if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
					button_state = LEFTDOWN;
				}
			} else if (mode==2) { // edit decision tree coords mode
				if ( button == GLUT_LEFT_BUTTON && state == GLUT_UP ) {
					p = projConditions[currentProj][0];
					if (p >= 0) {
						if ((ox > Parray[p].x) && (ox < Parray[p].x + Parray[p].size) &&
							(oy > Parray[p].x) && (oy < Parray[p].y + Parray[p].size)) {
							decisionTreeCoords[currentProj][0] = (ox - Parray[p].x) / Parray[p].size;
							decisionTreeCoords[currentProj][1] = (oy - Parray[p].y) / Parray[p].size;
							mode = 0;
							Projection::currentlegend = 8;
						}
					}	
					button_state = NODOWN; 
				} else if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
					button_state = LEFTDOWN;
				}
			}
			
			if ( button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN ) {
				R.zoomstart(from_x,from_y);
				button_state = MIDDLEDOWN;
			} else if ( button == GLUT_MIDDLE_BUTTON && state == GLUT_UP ) {
				R.zoomend(from_x, from_y);
				button_state = NODOWN; 		
				S.showall = 1;
			}
		} else if (coords_mode==1) { // parallel coordinates mode
			if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
				pax = -0.02 + 1.04*((float)x - Parray[currentProj].x)/Parray[currentProj].size;
				pay = -0.3 + 1.3*((sideheight - (float)y) - Parray[currentProj].y)/Parray[currentProj].size;
				if (pay > -0.1) { // user wants to select a category
					// find out which axis
					pax = pax + 0.5/((float)Group::ncatdims - 1);
					pax = pax * ((float)Group::ncatdims - 1);
					a = (int)pax;
					// find out which category
					if ((a>=0)&&(a<Group::ncatdims)) {
						pay = pay * (float)S.catrange[a];
						c = (int)pay;
						// toggle the red/blue flag
						if ((c>=0)&&(c<S.catrange[a])) {
							if (projParallel[currentProj][a][c]==0) {
								projParallel[currentProj][a][c]=1;
							} else {
								projParallel[currentProj][a][c]=0;
							}
						}
					}
				} else { // user wants to toggle between blue and red mode
					if (pax < 0.5) {
						redblueflag = 1; // red
					} else {
						redblueflag = 0; // blue
					}
				}
				glutPostRedisplay();		
			} else if ( button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN ) {
				pax = -0.02 + 1.04*((float)x - Parray[currentProj].x)/Parray[currentProj].size;
				pay = -0.3 + 1.3*((sideheight - (float)y) - Parray[currentProj].y)/Parray[currentProj].size;
				if (pay > -0.1) { // user wants to zoom in on an axis
					// find out which axis
					pax = pax + 0.5/((float)Group::ncatdims - 1);
					pax = pax * ((float)Group::ncatdims - 1);
					if ((pay>=0.0) && (pay<=1.0)) {
						// set zoom axis
						if (zoomaxis != (int)pax) {
							zoomaxis = (int)pax;
							parzoomstart = parzoomend = 0.0;
							zoommode = 1; // move end
						} else {
							// find out whether user wants to move zoomstart or zoomend or both
							if (pay<parzoomstart) {
								zoommode = 0; // move start
							} else if (pay>parzoomend) {
								zoommode = 1; // move end
							} else if ((pay - parzoomstart) < 0.05) {
								zoommode = 0; // move start
							} else if ((parzoomend - pay) < 0.05) {
								zoommode = 1; // move end
							} else {
								zoommode = 2; // move both
								zoomoff = pay - parzoomstart;
							}
						}
						button_state = MIDDLEDOWN;
					}
				}
				glutPostRedisplay();		
			} else if (state == GLUT_UP) {
				button_state = NODOWN;
			}


		}

			
		if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
			// check for changing the display of the decision tree
			done = 0;
			for (i=0;((i<numProjs)&&(!done));i++) {
				if (mainChainFlags[i]==1) {
					if ((ox > Parray[i].x - 10.0) && (ox < Parray[i].x) &&
						(oy > Parray[i].y - 10.0) && (oy < Parray[i].y)) {
						if (projConditions[i][0]>=0) {
							setCurrentProjection(projConditions[i][0]);
						}
						done = 1;
					}
				} else if (mainChainFlags[i]==0) {
					if ((ox > Parray[i].x + Parray[i].size) && (ox < Parray[i].x + Parray[i].size + 10.0) &&
						(oy > Parray[i].y + Parray[i].size) && (oy < Parray[i].y + Parray[i].size + 10.0)) {
						setCurrentProjection(i);
						done = 1;
					}
				}	
			}
		}
	}

	glutSetWindow(side_window); 
	glutPostRedisplay();

	prev_x = x;
	prev_y = y;
	
}


void sideGlutReshape( int x, int y )
{
	sidewidth = x;
	sideheight = y;
    glutSetWindow(side_window);  
	glutPostRedisplay();
}


void sideGlutKeyboard(unsigned char Key, int x, int y)
{
  switch(Key)
  {
  case 27: 
  case 'q':
    exit(0);
    break;
  };
  
  glutPostRedisplay();
}




/**************************************** main() ********************/

void main(int argc, char* argv[])
{

	int dummy, i, j, k, n;

	// set variables controlling window dimensions

	sidewidth = 990;
	sideheight = 900;

	sidex = 5;
	sidey = 30;

	zoomwidth = 256;
	zoomheight = 350;

	zoomx = 65; 
	zoomy = 65;

	parwidth = 768;
	parheight = 256;

	parx = 65; 
	pary = 65;

	controlx = 1000;
	controly = 0;

	legendx = 5;
	legendy = 920;

	legendwidth = 768;
	legendheight = 768;

	currentProj = 0;
	for (dummy=0;dummy<MAX_N_PROJS;dummy++) {
		projAxes[dummy] = NULL;
		projConditions[dummy][0] = projConditions[dummy][1] = -1;
		mainChainFlags[dummy] = 1;
		numChildren[dummy] = 0;
		projCoords[dummy] = 0; // set to star coords by default
		pcaflags[dummy] = 0;
	}
	P1.allmeans = new float*[MAX_N_PROJS];
	P1.allstddevs = new float*[MAX_N_PROJS];
	P1.allsymmats = new float**[MAX_N_PROJS];
	numProjs = 1;
	Parray[0].x = 450.0;
	Parray[0].y = sideheight - 10.0 - 512.0;
	Parray[0].size = 512.0;

	Projection::cue = -1;

	zoomparmode = 0;
	redblueflag = 0;

	COR.correlation_mode = 0;
	for (i=0;i<500000;i++) {
		obj_clusters[i] = (signed char)-1;
	}

	// ********************** CREATE SIDE WINDOW (for main display) *************** //

	glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
  
	side_window = glutCreateWindow( "Radviz" );
	glutPositionWindow( sidex , sidey );
	
	glutReshapeWindow( sidewidth , sideheight );
	glutDisplayFunc( sideGlutDisplay );
	glutReshapeFunc( sideGlutReshape );  
	glutKeyboardFunc( sideGlutKeyboard );
	glutMotionFunc( sideGlutMotion );
	glutMouseFunc( sideGlutMouse );

	
	glEnable(GL_DEPTH_TEST);


	// *********************** INIT RADVIZ AND GROUP OBJECTS ********************** //

	 // TEOH SET FOR EVERY DATA SET


	ncols = S.readVeryStandard();
	S.readVeryStandardTest();
	R.InitAxes(ncols);
	P.SetNumAxes(Group::ncatdims);

	// read dimensionnames
	R.readKDDdimensionNames();

	printf("finished reading data\n");

	printf("num axes %d\n", R.numaxes);

	projAxes[0] = R.axes;

	for (dummy=0;dummy<MAX_N_PROJS;dummy++) {
		npcacols = 7;
		P1.ncols = R.numaxes;
		P1.allmeans[dummy] = new float[R.numaxes];
		P1.allstddevs[dummy] = new float[R.numaxes];
		P1.allsymmats[dummy] = new float*[R.numaxes];
		for (j=0;j<R.numaxes;j++) {
			P1.allsymmats[dummy][j] = new float[R.numaxes];
		}
	}

	printf("finished principal component analysis\n");

	printf("finished clustering\n");

	/******** BEGIN GLUI **********************************/

	printf( "GLUI version: %3.2f\n", GLUI_Master.get_version() );

	glui = GLUI_Master.create_glui( "Control Panel", 0, controlx, controly ); 
                                  /* name, flags, x, and y */

	/*** Add invisible panel to hold rest of controls ***/
	GLUI_Panel *panel1 = glui->add_panel( "", GLUI_PANEL_NONE );

	GLUI_Panel *panel2 = glui->add_panel_to_panel ( panel1, "", GLUI_PANEL_NONE );

	glui->add_button_to_panel(panel2, "Create Projection", 0, proj_cb);
	glui->add_button_to_panel(panel2, "Delete Projection", 1, proj_cb);

	GLUI_Panel *coordinates_panel = glui->add_panel_to_panel( panel2, "Coordinates" );
	coords_box1 = glui->add_checkbox_to_panel(coordinates_panel, "Star", &dummy, 0, coords_box_cb);
	coords_box2 = glui->add_checkbox_to_panel(coordinates_panel, "Parallel", &dummy, 1, coords_box_cb);
	//coords_box3 = glui->add_checkbox_to_panel(coordinates_panel, "Cluster", &dummy, 2, coords_box_cb);
	coords_box1->set_int_val(1);
	coords_box2->set_int_val(0);
	//coords_box3->set_int_val(0);
	coords_mode = 0;
	GLUI_Panel *display_panel = glui->add_panel_to_panel( panel2, "Display" );
	//glui->add_checkbox_to_panel(display_panel, "Test top", &testtop, 0, redraw_func);
	show_train_box = glui->add_checkbox_to_panel(display_panel, "Show Train", &show_train, 0, redraw_func);
	show_train = 1;
	show_train_box->set_int_val(1);
	glui->add_checkbox_to_panel(display_panel, "Translucent", &train_translucent, 0, redraw_func);
	glui->add_checkbox_to_panel(display_panel, "Show Test", &show_test, 0, redraw_func);
	glui->add_checkbox_to_panel(display_panel, "Translucent", &test_translucent, 0, redraw_func);
	testcompare = 0;	
	show_axes_box = glui->add_checkbox_to_panel(display_panel, "Display Axes", &(R.display_axes_flag), 0, redraw_func);
	show_axes_box->set_int_val(1);
	glui->add_checkbox_to_panel(display_panel, "Display Axes Names", &(R.display_axes_names), 0, redraw_func);
	R.display_axes_flag = 1;	
	R.display_axes_names = 0;


	GLUI_Panel *setregion_panel = glui->add_panel_to_panel( panel2, "Set Region" );
	brushsize_spinner = glui->add_spinner_to_panel(setregion_panel, "Brush size", GLUI_SPINNER_INT, &brushsize, 0, brushsize_cb);
	brushsize_spinner->set_int_limits(1,12);
	brushsize_spinner->set_int_val(6);
	normbrushsize = 6.0/512.0;
	grow_box = glui->add_checkbox_to_panel(setregion_panel,"Grow region",&dummy,0,grow_region_cb);
	grow_box->set_int_val(0);
	grow_region_flag = 0;

	GLUI_Panel *displayoptions_panel = glui->add_panel_to_panel( panel2, "Display Options");
	faderegions_spinner = glui->add_spinner_to_panel(displayoptions_panel, "Fade Regions", GLUI_SPINNER_FLOAT, &faderegions, 0, redraw_func);
	faderegions_spinner->set_float_limits(0.0,1.0);
	faderegions_spinner->set_float_val(0.3);
	faderegions = 0.6;

	linethickness = 3;
	GLUI_Spinner *hide_category_spinner = glui->add_spinner_to_panel(displayoptions_panel, "Hide Category", GLUI_SPINNER_INT, &hide_category, 0, redraw_func);
	hide_category_spinner->set_int_limits(-1,20);
	hide_category_spinner->set_int_val(-1);
	hide_category = -1;

	GLUI_Panel *layout_panel = glui->add_panel_to_panel( panel2, "Layout" );
	expansion_spinner = glui->add_spinner_to_panel(layout_panel, "Expansion", GLUI_SPINNER_FLOAT, &(R.expansion), 0, expansion_cb);
	expansion_spinner->set_float_limits(0.1,100.0);
	expansion_spinner->set_float_val(5.0);
	expans[0] = R.expansion;
	axis_spinner = glui->add_spinner_to_panel(layout_panel, "Current Axis", GLUI_SPINNER_INT, &selected_axis);
	axis_spinner->set_int_limits(0,42);
	axis_spinner->set_int_val(0);
	glui->add_button_to_panel(layout_panel, "Boost Axis", 0, boost_cb);
	glui->add_button_to_panel(layout_panel, "Shrink Axis", 1, boost_cb);
	glui->add_button_to_panel(layout_panel, "Print Axis Names", 0, printnames_cb);


	GLUI_Panel *correlation_panel = glui->add_panel_to_panel( panel2, "Correlation" );
	corclass_spinner = glui->add_spinner_to_panel(correlation_panel, "Class", GLUI_SPINNER_INT, &correlation_class, 0, redraw_func);
	corclass_spinner->set_int_val(0);
	correlation_class = 0;
	glui->add_button_to_panel(correlation_panel, "Correlate", 0, correlate_cb);
	glui->add_button_to_panel(correlation_panel, "Print Correlations", 0, printcor_cb);


	GLUI_Panel *output_panel = glui->add_panel_to_panel( panel2, "Screen Dump" );
	glui->add_button_to_panel(output_panel, "To File", 0, output_cb);
	
	GLUI_Panel *io_panel = glui->add_panel_to_panel( panel2, "I/O" );
	glui->add_button_to_panel(io_panel, "Load", 0, io_cb);
	glui->add_button_to_panel(io_panel, "Save", 1, io_cb);

	glui->add_button_to_panel(panel1, "Classify", 0, classify_cb);	
	glui->add_button_to_panel(panel1, "Refresh", 0, redraw_func);	

	correlation_glui = GLUI_Master.create_glui( "Correlation", 0, controlx, controly ); 
	correlation_panel1 = correlation_glui->add_panel( "", GLUI_PANEL_NONE );
	correlation_glui->add_checkbox_to_panel(correlation_panel1, "Negative", &(COR.negative));
	correlation_glui->add_button_to_panel(correlation_panel1, "Remove", 0, correlation_remove_cb);
	correlation_glui->add_button_to_panel(correlation_panel1, "Test", 0, correlation_test_cb);
	correlation_glui->add_button_to_panel(correlation_panel1, "Commit", 0, correlation_commit_cb);
	correlation_glui->add_button_to_panel(correlation_panel1, "Cancel", 0, correlation_cancel_cb);	
	
	small_spinner = correlation_glui->add_spinner_to_panel(correlation_panel1, "Error margin", GLUI_SPINNER_FLOAT, &(COR.margin), 0, correlation_margin_cb);
	small_spinner->set_float_limits(0.0001,0.01);
	small_spinner->set_float_val(0.001);

	correlation_glui->add_button_to_panel(correlation_panel1, "Inter-dimensional", 0, interdim_cb);

	correlation_glui->hide();

	interdim_glui = GLUI_Master.create_glui( "Inter-dimensional", 0, controlx, controly ); 
	interdim_panel1 = interdim_glui->add_panel( "", GLUI_PANEL_NONE );
	interdim_glui->add_button_to_panel(interdim_panel1, "Commit", 0, interdim_commit_cb);
	interdim_glui->add_button_to_panel(interdim_panel1, "Cancel", 0, interdim_cancel_cb);	
	
	interdim_glui->hide();

	GLUI_Master.set_glutIdleFunc( myGlutIdle );
	glutMainLoop();

}

