
/********************************************************************************/
/* 						                										*/
/* 						M I N D   G E O M E T R Y								*/
/* 						                										*/
/* 						© 2002 by Martin Jaggi 									*/
/* 						                										*/
/* 						m.jaggi@gmx.net 										*/
/* 						http://www.m8j.net/mind			 						*/
/* 						                										*/
/* 						Code for MPW C	(for Mac PPC)							*/
/* 						                										*/
/********************************************************************************/

#include <Types.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <Fonts.h>
#include <Windows.h>
#include <Dialogs.h>
#include <Menus.h>
#include <Devices.h>
#include <Events.h>
#include <ToolUtils.h>
#include <Processes.h>
#include <math.h>
#include <NumberFormatting.h>
#include <Files.h>
#include <Resources.h>
#include <TextUtils.h>
#include <stdio.h>
#include <StandardFile.h>
#include <AppleEvents.h>

	
/* Constants */

#define appleID				1000		/* resource IDs/menu IDs for Apple, */
#define fileID				1001		/* 	File and */
#define settingsID			1002		/*	Edit menus */
	
#define appleM				0			/* Index for each menu in myMenus */
#define fileM				1
#define settingsM			2

#define menuCount			3			/* Total number of menus */

#define drawWinID 			1000		/* Windows */
#define infoWindowID		1111
#define introWinID			500
#define aboutMeDLOG 		1000		/* Dialogs */
#define zoomDLOG			3000

#define normalCursorID		1000		/* Cursors */
#define rotateCursorID		1001
#define growCursorID		1002

#define introPicID			1000		/* Pictures */
#define headerID			4000
#define zUpID				4004
#define zDnID				4005
#define bUpID				4006
#define bDnID				4007
#define gUpID				4008
#define gDnID				4009
#define addID				5000

#define aboutMeCommand		1


#define	pi				3.14159265359
#define	pixelRepeat		3
#define	maxGSize		60


struct GridT {							/* the Grid for 3D mode */
    float			w;
    float			cx,cy,cz;
};
typedef struct GridT GridT;


struct VectorT {
    float			x,y,z;
};
typedef struct VectorT VectorT;


struct ColrT {
    float			red,green,blue;
};
typedef struct ColrT ColrT;


struct WindowT {
	int				gsize,gridunit;			/* size of the grid */
	float			zoom;         			/* r units per grid unit */
	Boolean 		zoomIsSet;
	
	double			maxw,minw;
	
	float			brightness;				/* used for brighter coloration */
	
	int				picWidth;
	
    WindowPtr		windowP;
	GWorldPtr		gGWorld;				/* Offscreen GWorld */
	Rect			offRect;
};

struct GuiT {							/* the GUI */
	CursHandle  	normalCursor, rotateCursor, growCursor;
	PicHandle		header,  threeD,  slice,  add,		zUp,  zDn,  bUp,  bDn,  gUp,  gDn;
	PicHandle											zUpA, zDnA, bUpA, bDnA, gUpA, gDnA;
	Rect			headerR, threeDR, sliceR, addR,  	zUpR, zDnR, bUpR, bDnR, gUpR, gDnR;
	Rect			orbsR, fpsRect;
	RGBColor		blackc, bc, barc, whitec, greyc, greenc, bluec;
};



/* Globals */
	int nk;						// Kategorien
	int nj;						// Knoten
	int ni;						// Dimension

	float *a_a_out;				// Output-Wert für Kategorie k
	float **a_w;				// Assoziationsgewicht für Kategorie k zu Knoten j
	
	float *a_a_hid;				// Aktivierung von Knoten j
	float **a_h;			 	// Position von Knoten j
	
	int   num_learn_steps;		// Anzahl Lern-Schritte in der Eingabedatei
	int   learn_count;
	FILE* inF;
	
	float *a_a_in;				// Inputvektor des Exemplars
	float *a_a;				 	// Attention strength der Dimension i
	float *a_t;					// Teacher-Value für ein Exemplar für Kategorie k
	
	ColrT a_kcol[10];			// Farbe der Kategorie k
	
	Str255 filename;
	

GridT		grid[(maxGSize+1)*(maxGSize+1)*(maxGSize+1)];

float		rota;						/* everything for the rotation */
VectorT		rotA,xAxis,yAxis,zAxis;
float		rmat0,rmat1,rmat2,rmat3,rmat4,rmat5,rmat6,rmat7,rmat8;
VectorT		xBone,yBone,zBone;

float	rFac,wzFac,waFac;
	
struct WindowT	window;
WindowPtr	infoWindow;
Boolean		autoRotate,showRFunc,showAFunc;
int			fps;

struct GuiT		gui;

MenuHandle	myMenus[menuCount];
Rect		dragRect;					/* Rectangle used to mark bounds for dragging window */
Boolean		doneFlag;					/* true if user has chosen Quit command */
EventRecord	myEvent;
WindowPtr	whichWindow;
char		theChar;
OSErr		error;
SysEnvRec	theWorld;
QDGlobals	qd;



/* Prototypes */
void Rendering();
void calcMaxGrid();
void initGrid();
void calcGrid();
void rotate(float ra, float x,float y,float z);
void DrawFuncs();
void growWindow();
void ChangeZoom();
void SetUpMenus();
void UpdateMenus();
void DoCommand(long int mResult);
void Intro();
void ShowAboutMeDialog();
void ShowInfo();
double sqr(double a);
void initWindow();
void initGUI();
void readfile();



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


