/*
 * JuliaIIM -- Quick a dirty way to look at the Julia Set
 *
 * April 27, 89 --
 *	Clean up for public distribution.
 *	Menus are generated using the "qmenu" package
 *	Requester is genrated using "blk"
 * 		both packages are written by
 *				Stuart Ferguson		1/89
 *				(shf@well.UUCP)
 *		and are availabe in the Public Domain.
 *	The scaling of the pictures left much to be desired.
 *  One thing I really would like to work on is the "Imanginary Map".
 *	I would like to draw a Mandelbrot picture there and let the user
 * 	pick the value by clicking on the Mandelbrot map. This will give a
 *	very nice illustration that the Mandelbrot picture is really a 
 *	dictionary of the Julia Set.
 *	I had planned to work on this some more but finals are coming up.
 *
 *	You may use this codes if they are useful to you.
 *
 * compiles using 3.4a Manx
 * 1> cc JuliaIIM.c
 * 1> ln JuliaIIM.o qmenu.o -lm -lc
 */
#include "functions.h"
#include "exec/types.h"
#include "graphics/gfxmacros.h"
#include "intuition/intuition.h"
#include "math.h"

#define INTUITION_REV 0L
#define GRAPHICS_REV 0L
/* Write out Text */
#define MyText(r,x,y,b) Move((r),(x), (y)); Text((r), (b), (long) strlen(b))
/* Clear Screen */
#define MyClear(r,x,y) SetAPen((r), 0L); RectFill((r), 0L, 0L, (x), (y))

/* external for qmenu.o and request.h */
struct TextAttr ta = { (UBYTE*) "topaz.font", 8,0,0 };

/* global variables */
struct IntuitionBase *IntuitionBase = 0L;
struct GfxBase *GfxBase = 0L;
struct Window *Window = 0L;
struct RastPort *RastPort = 0L;

struct NewWindow nw =
{
	50, 10,			/* leftedge, topedge */
	340, 150,		/* width, height */
	-1, -1,			/* detail pen, block pen (default) */
	CLOSEWINDOW | MENUPICK | MOUSEBUTTONS | REQCLEAR | GADGETDOWN,	/* IDCMP Flag *

	SMART_REFRESH | WINDOWCLOSE | WINDOWDRAG | WINDOWDEPTH | ACTIVATE | GIMMEZEROZ
RO | WINDOWSIZING,
	NULL, NULL,		/* first gadget, check mark */ 
	(UBYTE *) "Julia IIM Window",		/* title */
	NULL, NULL,		/* screen, bitmap */
	100, 50,		/* minwidth, minheight */
	640, 200,		/* maxwidth, maxheight */
	WBENCHSCREEN	/* type */
};

#define PI 3.14159
#define HALF_PI 1.57079
#define X_RES 640.0
#define Y_RES 200.0

/* global variables */
UBYTE Buf[20];
double x, y, cx, cy;
int offset_width, offset_height;
int MaxDots;
STATIC BOOL Stop, Show, Clear, Pixel_Mode;

/* gadget */
#define CAN_ID  0x80 
#define OK_ID	0x81  
#define STR_ID  0x82 
#define NUMCHR 20
UBYTE undo[NUMCHR];
#define REQ mot_req
#define REQ_BUF mot_nbuf
#include "request.h"
/* gadget end */

/* Menu Stuffs */
#include "qmenu.h"

#define NILSUB	{NULL,NULL}

/* STOP MENU */
#define STOP 0
char *ms_str[] = 
{
	"!=SStop Calculation",
	NULL 
};

struct NewMenu ms_sub[] = 
{
	NILSUB,
};

char *stop_str[] = 
{
	"Julia IIM",
	NULL 
};

struct NewMenu stop_sub[] = 
{
	{ ms_str, ms_sub },
};

struct NewMenu stop_menu = { stop_str, stop_sub };
struct Menu *MenuStop;

/* MAIN MENU */

/* MENU PROJECT */
/* Menu Project About Strings */
char *mpa_str[] =
{
	"!bHung Le (mott@ucscb.UCSC.EDU)",
	NULL
};

