/*
			BioMorph
			By:  Chris MacDonald & Steve Martin
			Version:  1.0
			Last Revision:	28 July 89


*/




#ifdef LATTICE
#include <proto/all.h>
#include <stdlib.h>
/* Prototypes for functions defined in biomorph.c */
int main(int argc,
	 unsigned char **argv);
void mousezoom(unsigned short code);
void genmorph(void);
void analyze(unsigned short Menunumber);
void open_all(void);
void close_all(void);
#endif
#include <exec/types.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include <graphics/gfxmacros.h>
#include <math.h>


/*
system functions
*/

extern struct Screen *OpenScreen ();
extern struct Window *OpenWindow();
extern struct Library *OpenLibrary();
extern struct ViewPort *ViewPortAddress();



/*
system variables
*/

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct MathBase *MathBase;
struct Screen *s;
struct Window *w;
struct IntuiMessage *msg;
struct RastPort *rp;
struct ViewPort *vp;

/*
menu stuff
*/

struct TextAttr Font =
{
	(STRPTR)"topaz.font",
	TOPAZ_SIXTY,
	FS_NORMAL,
	FPF_ROMFONT
};

struct IntuiText GenerateText = {4,1,JAM1,1,1,&Font,
				(UBYTE *)               "  Generate",NULL};
struct IntuiText StopText ={4,1,JAM1,1,1,&Font,(UBYTE *)"  Stop",NULL};
struct IntuiText QuitText ={4,1,JAM1,1,1,&Font,(UBYTE *)"  Quit",NULL};

struct IntuiText GreyText = {4,1,JAM1,1,1,&Font,(UBYTE *) "  Grey",NULL};
struct IntuiText RedText = {4,1,JAM1,1,1,&Font,(UBYTE *)  "  Red",NULL};
struct IntuiText GreenText = {4,1,JAM1,1,1,&Font,(UBYTE *)"  Green",NULL};
struct IntuiText BlueText = {4,1,JAM1,1,1,&Font,(UBYTE *) "  Blue",NULL};


struct IntuiText TwoText = {4,1,JAM1,1,1,&Font,(UBYTE *)    "   2",NULL};
struct IntuiText FourText = {4,1,JAM1,1,1,&Font,(UBYTE *)   "   4",NULL};
struct IntuiText EightText = {4,1,JAM1,1,1,&Font,(UBYTE *)  "   8",NULL};
struct IntuiText SixteenText = {4,1,JAM1,1,1,&Font,(UBYTE *)"  16",NULL};
struct IntuiText TwentyText = {4,1,JAM1,1,1,&Font,(UBYTE *) "  20",NULL};


struct IntuiText SquareText ={4,1,JAM1,1,1,&Font,(UBYTE *)"  z^2 + c",NULL};
struct IntuiText CubeText = {4,1,JAM1,1,1,&Font,(UBYTE *) "  z^3 + c",NULL};
struct IntuiText FifthText = {4,1,JAM1,1,1,&Font,(UBYTE *)"  z^5 + c",NULL};


struct MenuItem Quit =
{
	NULL,1,26,140,10,ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,
	0,(APTR) &QuitText,NULL,0x51,NULL,0
};

struct MenuItem Stop =
{
	&Quit,1,14,140,10,ITEMTEXT | ITEMENABLED | HIGHCOMP |
	CHECKIT | COMMSEQ,0xfffffff9,(APTR) &StopText,NULL,0x53,NULL,0
};

struct MenuItem Generate =
{
	&Stop,1,2,140,10,ITEMTEXT | ITEMENABLED | HIGHCOMP |
	CHECKIT | CHECKED | COMMSEQ,0xfffffffa,(APTR) &GenerateText,
	NULL,0x47,NULL,0
};


struct MenuItem Blue =
{
	NULL,1,38,70,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffff7,(APTR) &BlueText,NULL,0,NULL,0
};

struct MenuItem Green =
{
	&Blue,1,26,70,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffb,(APTR) &GreenText,NULL,0,NULL,0
};

struct MenuItem Red =
{
	&Green,1,14,70,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffd,(APTR) &RedText,NULL,0,NULL,0
};

struct MenuItem Grey =
{
	&Red,1,2,70,10,ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKED
	| CHECKIT,0xfffffffe,(APTR) &GreyText,NULL,0,NULL,0
};


struct MenuItem Twenty =
{
	NULL,1,50,60,10,ITEMTEXT | ITEMENABLED | HIGHCOMP | CHECKED
	| CHECKIT,0xffffffef,(APTR) &TwentyText,NULL,0,NULL,0
};