//****************************
void calc_response(void) {
	int  k,j,i; float d;
	
	/* Aktivieren der Knoten */
	for (j=0; j<nj; j++) {
		d = 0; for (i=0; i<ni; i++) d += (a_h[j][i] - a_a_in[i])*(a_h[j][i] - a_a_in[i]);   /* euklidische norm */
		a_a_hid[j] = exp(-sqrt(d));
	}
	
	/* Berechnen der Output-Werte */
	for (k=0; k<nk; k++) {
		a_a_out[k] = 0; for (j=0; j<nj; j++) a_a_out[k] += a_w[k][j] * a_a_hid[j];
	}
}
//****************************
void learn(int kat) {
	int  k,j,i; float sk,sj;
	
	/* Berechnen der Teacher-Werte */
	for (k=0; k<nk; k++) {
		if (k==kat) {	if (a_a_out[k]> 1.0) a_t[k] = a_a_out[k]; else a_t[k] =  1; }
		else {			if (a_a_out[k]<-1.0) a_t[k] = a_a_out[k]; else a_t[k] = -1;	}
	}
	
	/* Ändern der Assoziations-Gewichte */
	for (k=0; k<nk; k++)  for (j=0; j<nj; j++)
		a_w[k][j] += 0.1 * (a_t[k]-a_a_out[k]) * a_a_hid[j];
								
	/* Ändern der attention strengths */
	for (i=0; i<ni; i++) {
		sj = 0;
		for (j=0; j<nj; j++) {
			sk = 0;
			for (k=0; k<nk; k++) sk += (a_t[k]-a_a_out[k]) * a_w[k][j];
			sj += sk * a_a_hid[j] * fabs( a_h[j][i] - a_a_in[i] );
		}
		a_a[i] -= 0.6 * sj;
		if (a_a[i]<0) a_a[i] = 0;
	}
	calcMaxGrid();
}
//****************************
void learn_step() {
	int  i,cat;
		
		for (i=0; i<ni; i++) fscanf(inF, "%f", &a_a_in[i]);	
		
		calc_response();
		
		fscanf(inF, "%i", &cat);
		
		learn(cat);	
}
//****************************

	
void main(void)
{   long int newSize;
	int k;
	Point pt1,pt2,ptOld;
	long int t; float dh,dv;		Str255 string; Boolean inRollOver;
	
	MaxApplZone();			/* Increase application heap. */
	
	error = SysEnvirons(1, &theWorld);
	if (theWorld.hasColorQD == false) {
		SysBeep (50);
		ExitToShell();							/* If no color QD, we must leave. */
	};

	InitGraf(&qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs(nil);
	InitCursor();

	
	
	doneFlag = false;							/* flag to detect when Quit command is chosen */
	
	autoRotate = true; 
	showRFunc = false;
	showAFunc = false;
			
	fps=0;
	
	
	a_a_out = (float *)malloc(sizeof(float));
	a_w = (float **)malloc(sizeof(float *));
	a_w[0] = (float *)malloc(sizeof(float));
	a_a_hid = (float *)malloc(sizeof(float));
	a_h = (float **)malloc(sizeof(float *));
	a_h[0] = (float *)malloc(sizeof(float));
	a_a_in = (float *)malloc(sizeof(float));
	a_a = (float *)malloc(sizeof(float));
	a_t = (float *)malloc(sizeof(float));
	
	initGUI();
	
	Intro();
	
	FlushEvents(everyEvent,0);

	SetUpMenus();
	
	infoWindow = GetNewCWindow(infoWindowID, nil, (WindowPtr) -1);
	SizeWindow(infoWindow,100,134+13+1,true);
	
	UpdateMenus();
	

	initWindow();
	
	
	/************************/
	readfile();
	/************************/
	
	
	
	
	k=0; t = TickCount();
	do {
		
		
		if (learn_count < num_learn_steps) {
			learn_step();
			learn_count++;
		} else if (learn_count == num_learn_steps) {
			fclose(inF);
			learn_count++;
		} else {
			rotate(rota,rotA.x,rotA.y,rotA.z);
		}
		
		calcGrid();
		Rendering();
			
		
		k++;
		if (k%40==0) {
			fps = 40/((TickCount()-t)/60.0);
			SetPort(infoWindow); RGBForeColor(&gui.bc); PaintRect(&gui.fpsRect); RGBForeColor(&gui.whitec);
			NumToString(fps,string); MoveTo(66,83); DrawString(string); DrawString("\p fps");
			RGBForeColor(&gui.blackc);
			k=0; t = TickCount();
		}
		
	
		SystemTask();
		
		if (WaitNextEvent(everyEvent, &myEvent, 5L, NULL)) {
		
			if (FindWindow(myEvent.where, &whichWindow)==inContent) {
				if (whichWindow==window.windowP) SetCursor(**&gui.rotateCursor);
				if (whichWindow==infoWindow) {
					SetPort(infoWindow); pt1=myEvent.where; GlobalToLocal(&pt1);
					DrawPicture(gui.zUp, &gui.zUpR);
					DrawPicture(gui.zDn, &gui.zDnR);
					DrawPicture(gui.bUp, &gui.bUpR);
					DrawPicture(gui.bDn, &gui.bDnR);
					DrawPicture(gui.gUp, &gui.gUpR);
					DrawPicture(gui.gDn, &gui.gDnR);
					inRollOver = false;
					RGBForeColor(&gui.bc); PaintRect(&gui.headerR); MoveTo(5,13); RGBForeColor(&gui.whitec); 
						if 		(PtInRect(pt1,&gui.zUpR)) { inRollOver=true; DrawPicture(gui.zUpA,&gui.zUpR);
								NumToString(window.zoom,string); DrawString("\pzoom = "); DrawString(string); }
						else if (PtInRect(pt1,&gui.zDnR)) { inRollOver=true; DrawPicture(gui.zDnA,&gui.zDnR);
								NumToString(window.zoom,string); DrawString("\pzoom = "); DrawString(string); }
						else if (PtInRect(pt1,&gui.bUpR)) { inRollOver=true; DrawPicture(gui.bUpA,&gui.bUpR);
								NumToString(window.brightness*10.01,string); DrawString("\pbrightness = "); DrawString(string); }
						else if (PtInRect(pt1,&gui.bDnR)) { inRollOver=true; DrawPicture(gui.bDnA,&gui.bDnR);
								NumToString(window.brightness*10.01,string); DrawString("\pbrightness = "); DrawString(string); }
						else if (PtInRect(pt1,&gui.gUpR)) { inRollOver=true; DrawPicture(gui.gUpA,&gui.gUpR);
								NumToString(window.gsize,string); DrawString("\pgrid size = "); DrawString(string); }
						else if (PtInRect(pt1,&gui.gDnR)) { inRollOver=true; DrawPicture(gui.gDnA,&gui.gDnR);
								NumToString(window.gsize,string); DrawString("\pgrid size = "); DrawString(string); }
					if (!inRollOver) DrawPicture(gui.header, &gui.headerR);
					RGBForeColor(&gui.blackc);
				}
			} else if (FindWindow(myEvent.where, &whichWindow)==inGrow) SetCursor(**&gui.growCursor);
			else SetCursor(**&gui.normalCursor);
		
			switch (myEvent.what) {				/* case on event type */

				case mouseDown:
					switch (FindWindow(myEvent.where, &whichWindow)) {

						case inSysWindow:		/* desk accessory window: call Desk Manager to handle it */
							SystemClick(&myEvent, whichWindow);
							break;

						case inMenuBar:			/* Menu bar: learn which command, then execute it. */
							DoCommand(MenuSelect(myEvent.where));
							break;

						case inDrag:			/* title bar: call Window Manager to drag */
							DragWindow(whichWindow, myEvent.where, &dragRect);
							break;
							
						case inGrow:
				 			newSize = GrowWindow(whichWindow, myEvent.where, &dragRect);
							if (newSize>0) {
					 		 window.picWidth =  LoWord(newSize);
					  		 SizeWindow(whichWindow,window.picWidth,window.picWidth,true);
							 
							 growWindow();
							 
						 	 Rendering();
					 	    }
							break;

						case inContent:
							if (whichWindow == infoWindow) {
								SetPort(infoWindow); GlobalToLocal(&myEvent.where);
								
								if (PtInRect(myEvent.where,&gui.addR)) {
									learn_step();
									calcMaxGrid(); calcGrid(); Rendering();
								}
								
								if (PtInRect(myEvent.where,&gui.zUpR)) {
									window.zoomIsSet = true;
									do { window.zoom/=1.06; calcMaxGrid(); calcGrid(); Rendering(); } while (Button());
								}
								if (PtInRect(myEvent.where,&gui.zDnR)) {
									window.zoomIsSet = true;
									do { window.zoom*=1.06; calcMaxGrid(); calcGrid(); Rendering(); } while (Button());
								}
								if (PtInRect(myEvent.where,&gui.bUpR)) {
									do { window.brightness*=1.06; calcGrid(); Rendering(); } while (Button());
								}
								if (PtInRect(myEvent.where,&gui.bDnR)) {
									do { window.brightness/=1.06; calcGrid(); Rendering(); } while (Button());
								}
								if (PtInRect(myEvent.where,&gui.gUpR) && (window.gsize+2<=maxGSize)) {
									window.zoomIsSet = false;
									do { window.gsize+=2; initGrid(); calcMaxGrid(); growWindow(); Rendering(); } while (Button());
								}
								if (PtInRect(myEvent.where,&gui.gDnR) && (window.gsize-2>=2)) {
									window.zoomIsSet = false;
									do { window.gsize-=2; initGrid(); calcMaxGrid(); growWindow(); Rendering(); } while (Button());
								}
								
								if (PtInRect(myEvent.where,&gui.headerR)) {
									ShowAboutMeDialog();
								}
							} else if (whichWindow == window.windowP) { 
								GlobalToLocal(&myEvent.where); pt1 = myEvent.where;  GetMouse(&pt2);
								do {						  /* rotate with mouse */
									 dh = -(pt2.h - pt1.h)/(window.picWidth/2.0);
									 dv =  (pt2.v - pt1.v)/(window.picWidth/2.0);
									 rotA.z = dh*zAxis.z + dv*yAxis.z;
									 rotA.y = dh*zAxis.y + dv*yAxis.y;
									 rotA.x = dh*zAxis.x + dv*yAxis.x;
									 rota = 1.1 * sqrt( sqr(rotA.x) + sqr(rotA.y) + sqr(rotA.z) );
									 if (rota>0) {rotA.x/= rota/1.1; rotA.y/= rota/1.1; rotA.z/= rota/1.1; }
									 rotate(rota,rotA.x,rotA.y,rotA.z);
									 calcGrid(); Rendering();
									 
									 ptOld=pt1; pt1 = pt2;  GetMouse(&pt2);
								} while (Button());
								
										/* now remember the movement (using ptOld) */
								 dh = -(pt2.h - ptOld.h)/(window.picWidth/2.0); dv =  (pt2.v - ptOld.v)/(window.picWidth/2.0);
								 rotA.z = dh*zAxis.z + dv*yAxis.z; rotA.y = dh*zAxis.y + dv*yAxis.y; rotA.x = dh*zAxis.x + dv*yAxis.x;
								 rota = 1.1 * sqrt( sqr(rotA.x) + sqr(rotA.y) + sqr(rotA.z) ); if (rota>0) {rotA.x/= rota/1.1; rotA.y/= rota/1.1; rotA.z/= rota/1.1; }
							} else {
								SelectWindow(whichWindow); /* and make it active if not */
							}
							break;
					}
					break;

				case updateEvt:
					 if ((WindowPtr)myEvent.message == infoWindow) {
					  BeginUpdate(infoWindow);
					  ShowInfo();
					  EndUpdate(infoWindow);
					 }
					break;
							
				case keyDown:
				case autoKey:					/* key pressed once or held down to repeat */
						theChar = (myEvent.message & charCodeMask); /* get the char */
						/* If Command key down, do it as a Menu Command. */
						if (myEvent.modifiers & cmdKey)
							DoCommand(MenuKey(theChar));
					break;
					
					
				case kHighLevelEvent:
					AEProcessAppleEvent( &myEvent );
					break;

			}

		}
		
	} while (!doneFlag);


	DisposeWindow(window.windowP);
	DisposeGWorld(window.gGWorld);
	
	ExitToShell();
}



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


void calcGrid()						/* calculate the wave values and multiply the layers */
{	long int	k,x,yz;
	float		w;
	int			kt;
	
 	for (yz = 0; yz < (window.gsize+1)*(window.gsize+1)*(window.gsize+1); yz+=(window.gsize+1)) {
	k = yz; grid[yz].w = grid[yz+1].w = grid[yz+2].w = 1.0;
 	for (x = 0; x < (window.gsize+1); x++) {
	
		a_a_in[0] = grid[k].cx/window.zoom;	
		a_a_in[1] = grid[k].cy/window.zoom;	
		a_a_in[2] = grid[k].cz/window.zoom;	
		calc_response();
	
		for (kt=0; kt<nk; kt++) {
		
			w = a_a_out[kt]/window.maxw;  if (w<0) w=0;
			w *= window.brightness; if (w>1) w=1;
			
													  /* store the final values in top grid layer */
			grid[yz].w		*=	1 - (w * (a_kcol[kt].red));
			grid[yz+1].w	*=	1 - (w * (1-a_kcol[kt].green));
			grid[yz+2].w	*=	1 - (w * (a_kcol[kt].blue));
		}
	   
	   /*grid[yz].w *= 1-w0;			 
	   grid[yz+2].w *= 1-w1;
	   grid[yz+1].w *= (1-w0)*(1-w1);*/
	   
		k++;
 	}
	}
}


void calcMaxGrid()
{	long int	k;
	double		maxw;
	int			kt;
	
	maxw=0;
 	for (k = 0; k < pow(window.gsize+1,3); k++) {
	
		a_a_in[0] = grid[k].cx/window.zoom;	
		a_a_in[1] = grid[k].cy/window.zoom;	
		a_a_in[2] = grid[k].cz/window.zoom;	
		calc_response();
		
		for (kt=0; kt<nk; kt++) {
			if (a_a_out[kt]>maxw) maxw=a_a_out[kt];
		}
 	}
	window.maxw = maxw;
}


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


void rotate(float ra, float x,float y,float z)			/* rotation: angle ra around axis x,y,z */
{	long int	k;
	float		nx,ny,nz;
	float		w,sn;
	float		xx,yy,zz,xy,xz,yz,wx,wy,wz;
	
	w = cos(ra/2);								/* the quaternion w,x,y,z */
	sn= sin(ra/2); x*= sn; y*= sn; z*= sn;
	
	xx = x*x; xx += xx;
	yy = y*y; yy += yy;
	zz = z*z; zz += zz;
	xy = x*y; xy += xy;
	xz = x*z; xz += xz;
	yz = y*z; yz += yz;
	wx = w*x; wx += wx;
	wy = w*y; wy += wy;
	wz = w*z; wz += wz;
	
	rmat0 = 1 - yy - zz;					/* the rotation matrix */
	rmat1 = xy - wz;
	rmat2 = xz + wy;
	rmat3 = xy + wz;
	rmat4 = 1 - xx - zz;
	rmat5 = yz - wx;
	rmat6 = xz - wy;
	rmat7 = yz + wx;
	rmat8 = 1 - xx - yy;
	
	/*rmat0 = 1 - 2*y*y - 2*z*z;
	rmat1 = 2*x*y - 2*w*z;
	rmat2 = 2*x*z + 2*w*y;
	rmat3 = 2*x*y + 2*w*z;
	rmat4 = 1 - 2*x*x - 2*z*z;
	rmat5 = 2*y*z - 2*w*x;
	rmat6 = 2*x*z - 2*w*y;
	rmat7 = 2*y*z + 2*w*x;
	rmat8 = 1 - 2*x*x - 2*y*y;*/

 	for (k = 0; k < pow(window.gsize+1,3); k++) { 	/* rotate each grid point */
	  
	  nx =  rmat0*grid[k].cx + rmat1*grid[k].cy + rmat2*grid[k].cz;
	  ny =  rmat3*grid[k].cx + rmat4*grid[k].cy + rmat5*grid[k].cz;
	  nz =  rmat6*grid[k].cx + rmat7*grid[k].cy + rmat8*grid[k].cz;
	  
	  grid[k].cx = nx; grid[k].cy = ny; grid[k].cz = nz;
	}
	
	  nx =  rmat0*xAxis.x + rmat1*xAxis.y + rmat2*xAxis.z; 	/* and rotate the coordinate system */
	  ny =  rmat3*xAxis.x + rmat4*xAxis.y + rmat5*xAxis.z;
	  nz =  rmat6*xAxis.x + rmat7*xAxis.y + rmat8*xAxis.z;
	  xAxis.x = nx; xAxis.y = ny; xAxis.z = nz;
	  nx =  rmat0*yAxis.x + rmat1*yAxis.y + rmat2*yAxis.z;
	  ny =  rmat3*yAxis.x + rmat4*yAxis.y + rmat5*yAxis.z;
	  nz =  rmat6*yAxis.x + rmat7*yAxis.y + rmat8*yAxis.z;
	  yAxis.x = nx; yAxis.y = ny; yAxis.z = nz;
	  nx =  rmat0*zAxis.x + rmat1*zAxis.y + rmat2*zAxis.z;
	  ny =  rmat3*zAxis.x + rmat4*zAxis.y + rmat5*zAxis.z;
	  nz =  rmat6*zAxis.x + rmat7*zAxis.y + rmat8*zAxis.z;
	  zAxis.x = nx; zAxis.y = ny; zAxis.z = nz;
	  

											  /* inverse matrix */
						/*rmat1 = xy + wz;
						rmat2 = xz - wy;
						rmat3 = xy - wz;
						rmat5 = yz + wx;
						rmat6 = xz + wy;
						rmat7 = yz - wx;*/
						  														 	/* and rotate the coordinate bones back */
						  /*nx =  rmat0*xBone.x + rmat1*xBone.y + rmat2*xBone.z;
						  ny =  rmat3*xBone.x + rmat4*xBone.y + rmat5*xBone.z;
						  nz =  rmat6*xBone.x + rmat7*xBone.y + rmat8*xBone.z;
						  xBone.x = nx; xBone.y = ny; xBone.z = nz;
						  nx =  rmat0*yBone.x + rmat1*yBone.y + rmat2*yBone.z;
						  ny =  rmat3*yBone.x + rmat4*yBone.y + rmat5*yBone.z;
						  nz =  rmat6*yBone.x + rmat7*yBone.y + rmat8*yBone.z;
						  yBone.x = nx; yBone.y = ny; yBone.z = nz;
						  nx =  rmat0*zBone.x + rmat1*zBone.y + rmat2*zBone.z;
						  ny =  rmat3*zBone.x + rmat4*zBone.y + rmat5*zBone.z;
						  nz =  rmat6*zBone.x + rmat7*zBone.y + rmat8*zBone.z;
						  zBone.x = nx; zBone.y = ny; zBone.z = nz;*/
}

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


void Rendering()
{	long int		gRow,gLayer,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16;
	int				sy,sz,y,z;
	float			dz0,dz1,dy0,dy1,dstep;
	float			c,c01y0,c01y1,c12y0,c12y1,c01z0,c01z1,c12z0,c12z1;
	float			wr,wg,wb;
	PixMapHandle	offPixMapHandle;  Boolean good;
	Ptr				image;
	int				i,rowBytes;


    offPixMapHandle = GetGWorldPixMap(window.gGWorld);
	
    good = LockPixels(offPixMapHandle);
	
	image =	GetPixBaseAddr(offPixMapHandle);
	rowBytes = ((**offPixMapHandle).rowBytes & 0x7fff);
	
	
	z=-1;
	dstep = 1.0 / (float)window.gridunit;				/* factor (0..1) change for one pixel */
	
	gRow   = (window.gsize+1);
	gLayer = gRow*gRow;
	
	
 	for (sz = 0; sz < window.offRect.right; sz++) { 					i=0; y=-1;
		if (sz%window.gridunit==0) { z++;  dz0=0; dz1=1; }
		
			c01z0 =  1.5*dz0*dz0*dz0 - 2.5*dz0*dz0 + 1;  								  /* bicubic function for 0<=x<1 */
			c01z1 =  1.5*dz1*dz1*dz1 - 2.5*dz1*dz1 + 1;
			c12z0 =  - 0.5*(dz0+1)*(dz0+1)*(dz0+1) + 2.5*(dz0+1)*(dz0+1) - 4*(dz0+1) + 2; /* bicubic function for 1<=x<2 */
			c12z1 =  - 0.5*(dz1+1)*(dz1+1)*(dz1+1) + 2.5*(dz1+1)*(dz1+1) - 4*(dz1+1) + 2;
			
		for (sy = 0; sy < window.offRect.right; sy++) {
			
			if (sy%window.gridunit==0) {		y++;  dy0=0; dy1=1;
				p1 = y*gRow + (z * gLayer);	p2 = p1+gRow;
				p3 = p1+gLayer;				p4 = p3+gRow;
				p5 = p1-gRow;		/* assign the 16 points for bicubic interpolation */
				p6 = p3-gRow;
				p7 = p1-gRow-gLayer;
				p8 = p3-gRow+gLayer;
				p9 = p2+gRow;
				p10= p4+gRow;
				p11= p2+gRow-gLayer;
				p12= p4+gRow+gLayer;
				p13= p1-gLayer;
				p14= p2-gLayer;
				p15= p3+gLayer;
				p16= p4+gLayer;
				if (z==0) 					{ p7=p5; p11=p9;  p13=p1;  p14=p2; }
				else if (z==window.gsize-1)	{ p8=p6; p12=p10; p15=p3;  p16=p4; }
				if (y==0) 					{ p5=p1; p6 =p3;  p7 =p13; p8 =p15; }
				else if (y==window.gsize)	{ p9=p2; p10=p4;  p11=p14; p12=p16; }
			}
		 
			/*wr =		dz1	*	(dy1*grid[p1].w + dy0*grid[p2].w)     ( linear interpolation )
				   + 	dz0	*	(dy1*grid[p3].w + dy0*grid[p4].w);
			wb =		dz1	*	(dy1*grid[p1+1].w + dy0*grid[p2+1].w)
				   + 	dz0	*	(dy1*grid[p3+1].w + dy0*grid[p4+1].w);*/
				   
			c01y0 =  1.5*dy0*dy0*dy0 - 2.5*dy0*dy0 + 1;									  /* bicubic function for 0<=x<1 */
			c01y1 =  1.5*dy1*dy1*dy1 - 2.5*dy1*dy1 + 1;
			c12y0 =  - 0.5*(dy0+1)*(dy0+1)*(dy0+1) + 2.5*(dy0+1)*(dy0+1) - 4*(dy0+1) + 2; /* bicubic function for 1<=x<2 */
			c12y1 =  - 0.5*(dy1+1)*(dy1+1)*(dy1+1) + 2.5*(dy1+1)*(dy1+1) - 4*(dy1+1) + 2;
			
			/*  0  0 */  c= c01y0 * c01z0; wr =  c * grid[p1].w;	wg =  c * grid[p1+1].w;		wb =  c * grid[p1+2].w;
			/*  1  0 */  c= c01y1 * c01z0; wr += c * grid[p2].w;	wg += c * grid[p2+1].w;		wb += c * grid[p2+2].w;
			/*  0  1 */  c= c01y0 * c01z1; wr += c * grid[p3].w;	wg += c * grid[p3+1].w;		wb += c * grid[p3+2].w;
			/*  1  1 */  c= c01y1 * c01z1; wr += c * grid[p4].w;	wg += c * grid[p4+1].w;		wb += c * grid[p4+2].w;
			
			/* -1  0 */  c= c12y0 * c01z0; wr += c * grid[p5].w;	wg += c * grid[p5+1].w;		wb += c * grid[p5+2].w;
			/* -1  1 */  c= c12y0 * c01z1; wr += c * grid[p6].w;	wg += c * grid[p6+1].w;		wb += c * grid[p6+2].w;
			/* -1 -1 */  c= c12y0 * c12z0; wr += c * grid[p7].w;	wg += c * grid[p7+1].w;		wb += c * grid[p7+2].w;
			/* -1  2 */  c= c12y0 * c12z1; wr += c * grid[p8].w;	wg += c * grid[p8+1].w;		wb += c * grid[p8+2].w;
			
			/*  2  0 */  c= c12y1 * c01z0; wr += c * grid[p9].w;	wg += c * grid[p9+1].w;		wb += c * grid[p9+2].w;
			/*  2  1 */  c= c12y1 * c01z1; wr += c * grid[p10].w;	wg += c * grid[p10+1].w;	wb += c * grid[p10+2].w;
			/*  2 -1 */  c= c12y1 * c12z0; wr += c * grid[p11].w;	wg += c * grid[p11+1].w;	wb += c * grid[p11+2].w;
			/*  2  2 */  c= c12y1 * c12z1; wr += c * grid[p12].w;	wg += c * grid[p12+1].w;	wb += c * grid[p12+2].w;
			
			/*  0 -1 */  c= c01y0 * c12z0; wr += c * grid[p13].w;	wg += c * grid[p13+1].w;	wb += c * grid[p13+2].w;
			/*  1 -1 */  c= c01y1 * c12z0; wr += c * grid[p14].w;	wg += c * grid[p14+1].w;	wb += c * grid[p14+2].w;
			/*  0  2 */  c= c01y0 * c12z1; wr += c * grid[p15].w;	wg += c * grid[p15+1].w;	wb += c * grid[p15+2].w;
			/*  1  2 */  c= c01y1 * c12z1; wr += c * grid[p16].w;	wg += c * grid[p16+1].w;	wb += c * grid[p16+2].w;
			if (wr>1) wr=1; if (wr<0) wr=0;
			if (wg>1) wg=1; if (wg<0) wg=0;
			if (wb>1) wb=1; if (wb<0) wb=0;
			
															/* set the pixel */
			*(image + i+1) = wb *255;		/* r */
			*(image + i+2) = wg *255;		/* g */
			*(image + i+3) = wr *255;		/* b */
			
			i += 4;
			
			dy0 += dstep; dy1 -= dstep;
		}
		image += rowBytes;
		dz0 += dstep; dz1 -= dstep;
	}
	image -= rowBytes; i -= 3*4;
	*(image+i+1)=100; *(image+i+2)=100; *(image+i+3)=100; i += 4;
	*(image+i+1)=100; *(image+i+2)=100; *(image+i+3)=100; i += 4;
	*(image+i+1)=100; *(image+i+2)=100; *(image+i+3)=100; i += 4;
	image -= rowBytes; i -= 4; *(image+i+1)=100; *(image+i+2)=100; *(image+i+3)=100; i += 4;
	image -= rowBytes; i -= 4; *(image+i+1)=100; *(image+i+2)=100; *(image+i+3)=100; i += 4;

	
	 
	UnlockPixels(offPixMapHandle);
	
	SetPort(window.windowP);
			 
	CopyBits( (BitMap*)*GetGWorldPixMap(window.gGWorld), &window.windowP->portBits,
	 &window.offRect, &window.windowP->portRect, srcCopy/*ditherCopy*/, 0l);
	
	DrawFuncs();
}

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


void DrawFuncs()
{	int cx;
	
	cx = window.picWidth / 2;

	/*MoveTo(cx,cx); RGBForeColor(&gui.greyc); 
	LineTo( cx + (xAxis.y*100), cx + (xAxis.z*100) );
	MoveTo(cx,cx); RGBForeColor(&gui.barc);
	LineTo( cx + (yAxis.y*100), cx + (yAxis.z*100) );
	MoveTo(cx,cx); RGBForeColor(&gui.greenc);
	LineTo( cx + (zAxis.y*100), cx + (zAxis.z*100) );*/
	
	RGBForeColor(&gui.blackc);
}

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


void initGrid(void)											/* initialisation of the grid */
{	int			x,y,z;
	long int	k;
	
	y=0; z=0; x=0;
 	do {
	
		k = x + y*(window.gsize+1) + z*(window.gsize+1)*(window.gsize+1);
 	  	grid[k].cx = (float)x-(window.gsize/2.0);
   		grid[k].cy = (float)y-(window.gsize/2.0);
   		grid[k].cz = (float)z-(window.gsize/2.0);
		
		grid[k].w = 1.0;
		
	   x++;
	   if (x>window.gsize) {  x=0; y++; if (y>window.gsize) {y=0; z++;}  }
	   
	} while (z<=window.gsize);
	
	xAxis.x=1; xAxis.y=0; xAxis.z=0;						/* reset coordinate system & rotation */
	yAxis.x=0; yAxis.y=1; yAxis.z=0;
	zAxis.x=0; zAxis.y=0; zAxis.z=1;
	rota=0.05;
	rotA.x= cos(0)*sin(-0.4); rotA.y= sin(0)*sin(0.4); rotA.z= cos(0.4);
	
	xBone.x=1; xBone.y=0; xBone.z=0;						/* reset coordinate system & rotation */
	yBone.x=0; yBone.y=1; yBone.z=0;
	zBone.x=0; zBone.y=0; zBone.z=1;
}

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


void growWindow(void)
{
		window.gridunit = window.picWidth / window.gsize / pixelRepeat;
		while (window.gridunit*window.gsize*pixelRepeat>window.picWidth)  window.gridunit--;
			
		SetRect(&window.offRect,0,0,window.gridunit * window.gsize,window.gridunit * window.gsize);
	
	DisposeGWorld( window.gGWorld );
	
	NewGWorld( &window.gGWorld, 32, &window.offRect, nil, nil, 0 );
	
	CopyBits(&window.windowP->portBits,(BitMap*)*GetGWorldPixMap(window.gGWorld),
		&window.windowP->portRect,&window.offRect,srcCopy,0l);
}

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



/*
**	Read menu descriptions from resource file into memory 
**	and store handles in myMenus array.
**	Insert into MenuBar and draw.
*/
void SetUpMenus(void)
{
	int i;

	myMenus[appleM] = GetMenu(appleID); 		/* read Apple menu from resource file */
	AppendResMenu(myMenus[appleM], 'DRVR');		/* add desk accessory names to Apple menu */
	myMenus[fileM] = GetMenu(fileID);			/* read file menu from resource file */
	myMenus[settingsM] = GetMenu(settingsID);

	for (i = 0; i < menuCount; i++) 
		InsertMenu(myMenus[i], 0); 				/* install menus in menu bar */
	
	DrawMenuBar();								/* and draw menu bar */
}



void Intro(void)
{	WindowPtr	windowP;
	PicHandle   introPic;
	Rect		r;
	long int 	t;
	
	windowP = GetNewCWindow(introWinID, nil, (WindowPtr) -1);		/* the intro window */
	SetPort(windowP);
	introPic = GetPicture(introPicID);	SetRect(&r,	0, 0,  348,348);
	DrawPicture(introPic, &r);		KillPicture(introPic);	ReleaseResource((Handle)introPic);
	
	DisposeHandle((Handle)introPic);
	t = TickCount();
	
	while ((TickCount()-t < 100) && !Button()) {}
	
	DisposeWindow(windowP);
}



void ChangeZoom(void)
{	DialogPtr	theDialog;
	short		itemHit;
	Str255		string;
	long int    number;
 	Handle item; short type; Rect r;

 theDialog = GetNewDialog(zoomDLOG, nil, (WindowPtr) -1);
 
 GetDialogItem(theDialog,3,&type,&item,&r); NumToString(window.zoom*10.01,string);   SetDialogItemText(item,string);
 GetDialogItem(theDialog,4,&type,&item,&r); NumToString(window.gsize,string);	     SetDialogItemText(item,string);
 GetDialogItem(theDialog,5,&type,&item,&r); NumToString(window.brightness*10,string);SetDialogItemText(item,string);
 
 do {
  ModalDialog(nil, &itemHit);
 } while ((itemHit!=1) && (itemHit!=2));	
 
  if (itemHit == 1) {
	  GetDialogItem(theDialog,3,&type,&item,&r); GetDialogItemText(item,string);
	  StringToNum(string,&number);
 	  if (number < 1) number = 1;
	if (number != (int)(window.zoom*10.01)) {
	window.zoom = number/10.0;
	window.zoomIsSet = true;
	calcMaxGrid();
	}
	
	  GetDialogItem(theDialog,4,&type,&item,&r); GetDialogItemText(item,string);
	  StringToNum(string,&number);
	  if (number%2==0)			number--;
 	  if (number < 2) 			number = 2;
 	  if (number > maxGSize)	number = maxGSize;
	if (number != window.gsize) {
	window.gsize = number; initGrid(); calcMaxGrid(); growWindow();  /* grid size has changed */
	}
	
	  GetDialogItem(theDialog,5,&type,&item,&r); GetDialogItemText(item,string);
	  StringToNum(string,&number);
 	  if (number < 1) 	 number = 1;
 	  if (number > 1000) number = 1000;
	window.brightness = number/10.0;
	
 	DisposeDialog(theDialog);
	
	calcGrid();
	
	Rendering();
  }
  else DisposeDialog(theDialog);
  
}



void ShowAboutMeDialog(void)
{
	DialogPtr	theDialog;
	short		itemHit;

	theDialog = GetNewDialog(aboutMeDLOG, nil, (WindowPtr) -1);
	ModalDialog(nil, &itemHit);
	DisposeDialog(theDialog);
}


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


void ShowInfo(void)
{	Str255	string;
	
  SetPort(infoWindow); EraseRect(&infoWindow->portRect);  
	
	DrawPicture(gui.header,  &gui.headerR);
	DrawPicture(gui.zUp,  	&gui.zUpR);
	DrawPicture(gui.zDn,  	&gui.zDnR);
	DrawPicture(gui.bUp,  	&gui.bUpR);
	DrawPicture(gui.bDn,  	&gui.bDnR);
	DrawPicture(gui.gUp,  	&gui.gUpR);
	DrawPicture(gui.gDn,  	&gui.gDnR);
	
	RGBForeColor(&gui.bc); PaintRect(&gui.orbsR); RGBForeColor(&gui.whitec); TextSize(9);
	
	DrawPicture(gui.add,  	&gui.addR);
	
	NumToString(fps,string); MoveTo(66,83); DrawString(string); DrawString("\p fps");
	
	MoveTo(5,110); DrawString("\pfilename:");
	MoveTo(5,126); DrawString(filename);
  
	RGBForeColor(&gui.blackc);

}

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


double sqr(double a)
{
 return(a*a);
}


void UpdateMenus(void)
{
	CheckItem(myMenus[settingsM], 4, autoRotate);
}


/*
**	Execute menu command specified by mResult,
**	the result of MenuSelect
*/
void DoCommand(long int mResult)
{
	short	theItem,							/* menu item number from mResult low-order word */
			theMenu;							/* menu number from mResult high-order word */
	Str255	name;								/* desk accessory name */
	int		temp;

	theItem = LoWord(mResult);					/* call Toolbox Utility routines to */
	theMenu = HiWord(mResult);					/* set menu item number and menu */

	switch (theMenu) {							/* switch on menu ID */

		case appleID:
			if (theItem == aboutMeCommand)
				ShowAboutMeDialog();
			else {
				GetMenuItemText(myMenus[appleM], theItem, name);
				temp = OpenDeskAcc(name);
			}
			break;

		case fileID:
			if (theItem == 1) {
				readfile();
			}
			else if (theItem == 3)
				doneFlag = true;
			break;

		case settingsID:
				
			if (theItem == 1) { ChangeZoom(); }	
			else if (theItem == 2) { 
				window.zoomIsSet = false;
   				window.gsize = 17; 
				window.brightness = 1;
				initGrid(); calcMaxGrid(); growWindow(); calcGrid(); Rendering(); }			
			
			else if (theItem == 4) {
				autoRotate = !autoRotate;
				if (autoRotate) {
					initGrid(); calcMaxGrid(); calcGrid(); Rendering();
				} else rota=0;
				UpdateMenus(); ShowInfo();
				}
			break;
	}

	HiliteMenu(0);								/* Unhighlight menu title */
												/* (highlighted by MenuSelect) */
}



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

void initWindow(void)
{	
	window.windowP = GetNewCWindow(drawWinID, nil, (WindowPtr) -1);
	SetPort(window.windowP);
	

   	window.gsize = 17;                /* grid size */
	initGrid();
	
	window.zoomIsSet = false;
	window.zoom = 2.8;
	
	window.brightness = 1;				/* used for brighter coloration */

	window.picWidth  = 357;
	
	ShowInfo();
	
	
	window.gridunit = window.picWidth / window.gsize / pixelRepeat;
	while (window.gridunit*window.gsize*pixelRepeat>window.picWidth)  window.gridunit--;
	
		
	SetRect(&window.offRect,0,0,window.gridunit * window.gsize,window.gridunit * window.gsize);
	
	NewGWorld( &window.gGWorld, 32, &window.offRect, nil, nil, 0 );
	
	
	CopyBits(&window.windowP->portBits,(BitMap*)*GetGWorldPixMap(window.gGWorld),
		&window.windowP->portRect,&window.offRect,srcCopy,0l);
	
}


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

void initGUI(void)
{	
	gui.normalCursor =	GetCursor(normalCursorID);
	gui.rotateCursor =	GetCursor(rotateCursorID);
	gui.growCursor   =	GetCursor(growCursorID);
	SetCursor(**&gui.normalCursor);
	
	SetRect(&dragRect, 4, 24, qd.screenBits.bounds.right - 4, qd.screenBits.bounds.bottom - 4);
	
	gui.blackc.red = 0; 	 gui.blackc.green = 0;      gui.blackc.blue = 0;
	gui.bc.red     = 15*255; gui.bc.green     = 15*255; gui.bc.blue     = 15*255;
	gui.barc.red   = 40*255; gui.barc.green   = 40*255; gui.barc.blue   = 40*255;
	gui.whitec.red = 65000;  gui.whitec.green = 65000;  gui.whitec.blue = 65000;
	gui.greyc.red  = 32000;	 gui.greyc.green  = 32000;  gui.greyc.blue  = 32000;
	gui.greenc.red = 0;		 gui.greenc.green = 32000;  gui.greenc.blue = 0;
	gui.bluec.red  = 13000;	 gui.bluec.green  = 31000;  gui.bluec.blue  = 41000;
		
	gui.header =		GetPicture(headerID);		SetRect(&gui.headerR,	0, 0, 100,22);
	
	gui.zUp =			GetPicture(zUpID);			SetRect(&gui.zUpR,	1, 38,34, 51);		gui.zUpA = GetPicture(zUpID+6);
	gui.zDn =			GetPicture(zDnID);			SetRect(&gui.zDnR,	1, 51,34, 64);		gui.zDnA = GetPicture(zDnID+6);
	gui.bUp =			GetPicture(bUpID);			SetRect(&gui.bUpR,	34,38,67, 51);		gui.bUpA = GetPicture(bUpID+6);
	gui.bDn =			GetPicture(bDnID);			SetRect(&gui.bDnR,	34,51,67, 64);		gui.bDnA = GetPicture(bDnID+6);
	gui.gUp =			GetPicture(gUpID);			SetRect(&gui.gUpR,	67,38,100,51);		gui.gUpA = GetPicture(gUpID+6);
	gui.gDn =			GetPicture(gDnID);			SetRect(&gui.gDnR,	67,51,100,64);		gui.gDnA = GetPicture(gDnID+6);
	
	SetRect(&gui.orbsR,0,73,100,1000);
	
	gui.add =			GetPicture(addID);			SetRect(&gui.addR,	 0, 73,25,85);

	SetRect(&gui.fpsRect,50,73,100,87);

}


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

void readfile(void)
{       Point			p;
		SFReply			reply;
        OSErr           err;
        SFTypeList      typeList;
		char *fname = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
	/* alcove */
	int  k,j,i;
		
  p.h = p.v = 200;
  typeList[0] = 'TEXT';
  SFGetFile(p, NULL, NULL, 1, typeList, NULL, &reply);
  
  err = SetVol(reply.fName, reply.vRefNum /* from sfgetfile */);	
	
	/********************/			
	
			  
						   
			  for (i = 0; i<reply.fName[0]; i++) { fname[i] = reply.fName[i+1]; filename[i+1] = reply.fName[i+1];}  /* translate to c string */
			  fname[i] = '\0'; filename[0] = reply.fName[0];
		
			  
	inF = fopen(fname, "r");
	
	free(a_a_out);
	for (k=0; k<nk; k++)  free(a_w[k]);
	free(a_a_hid);
	for (j=0; j<nj; j++) free(a_h[j]);
	free(a_h);
	free(a_a_in);
	free(a_a);
	free(a_t);

	fscanf(inF, "%i", &nk);
	fscanf(inF, "%i", &nj);
	fscanf(inF, "%i", &ni);
	
	a_a_out = (float *)malloc(nk * sizeof(float));
	a_w = (float **)malloc(nk * sizeof(float *));
	for (k=0; k<nk; k++)  a_w[k] = (float *)malloc(nj * sizeof(float));
	
	a_a_hid = (float *)malloc(nj * sizeof(float));
	a_h = (float **)malloc(nj * sizeof(float *));
	for (j=0; j<nj; j++)  a_h[j] = (float *)malloc(ni * sizeof(float));
		
	a_a_in = (float *)malloc(ni * sizeof(float));
	a_a = (float *)malloc(ni * sizeof(float));
	a_t = (float *)malloc(nk * sizeof(float));
		
	for (k=0; k<nk; k++) {							/* Initialisierung der Assoziationsgewichte */
		for (j=0; j<nj; j++) a_w[k][j] = 1.0/nj;
	}
	for (i=0; i<ni; i++) a_a[i] = 1;				/* Initialisierung der attention strengths */
	
	
	for (k=0; k<nk; k++) {
		fscanf(inF, "%f", &a_kcol[k].red);		a_kcol[k].red   /= 255.0;	/* category colors */
		fscanf(inF, "%f", &a_kcol[k].green);	a_kcol[k].green /= 255.0;
		fscanf(inF, "%f", &a_kcol[k].blue);		a_kcol[k].blue  /= 255.0;
	}

	for (j=0; j<nj; j++) {
		for (i=0; i<ni; i++) fscanf(inF, "%f", &a_h[j][i]);		/* knode positions */
	}
	
	
	fscanf(inF, "%i", &num_learn_steps);			/* num learning examples */
	
	learn_count = 0;
	
	learn_step();
	
  
  ShowInfo();
  calcMaxGrid(); calcGrid(); Rendering();

}