/* Menu Project Strings */
#define NEW 0
#define ABOUT 1
/* 2 is the line separating ABOUT and QUIT */
#define QUIT 3
char *mp_str[] = 
{
	"!=NNew",
	"About",
	"-",
	"!=QQuit",
	NULL 
};

/* Menu Project SubMenu */
struct NewMenu mp_sub[] = 
{
	NILSUB,
	{ mpa_str, NULL },
	NILSUB,
};

/* OPTIONS MENU */
/* Menu Option Count Strings */
char *moc_str[]=
{
	"!c!0111111   100",
	"!c!1011111   500",
	"!+!1101111  1000",
	"!c!1110111  2000",
	"!c!1111011  3000",
	"!c!1111101  5000",
	"!c!1111110 10000",
	NULL
};

#define MODE_PIXEL 0
#define MODE_CIRCLE 1
char *mom_str[] =
{
	"!+!01Pixel",
	"!c!10Circle",
	NULL
};
/* Menu Option Strings */
#define PIXEL_COUNTS 0
#define PIXEL_MODE 1
#define SHOW_PARS 2
#define CLEAR_SCREEN 3
#define ENTER_C 4
#define IMAGINARY_MAP 5
char *mo_str[] = {
	"Pixels Count",
	"Pixel Mode",
	"!+!t!=SShow Parameters",
	"!+!t!=CClear Screen",
	"!=PEnter C parameters ...",
	"!=MImaginary Map",
	NULL 
};

/* Menu Option SubMenus */
struct NewMenu mo_sub[] = {
	{ moc_str, NULL },
	{ mom_str, NULL },
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
};
/* PRESET MENU */
#define  CIRCLE 1 
#define  TRIEYES 2 
#define  INVERSE_S 3 
#define  CRACK 4 
#define  FOURSOME 5 
#define  TWIST  6 
#define  DIAMOND 7 

/* These names reflects somewhat the state of my mind during
   the early morning hours
 */
char * mpreset_str[] =
{
	"!=1Circle",
	"!=2TriEyes",
	"!=3Inverser S",
	"!=4Crack",
	"!=5Foursome",
	"!=6Twist",
	"!=7Diamond",
	"!=8Siegel Disk",
	NULL
};

struct NewMenu mpreset_sub[] =
{
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
	NILSUB,
};

/* MAIN MENU */
/* Menu Main String */
char *main_str[] = {
	"Project",
	"Options",
	"Presets",
	NULL 
};

/* Menu Main SubMenu */
#define PROJECT 0
#define OPTION 1
#define PRESETS 2
struct NewMenu main_sub[] = {
	{ mp_str, mp_sub},
	{ mo_str, mo_sub},
	{ mpreset_str, mpreset_sub},
};

struct NewMenu main_menu = { main_str, main_sub};
struct Menu *MenuMain;

/* Menu ends */
struct data
{
	double x, y;
};
struct data presets[8] =
{
	/* #define  CIRCLE 1 */ 
	{ 0.00, 0.00 },
	/* #define  TRIEYES 2 */ 
	{ -1.00, 0.00 },
	/* #define  INVERSE_S 3  */
	{ 0.2 , 0.3 },
	/* #define  CRACK 4  */
	{ 0.00, 1.00 },
	/* #define  FOURSOME 5  */
	{ 0.25, 0.00 },
	/* #define  TWIST  6  */
	{ -0.9, 0.12 },
	/* #define  DIAMOND 7  */
	{ -0.3, 0.00 },
	/* #define SEIGEL_DISK 8 */
	{ -0.39054, -0.58679 },
};

/* for MANX */
_cli_parse() {}
_wb_parse() {}