struct MenuItem Sixteen =
{
	&Twenty,1,38,60,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffff7,(APTR) &SixteenText,NULL,0,NULL,0
};

struct MenuItem Eight =
{
	&Sixteen,1,26,60,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffb,(APTR) &EightText,NULL,0,NULL,0
};

struct MenuItem Four =
{
	&Eight,1,14,60,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffd,(APTR) &FourText,NULL,0,NULL,0
};

struct MenuItem Two =
{
	&Four,1,2,60,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffe,(APTR) &TwoText,NULL,0,NULL,0
};


struct MenuItem Fifth =
{
	NULL,1,26,100,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffb,(APTR) &FifthText,NULL,0,NULL,0
};

struct MenuItem Cube =
{
	&Fifth,1,14,100,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKED | CHECKIT,0xfffffffd,(APTR) &CubeText,NULL,0,NULL,0
};

struct MenuItem Square =
{
	&Cube,1,2,100,10,ITEMTEXT | ITEMENABLED | HIGHCOMP
	| CHECKIT,0xfffffffe,(APTR) &SquareText,NULL,0,NULL,0
};




struct Menu Zx = {NULL,165,0,80,0,NULL,(BYTE *) "Equation",&Square};
struct Menu Zoom = {&Zx,115,0,50,0,NULL,(BYTE *) "Zoom",&Two};
struct Menu Colors = {&Zoom,55,0,60,0,MENUENABLED,(BYTE *) "Colors",&Grey};
struct Menu File = {&Colors,10,0,45,10,MENUENABLED,
			(BYTE *) "File",&Generate};



/*
voidoids
*/

void open_all(), close_all(), initmenu();
void addplex(),multplex(),sqrplex(),cubeplex(),analyze(),genmorph();
void mousezoom();



/*
window and screen  (windscreen?)
*/

struct NewScreen myScreen =
{
	0,0,
	320,200,
	4,
	1,0,
	0,
	CUSTOMSCREEN,
	NULL,
	(UBYTE *)"BioMorph 1.0",
	NULL,
	NULL
};


struct NewWindow myWindow =
{
      80,9,
      165, 160,
      0,
      1,
      MENUPICK | MOUSEBUTTONS,
      SMART_REFRESH | ACTIVATE | WINDOWSIZING |
      WINDOWDRAG | GIMMEZEROZERO,
      NULL,
      NULL,
      (UBYTE *)"z^3 + c",
      NULL,
      NULL,
      50, 40,
      210, 200,
      WBENCHSCREEN
};


/*
color maps
*/


UWORD greycolortable[] =
{
	0x004,0xfff,0x990,0x059,
	0x666,0x777,0x888,0x999,
	0xaaa,0xbbb,0xccc,0xddd,
	0xeee,0xfff,0x000
};

UWORD redcolortable[] =
{
	0x004,0xfff,0x990,0x059,
	0x600,0x700,0x800,0x900,
	0xa00,0xb00,0xc00,0xd00,
	0xe00,0xf00,0x000
};

UWORD greencolortable[] =
{
	0x004,0xfff,0x990,0x059,
	0x060,0x070,0x080,0x090,
	0x0a0,0x0b0,0x0c0,0x0d0,
	0x0e0,0x0f0,0x000
};

UWORD bluecolortable[] =
{
	0x004,0xfff,0x990,0x059,
	0x006,0x007,0x008,0x009,
	0x00a,0x00b,0x00c,0x00d,
	0x00e,0x00f,0x000
};

/*
IDCMP stuff
*/

struct IntuiMessage *message;
USHORT MessageClass, MessageCode, MousX, MousY;




/*
global variable descriptions


	creal,	  =	real & imaginary portion of
	cimag		constant

	magfactor =	magnification factor of graph

	fnum	  =	function power value (2 = square,
			3 = cube, 4 = z ^ 5)

	stopflag  =	flag which tells genmorph() to
			quit when Stop is selected from
			File menu

	minwinx,  =	minimum window size values, set
	minwiny 	to insure user can't size window
			down while generating graph

	winx,	  =	size of window user selects for
	winy		next graph

	mxstart,  =	upper left corner value for mouse
	mystart 	zoom box.  transferred to xstart, ystart
			in genmorph()

	xstart,   =	upper left corner value of last generated
	ystart		or currently generating graph

	increment =	distance between adjacent graph points

	size	  =	size in pixels of most recently computed
			or currently computing graph

*/


float creal, cimag, magfactor, xstart, ystart, mxstart, mystart, increment;
short fnum,stopflag,minwinx,minwiny,winx,winy,size;



main(argc,argv)
int argc;
UBYTE *argv[];
{
/*
user can enter a '?' or a complex constant
when starting the program
*/

	printf("\nBioMorph 1.0\n\nby Chris MacDonald & Steve Martin\n\n");

	if (argc == 1)    /* no arguments */
	{
		creal = 0.5;
		cimag = 0.0;

		printf("Using default constant:   real      = 0.5\n");
		printf("                          imaginary = 0.0\n\n");
		printf("Next time, try your own constant!\n\n");
		printf("Usage:  Biomorph (real) (imaginary)\n");
		printf("Where real and imaginary are a complex constant\n\n");
		Delay(125);
	}
	else if (argc == 2 && (*argv[1] == '?'))   /* user types '?' */
	{
		printf("\nUsage:  Biomorph (real) (imaginary)\n");
		printf("Where real and imaginary are a complex constant\n\n");
		exit(TRUE);
	}
	else
	{
		creal = atof(argv[1]);   /* user enters complex constant */
		cimag = 0.0;
		if(argc >= 3) cimag = atof(argv[2]);
		printf("\nComplex constant:\n");
		printf("real      = %f\n",creal);
		printf("imaginary = %f\n\n",cimag);
		Delay(130);
	}

	open_all();              /* opens libraries, screen and window */

	LoadRGB4(vp,&greycolortable[0],15);     /* load color table */

	SetRast(rp,3);                          /* clear window */

			/* default graph drawn when program starts */
	magfactor = 20;

			/*where upper left corner of graph should be */
	mxstart = -1.0 * magfactor / 2.0;
	mystart = mxstart;

	fnum = 3;			/* function is cube (z^3 + c) */
	genmorph();                     /* generate graph */


			/* loop which waits for user to access menu */
	while (1)
	{
		if((message = (struct IntuiMessage *)
			GetMsg(w->UserPort)) == NULL)
		{
			Wait(1L << w->UserPort->mp_SigBit);
			continue;
		}
		stopflag = 0;
		MessageClass = message->Class; /* get needed data */
		MessageCode = message->Code;
		MousX = w->GZZMouseX;
		MousY = w->GZZMouseY;
		ReplyMsg((void *)message);              /* respond to message */

		switch(MessageClass)
		{
			case MENUPICK : 	analyze(MessageCode);
			break;			       /* accessed menu */

			case MOUSEBUTTONS :	mousezoom(MessageCode);
			break;			 /* pressed mouse button */
		}

	}

}




/*
mousezoom()'s purpose is to generate a "zoom box" while mouse button is
pressed and held down.	When the button is released, the starting
coordinates and magnification of the selected box are computed.
*/

/*		mousezoom variable descriptions

	loop	=	flag indicating end of zoom
			box loop

	setx,	=	the starting window coordinates
	sety		of the zoom box

	mx	=	x distance mouse moves from setx
*/


void mousezoom(code)
USHORT code;		/* message code  */
{
	short loop = 1;
	USHORT mx,setx,sety;

	switch(code)
	{
		case SELECTDOWN :			/*left button down*/

			setx = MousX;		/*set starting point of*/
			sety = MousY;		/*zoom box	       */

			if(setx >= 215) setx = 0;  /*fixes problem when mouse*/
			if(sety >= 215) sety = 0;  /*points to left | up edge*/

			mx = w->GZZMouseX;	/*get current mouse x position*/

			SetWindowTitles(w,"Zooming!",(void *)-1);

			SetDrMd(rp,COMPLEMENT); /* "self erasing" mode*/
			SetAPen(rp,1);
			Move(rp,setx,sety);             /*  draw the zoom box */
			Draw(rp,mx,sety);
			Draw(rp,mx,sety + mx - setx);
			Draw(rp,setx,sety + mx -setx);
			Draw(rp,setx,sety);

			while (loop)    /*adjusts zoom box while button down*/

			{
				while((message = (struct IntuiMessage *)
					GetMsg(w->UserPort)) == NULL)
				{
					Move(rp,setx,sety);   /* erase old box */
					Draw(rp,mx,sety);
					Draw(rp,mx,sety + mx - setx);
					Draw(rp,setx,sety + mx -setx);
					Draw(rp,setx,sety);

					mx = w->GZZMouseX;  /* get new x value */

					/*stops dragging box to the left*/
					if((mx < setx) || (mx > 400)) mx = setx;

					Move(rp,setx,sety);  /* draw new box */
					Draw(rp,mx,sety);
					Draw(rp,mx,sety + mx - setx);
					Draw(rp,setx,sety + mx -setx);
					Draw(rp,setx,sety);

				}
				MessageClass = message->Class; /*drops out of loop*/
				MessageCode = message->Code;   /*when a message   */
				ReplyMsg((void *)message);             /*occurs           */

				switch(MessageClass)

				{
					case MOUSEBUTTONS:

					switch(MessageCode)

					{
					case SELECTUP:	 /* user releases button */

				/* set new mag & position based on zoom box */
					magfactor =(mx - setx) * increment;
					mxstart = xstart +
						  ((setx - 2) * increment);
					mystart = ystart +
						  ((sety - 1) * increment);

					SetWindowTitles(w,"Got it.",(void *) -1);
					Delay(20);

					switch(fnum)  /* put window title back */
					{
					case 2:
					SetWindowTitles(w,"z^2 + c",(void *) -1);
					break;

					case 3:
					SetWindowTitles(w,"z^3 + c",(void *) -1);
					break;

					case 4:
					SetWindowTitles(w,"z^5 + c",(void *) -1);
					break;
					}

					Move(rp,setx,sety);  /* erase box */
					Draw(rp,mx,sety);
					Draw(rp,mx,sety + mx - setx);
					Draw(rp,setx,sety + mx -setx);
					Draw(rp,setx,sety);

					loop = 0;   /* set flag to end loop */
					break;
					}
					break;

				}
			}
	}
}