main()
{

	/* initialize data */
	cx =  0.2;
	cy =  0.3;
	x  =  y = 1.0;
	MaxDots = 1000;
	offset_width = 0;
	offset_height = 0;
	sprintf(REQ_BUF[0], "%8f", cx);
	sprintf(REQ_BUF[1], "%8f", cy);

	Stop = FALSE;
	Show = Clear = Pixel_Mode = TRUE;

	/* open libraries */
	open_libs();

	/* open display window */
	if ( (Window = (struct Window *)OpenWindow(&nw) ) == NULL)
		clean_up();

	/* using "qmenu" package. Thanks Stuart ! */
	if (!(MenuStop = GenMenu (&stop_menu))) 
		clean_up();
	if (!(MenuMain = GenMenu (&main_menu))) 
		clean_up();

	RastPort = Window->RPort;

	SetAPen(RastPort, 1L);
	Julia();

	while(1)
	{
		Wait( 1L << Window->UserPort->mp_SigBit);
		HandleEvent();
	}
}

/*
 * does the Julia set
 */
Julia()
{
	int i;
	double m, n;
	UBYTE buffer[81];

	/* Set offset */
	offset_width = (X_RES - Window->Width) / 2;
	offset_height = (Y_RES - Window->Height) / 2;
	ClearMenuStrip(Window);
	SetMenuStrip (Window, MenuStop);
	Stop = FALSE;

	/* Just want be sure that Z is in the Julia Set 
	   so we discard the first 10 terms
	 */
	for (i = 0; i < 10 ; i++)
		Iteration();

	if (Show)
	{
		sprintf(buffer, "f(z) = z^2 + ((%8f) + (%8f)i)", cx, cy);
		MyText(RastPort, 2L, 10L, buffer);
		sprintf(buffer, "Current Dot: ");
		MyText(RastPort, 2L, 20L, buffer);
	}

	for (i = 0; ((i < MaxDots) && (Stop == FALSE)) ; i++)
	{
		HandleStopEvent();
		Iteration();
		Scale(&m, &n);
		Center(&m, &n);
		if (Show)
		{
			sprintf(buffer, "%8d", i);
			MyText(RastPort, 100L, 20L, buffer);
		}
		if (Pixel_Mode)
			WritePixel(RastPort, (long)m, (long)n);
		else
			DrawCircle(RastPort, (long) m, (long) n, 1L);
	}
	ClearMenuStrip(Window);
	SetMenuStrip(Window, MenuMain);
}

/*
 * The main inverse iteration loop
 */
Iteration()
{
		double wx, wy, radius, theta;
		extern double x, y, dx, dy;

		wx = x - cx;
		wy = y - cy;

		/* current theta */
		theta = atan(wy / wx);
		if (wx < 0)
			/* rotate the angle */
			theta = PI + theta;
		else if (wx == 0)
			theta = HALF_PI;

		/* does square root */
		/* for the angle, all we need to do is to take half of the angle */
		theta = theta/2;
		/* magnitude of Z */
		radius = (sqrt(wx*wx + wy*wy));
		/* for sake of different paths */
		if (ran() > 0.5)
			radius = -sqrt(radius);
		else
			radius = sqrt(radius);

		/* new Z */
		x = radius * cos(theta);
		y = radius * sin(theta);
}

/*
 * Scale(int *m, *n)
 * 	. scale the two global variables x, y (world coordinates)
 *    to m, n (display coordinates) 
 */
Scale(m, n)
double *m, *n;
{
		extern double x, y;

		*m = ((x+4) * X_RES / 8);
		*n = ((2 - y) * Y_RES / 4);
}

/*
 * Center the picture
 */
Center(m,n)
double *m, *n;
{
	extern int offset_width, offset_height;

	*m = *m - offset_width + 0.77;
	*n = *n - offset_height;
}

/*
 * clean up intuition, try to do the right things
 */
clean_up()
{
	struct IntuiMessage *messg;

	/* Window is the the main and only window */
	if (Window)
	{
		/* Drain the IDCMP */
		while (messg = (struct IntuiMessage *) (GetMsg (Window->UserPort)))
			ReplyMsg(messg);
		if (MenuStop) FreeMenu (MenuStop);
		if (MenuMain) FreeMenu (MenuMain);
		ClearMenuStrip(Window);
		CloseWindow(Window);
	}

	/* Close down libraries */
	if (GfxBase) 		CloseLibrary(GfxBase);
	if (IntuitionBase) 	CloseLibrary(IntuitionBase);

	exit(FALSE);
}