void genmorph()
{

/*
	x,	=	graph loop vars. derived from
	y		current window size

	n	=	iteration loop variable

	value	=	contains result of window sizing
			call

	zreal,	=	current equation coordinates
	zimag

	a, b	=	temporary storage for math routines

*/


	short x,y,n,value;
	float zreal,zimag,a,b;

	SetDrMd(rp,JAM1);

	stopflag = 0;	/* flag to stop generating */

	/* setting limits of window size */
	winx = w->Width;
	winy = w->Height;
	value = WindowLimits(w,winx,winy,210,200);

	/* how large the graph should be, based on window size */
	if(winx < winy)
	{
		size = winx - 8;
	}
	else
	{
		size = winy - 8;
	}

	/* how much zreal&imag should be incremented each
	   time through loop				  */
	increment = magfactor / size;

	xstart = mxstart;	/* assign mousezoom() values */
	ystart = mystart;


	for(y = 0; y <= size - 6; ++y)   /* y graph loop */
	{
		for(x = 0; x <= size; ++x)   /* x graph loop */
		{
			/* setting z value for current coordinates */
			zreal = ((float) xstart + (increment * x));
			zimag = ((float) ystart + (increment * y));

			/* iteration loop */
			for(n = 1; n <= 10; ++n)
			{
				a = zreal;
				b = zimag;

				if (fnum == 2)    /* square */
				{
					zreal = a*a - b*b + creal;
					zimag = 2*a*b + cimag;
				}

				else if (fnum == 3)   /* cube */
				{
					zreal = a*(a*a - 3*b*b) + creal;
					zimag = b*(3*a*a - b*b) + cimag;
				}

				else if (fnum == 4)   /* fifth */
				{
					a = zreal;
					b = zimag;

		zreal = a * (a*a*a*a - 10*a*a*b*b + 5*b*b*b*b) + creal;
		zimag = b * (5*a*a*a*a - 10*a*a*b*b + b*b*b*b) + cimag;
				}


	/* break out of loop if result is ever greater than ten */

				if(fabs(zreal) > 10 || fabs(zimag) > 10 ||
					(zreal*zreal + zimag*zimag) > 100)
					break;
			}


	/* setting color of pixel based on value of z after iteration */

			if(!(fabs(zreal) < 10 || fabs(zimag) < 10))
			{
				SetAPen(rp,14);
			}
			else if(fabs(zreal) < fabs(zimag))
			{
				SetAPen(rp,((short) fabs(zreal)) + 4);
			}
			else
			{
				SetAPen(rp,((short) fabs(zimag)) + 4);
			}
			WritePixel(rp,x,y);
		}


		/* test for menu access at end of each x loop */
		/* clears all messages except newest	      */
		MessageClass = NULL;
		while(message = (struct IntuiMessage *)GetMsg(w->UserPort))
		{
			MessageClass = message->Class;
			MessageCode = message->Code;
			ReplyMsg((void *)message);
		}

		if(MessageClass)
		{
			switch(MessageClass)
			{
				case MENUPICK : analyze(MessageCode);
				break;
			}
		}


		/* quit generating if user picks Stop from File menu */
		if(stopflag)
		{
			stopflag = 0;
			x = size;
			y = size - 6;
		}

	}

	/* set window size limits back to normal */
	value = WindowLimits(w,50,40,210,200);

	/* enable zoom and power menus */
	ClearMenuStrip(w);
	Zx.Flags = MENUENABLED;
	Zoom.Flags = MENUENABLED;
	Stop.Flags |= CHECKED;	     /* set Stop checkmark */
	Generate.Flags = ITEMTEXT | ITEMENABLED | HIGHCOMP
	| COMMSEQ | CHECKIT;	     /* unset Generate checkmark */
	SetMenuStrip(w,&File);
	DisplayBeep(NULL);           /* flash screen when done */

}