/* take care of IDCMP */
HandleEvent()
{
	struct IntuiMessage *imessg;

	/* while there are messages take look at */
	while (imessg = (struct IntuiMessage *) (GetMsg (Window->UserPort)))
	{
		/* make a copy */
		ULONG class = imessg->Class;
		USHORT code = imessg->Code;
		/* reply to it */
		ReplyMsg(imessg);
		/* identify message */
		switch(class)
		{
			case CLOSEWINDOW:
				clean_up();
				break;
			case MENUPICK:
				/* in case user does several things at once */
				while (code != MENUNULL)
				{
					do_menu(code);
					code = ItemAddress(MenuMain, code)->NextSelect;
				};
				break;

			default:
				break;
		}
	}
}

do_menu(code)
USHORT code;
{
	switch(MENUNUM(code))
	{
		case PROJECT:
			do_project(code);
			break;

		case OPTION:
			do_option(code);
			break;

		case PRESETS:
			do_presets(code);
			break;

		default:
			break;
	}
}
do_presets(code)
USHORT code;
{
	int item;
	item = ITEMNUM(code);
	cx = presets[item].x; 
	cy = presets[item].y; 
	sprintf(REQ_BUF[0], "%8f", cx);
	sprintf(REQ_BUF[1], "%8f", cy);
	if (Clear)
	{
		MyClear(RastPort, (long) Window->Width, (long) Window->Height);
	}
	SetAPen(RastPort, 1L);
	Julia();
}

do_project(code)
USHORT code;
{
	switch(ITEMNUM(code))
	{
		case NEW:
			if (Clear)
			{
				MyClear(RastPort, (long) Window->Width, (long) Window->Height);
			}
			SetAPen(RastPort, 1L);
			Julia();
			break;

		case QUIT:
			clean_up();
			break;

		case ABOUT:
		default:
			break;
	}
}

do_option(code)
USHORT code;
{
	struct MenuItem *mi;

	switch(ITEMNUM(code))
	{
		case PIXEL_COUNTS:
			mi = (MenuMain[OPTION].FirstItem[PIXEL_COUNTS].SubItem);
			if (mi[0].Flags & CHECKED)
				MaxDots =   100;
			else if (mi[1].Flags & CHECKED)
				MaxDots =   500;
			else if (mi[2].Flags & CHECKED)
				MaxDots =   1000;
			else if (mi[3].Flags & CHECKED)
				MaxDots =   2000;
			else if (mi[4].Flags & CHECKED)
				MaxDots =   3000;
			else if (mi[5].Flags & CHECKED)
				MaxDots =   5000;
			else if (mi[6].Flags & CHECKED)
				MaxDots =   10000;
			break;

		case PIXEL_MODE:
			mi = (MenuMain[OPTION].FirstItem[PIXEL_MODE].SubItem);
			if (mi[MODE_PIXEL].Flags & CHECKED)
				Pixel_Mode = TRUE;
			else if (mi[MODE_CIRCLE].Flags & CHECKED)
				Pixel_Mode = FALSE;
			break;

		case SHOW_PARS:
			if (MenuMain[OPTION].FirstItem[SHOW_PARS].Flags & CHECKED)
				Show = TRUE;
			else
				Show= FALSE;
			break;

		case CLEAR_SCREEN:
			if (MenuMain[OPTION].FirstItem[CLEAR_SCREEN].Flags & CHECKED)
				Clear = TRUE;
			else
				Clear = FALSE;
			break;

		case ENTER_C:
			enter_c();
			break;

		case IMAGINARY_MAP:
			imaginary_map();
			break;

		default:
			break;
	}
}