/*
analyze():  responds to user menu access
*/

void analyze(Menunumber)
USHORT Menunumber;
{
	USHORT Menu, MenuItem;

	Menu = MENUNUM(Menunumber);
	MenuItem = ITEMNUM(Menunumber);

	switch(Menu)
	{
		case 0	: /* File Menu */
			switch(MenuItem)
			{
				case 0	: /* Generate */
					if (!(stopflag))
					{
						SetRast(rp,0);
						ClearMenuStrip(w);
						Zoom.Flags = NULL;
						Zx.Flags = NULL;
						SetMenuStrip(w,&File);
						genmorph();
					}
					break;

				case 1	: /* Stop */
					stopflag = 1;
					ClearMenuStrip(w);
					Zoom.Flags = MENUENABLED;
					Zx.Flags = MENUENABLED;
					SetMenuStrip(w,&File);
					break;

				case 2	: /* Quit */
					close_all();
					exit(TRUE);
					break;
			}
		break;

		case 1	: /* Colors */
			switch(MenuItem)
			{
				case 0	: /* Grey */
					LoadRGB4(vp,&greycolortable[0],15);
					break;

				case 1	: /* Red */
					LoadRGB4(vp,&redcolortable[0],15);
					break;

				case 2	: /* Green */
					LoadRGB4(vp,&greencolortable[0],15);
					break;

				case 3	: /* Blue */
					LoadRGB4(vp,&bluecolortable[0],15);
					break;
			}
		break;

		case 2	: /* Zoom */
			switch(MenuItem)
			{
				case 0	:
					magfactor = 2;
					mxstart = -1.0 * magfactor / 2.0;
					mystart = mxstart;
					break;

				case 1	:
					magfactor = 4;
					mxstart = -1.0 * magfactor / 2.0;
					mystart = mxstart;
					break;

				case 2	:
					magfactor = 8;
					mxstart = -1.0 * magfactor / 2.0;
					mystart = mxstart;
					break;

				case 3	:
					magfactor = 16;
					mxstart = -1.0 * magfactor / 2.0;
					mystart = mxstart;
					break;

				case 4	:
					magfactor = 20;
					mxstart = -1.0 * magfactor / 2.0;
					mystart = mxstart;
					break;
			}
		break;

		case 3	: /* Power */
			switch(MenuItem)
			{
				case 0	:
					fnum = 2;
					SetWindowTitles(w,"z^2 + c",(void *) -1);
					break;

				case 1	:
					fnum = 3;
					SetWindowTitles(w,"z^3 + c",(void *) -1);
					break;

				case 2	:
					fnum = 4;
					SetWindowTitles(w,"z^5 + c",(void *) -1);
					break;
			}
		break;

	}
}



void open_all()
{
	if((IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 0L)) == NULL)
			exit(1);
	if((MathBase = (struct MathBase *)
		OpenLibrary("mathffp.library", 0L)) == NULL)
			exit(1);
	if ((GfxBase = (struct GfxBase *)
		OpenLibrary("graphics.library", 0L)) == NULL)
			exit(1);

if ((s = OpenScreen(&myScreen)) == NULL)
	exit(1);

vp = &(s -> ViewPort);

myWindow.Screen = s;
myWindow.Type = CUSTOMSCREEN;

	if ((w = OpenWindow(&myWindow)) == NULL)
		exit(1);
	rp = w->RPort;

	SetMenuStrip(w,&File);
}




void close_all()
{
	while(message = (struct IntuiMessage *)GetMsg(w->UserPort))
	{
		ReplyMsg((void *)message);
	}

	if (w) ClearMenuStrip(w);
	if (w) CloseWindow(w);
	if (s) CloseScreen(s);
	if (GfxBase) CloseLibrary(GfxBase);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
}