imaginary_map()
{
	int width, height;
	struct IntuiMessage *imessg;
	float new_cx, new_cy;

	width = Window->Width;
	height = Window->Height;

	/* Clear the Screen */
	MyClear(RastPort, (long) width, (long) height);

	SetAPen(RastPort, 3L);
	MyText(RastPort, 5L, 10L, "Click on plane for a C value");

	/* Draw Axis for now */
	SetAPen(RastPort, 2L);
	Move(RastPort, 2L, (long) (Window->Height / 2));
	Draw(RastPort, (long) (Window->Width), (long)(Window->Height / 2));
	Move(RastPort, (long) (Window->Width / 2), 10L);
	Draw(RastPort, (long) (Window->Width / 2), (long)(Window->Height - 2));

	Wait( 1L << Window->UserPort->mp_SigBit);
	/* while there are messages take look at */
	while (imessg = (struct IntuiMessage *) (GetMsg (Window->UserPort)))
	{
		/* make a copy */
		ULONG class = imessg->Class;
		USHORT code = imessg->Code;
		SHORT mouseX = imessg->MouseX;
		SHORT mouseY = imessg->MouseY;
		/* reply to it */
		ReplyMsg(imessg);
		/* identify message */
		switch(class)
		{
			case MOUSEBUTTONS:
				if (code == SELECTDOWN)
				{
					/* Decenter */
					new_cx = mouseX + offset_width - 0.77;
					new_cy = mouseY + offset_height - 10;
					/* Descale */
					new_cx = (new_cx * ( 8 / X_RES)) - 4;
					new_cy = -((new_cy * ( 4 / Y_RES)) - 2);
					/*
					printf("new cx %f; new cy %f\n", new_cx, new_cy);
					*/
					cx = new_cx;
					cy = new_cy;
					sprintf(REQ_BUF[0], "%8f", cx);
					sprintf(REQ_BUF[1], "%8f", cy);
					if (Clear)
					{
						MyClear(RastPort, (long) Window->Width, (long) Window->Height);
					}
					SetAPen(RastPort, 1L);
					Julia();
				}
				break;

			default:
				break;
		}
	}
}

enter_c()
{

/* Here come gadget */
   struct IntuiMessage *im;

   BOOL looping = TRUE;
   ULONG class;
   struct Gadget *gadget;

   REQ.LeftEdge = 5;
   REQ.TopEdge = 12;
   Request (&REQ,Window);

   while (looping) 
   {
     if ((im = (struct IntuiMessage *) GetMsg(Window->UserPort)) == 0L)
	 {
	 	Wait(1L << Window->UserPort->mp_SigBit);
		continue;
	 }
        class = im->Class;
		gadget = (struct Gadget *) im->IAddress;
        ReplyMsg (im);
		if (class == REQCLEAR)
			looping = FALSE;
		if (class == GADGETDOWN)
		{
			switch(gadget->GadgetID)
			{
				case OK_ID:
					if (sscanf(REQ_BUF[0], "%lf", &cx) && sscanf(REQ_BUF[1], "%lf", &cy))
					;
					else
					{
						sprintf(REQ_BUF[0], "%8f", cx);
						sprintf(REQ_BUF[1], "%8f", cy);
					}
					break;

				case CAN_ID:
					sprintf(REQ_BUF[0], "%8f", cx);
					sprintf(REQ_BUF[1], "%8f", cy);
					break;
			}
		}
	}
	while (im = (struct IntuiMessage *) GetMsg(Window->UserPort))
	 	ReplyMsg(im);
}
/* End gadget */

HandleStopEvent()
{
	struct IntuiMessage *imessg;

	/* while there are messages take look at */
	while (imessg = (struct IntuiMessage *) (GetMsg (Window->UserPort)))
	{
		/* make a copy */
		ULONG class = imessg->Class;
		USHORT code = imessg->Code;
		/* reply to it */
		ReplyMsg(imessg);
		/* identify message */
		switch(class)
		{
			case MENUPICK:
				if (MENUNUM(code) != MENUNULL)
					if ((MENUNUM(code) == STOP) && (ITEMNUM(code) == STOP))
						Stop = TRUE;
				break;
			default:
				Stop = FALSE;
				break;
		}
	}
}

/* open Intuition and Graphics libraries */
open_libs()
{
	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", INTU
TION_REV);
	if (IntuitionBase == NULL)	clean_up();

	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", GRAPHICS_REV);
	if (GfxBase == NULL) 		clean_up();
}
