/************************************************************************
*  Version 3.00  MANDELBROT - Self-Squared Dragon Generator  29-May-86  *
*  Commodore Amiga         Graphics Routines                   MAND1.C  *
*************************************************************************
*         Copyright (c) 1986, Robert S. French and R. J. Mical          *
* --------------------------------------------------------------------- *
*  This program has been placed in the public domain.  A limited        *
*  license is hereby granted for the unlimited use and distribution of  *
*  this program, provided it is not used for commercial or profit-      *
*  making purposes.  Thank you.                                         *
*************************************************************************
*  Author information:            | Name:   R. J. Mical                 *
*                                 | USnail: Commodore-Amiga, Inc.       *
*  Name:   Robert S. French       |         983 University Avenue       *
*  USnail: 2740 Frankfort Avenue  |         Los Gatos, CA  95030        *
*          Louisville, KY  40206  \-------------------------------------*
*  Phone:  (502) 897-5096              UUCP: ihnp4!ptsfa!well!french    *
*  ARPA:   French#Robert%d@LLL-MFE or RFrench@MIT-MULTICS               *
*************************************************************************
*  Please send any comments, suggestions, or bugs to one of the above   *
*  addresses.                                                           *
************************************************************************/

#include "mand.h"
#include "globals.h"

int start_x,last_y,last_color;


/*----------------*/
/* Color routines */

init_colors()
{
	switch (color_set) {
		case 0: init_cincr(); break;
		case 1: init_c7rot(); break;
		case 2: copy_userpalette(); break;
	}
}

init_cincr()
/* this routine initializes the color table with sequential values from
* 0 to 4095
*/
{
	UWORD i;

	for (i=0;i<4096;i++)
		*(color_table + i) = i;
}


init_c7rot()
/* this routine initializes the color table with this pattern:
* 0 = 0
* 1-15 = unit steps of blue
* 16-30 = unit steps of green
* 31-45 = unit steps of red
* 46-60 = unit steps of cyan (blue and green)
* 61-75 = unit steps of magenta (blue and red)
* 76-90 = unit steps of yellow (green and red)
* 91-115 = unit steps of white (all colors)
*/
{
	UWORD i,j,*base;

	base = color_table;
	*(base++) = 0;

	for (j = 0; j < 39; j++) {
		for (i = 1; i < 16; i++)
			*(base++) = i;
		for (i = 1; i < 16; i++)
			*(base++) = i << 4;
		for (i = 1; i < 16; i++)
			*(base++) = i << 8;
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 4);
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 8);
		for (i = 1; i < 16; i++)
			*(base++) = (i << 4) | (i << 8);
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 4) | (i << 8);
	}
}


copy_userpalette()
{
	SHORT i;

	for (i = 3; i <= 32; i++)
		*(color_table + color_offset + (i - 2) * color_inc) = UserPalette[i];
}


gen_mand()
{
	void write_out();

	int x_coord,y_coord,count;
	FLOATSTRUCT x_gap,y_gap,z_r,z_i,u_r,u_i,temp,const0;
	FLOATSTRUCT const1,z_r_squared,z_i_squared;
	ULONG class;
	USHORT code;
	int returncode;

	/* if there was a display open, close it now */
	CloseDisplay();

	/* show the current settings! */
	CurrentSettings();

	/* reset the display variables: */
	SettingCenter = SettingBoxSize = FALSE;

	if (v_fp) {
		fclose(v_fp);
		v_fp = NULL;
	}

	v_fp = fopen(TEMPNAME,"w+");
	if (v_fp == NULL) {
		abort("Can't open temporary file!");
	}

	threed_stat = 0;
	v_starty = 1;
	v_offset = 0L;
	modified = FALSE;
	want_read = FALSE;
	last_y = -1;
	ZoomCenterX = (max_x >> 1);
	ZoomCenterY = (max_y >> 1);
	ZoomBoxSizeX = max_x;
	ZoomBoxSizeY = max_y;

	if (open_winds()) {
		returncode = 1;
		goto OPEN_W_RETURN;
	}

	MAKEFP(x_gap) = DIV(FLT(max_x),SUB(MAKEFP(start_r),MAKEFP(end_r)));
	MAKEFP(y_gap) = DIV(FLT(max_y),SUB(MAKEFP(end_i),MAKEFP(start_i)));

	MAKEFP(const0) = FLT(0);
	MAKEFP(const1) = FLT(1);

	u_i = end_i;

	for (y_coord = 0; y_coord < max_y; y_coord++) {
		u_r = start_r;
		last_color = 0xfff;
		v_pos_line(y_coord);
		modified = TRUE;
		for (x_coord = 0; x_coord < max_x; x_coord++) {
			while (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
				class = message->Class;
				code  = message->Code;
				ReplyMsg(message);
				if (class == MENUPICK) {
					switch MENUNUM(code) {
						case MENU_PROJECT:
							ProjectMenu(code);
							break;
						case MENU_OPTIONS:
							switch ITEMNUM(code) {
								case OPTIONS_STOP:
									returncode = 0;
									fclose(v_fp);
									v_fp = NULL;
									goto OPEN_W_RETURN;
								case OPTIONS_CLOSE:
									CloseDisplay();
									fclose(v_fp);
									v_fp = NULL;
									returncode = 1;
									goto OPEN_W_RETURN;
							}
							break;
					}
				}
			}

			z_r = const0;
			z_i = const0;

			for (count = 0; count < max_count; count++) {
				/* By setting these variables and reusing the results below,
				* we save approximately 10% execution time in this
				* innermost of inner loops.
				*/
				MAKEFP(z_r_squared) = MUL(MAKEFP(z_r), MAKEFP(z_r));
				MAKEFP(z_i_squared) = MUL(MAKEFP(z_i), MAKEFP(z_i));
				if ( FIX(ADD(MAKEFP(z_r_squared), MAKEFP(z_i_squared))) >= 4 )
					break;
				if (func_num == 0) {
					MAKEFP(temp) = SUB(MAKEFP(u_r),
					SUB(MAKEFP(z_i_squared), MAKEFP(z_r_squared)));
					MAKEFP(z_i) = SUB(MAKEFP(u_i),
					MUL(ADD(MAKEFP(z_r),MAKEFP(z_r)), MAKEFP(z_i)));
					z_r = temp;
				}
				else {
					if (func_num == 1) {
						MAKEFP(temp) = ADD(MAKEFP(u_r),
						SUB(MAKEFP(z_i_squared), MAKEFP(z_r_squared)));
						MAKEFP(z_i) = ADD(MAKEFP(u_i),
						MUL(ADD(MAKEFP(z_r),MAKEFP(z_r)), MAKEFP(z_i)));
						z_r = temp;
					}
				}
			}

			if (count >= max_count)
				count = 0;
			*(v_mand_store+(y_coord-v_starty)*max_x+x_coord) = count;
			write_out(count,x_coord,y_coord);

			MAKEFP(u_r) = ADD(MAKEFP(u_r),MAKEFP(x_gap));
		}
		MAKEFP(u_i) = ADD(MAKEFP(u_i),MAKEFP(y_gap));
	}

	/* successful return with window opened */
	returncode = 0;
	/* Now that we're drawing the primary display,
	* restore the entire menu system
	*/

OPEN_W_RETURN:

	reset_menus();

	return(returncode);
}

disp_mand()
{
	void write_out();

	int x_coord,y_coord,count;
	ULONG class;
	USHORT code;

	CloseDisplay();

	if (open_winds())
		return (1);

	reset_draw();

	want_read = TRUE;
	threed_stat = 0;

	for (y_coord=0;y_coord<max_y;y_coord++) {
		last_color = 0xfff;
		v_pos_line(y_coord);
		while (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
			class = message->Class;
			code  = message->Code;
			ReplyMsg(message);
			if (class == MENUPICK) {
				switch MENUNUM(code) {
					case MENU_PROJECT:
						ProjectMenu(code);
						break;
					case MENU_OPTIONS:
						switch ITEMNUM(code) {
							case OPTIONS_STOP:
								reset_menus();
								return (0);
							case OPTIONS_CLOSE:
								CloseDisplay();
								return (1);
						}
						break;
				}
			}
		}
		for (x_coord=0;x_coord<max_x;x_coord++) {
			count = *(v_mand_store+(y_coord-v_starty)*max_x+x_coord);
			write_out(count,x_coord,y_coord);
		}
	}

	reset_menus();
	return (0);
}

draw_threed(scale)
int scale;
{
	int x_coord,y_coord,x2,y2,y3,avg,modulus,count;
	UWORD *last_tab;
	ULONG class;
	USHORT code;

	CloseDisplay();

	last_tab = (UWORD *)AllocMem(max_x*8*sizeof(UWORD),MEMF_CHIP|MEMF_PUBLIC);
	if (last_tab == NULL)
		abort("Insufficient memory for temporary storage");

	if (open_winds())
		return (1);

	reset_draw();

	if (color_mode & HIRES_MODE)
		modulus = 13;
	else
		modulus = 29;

	want_read = TRUE;
	threed_stat = 1;

	for (y_coord=0;y_coord<8;y_coord++) {
		v_pos_line(y_coord*2);
      movmem(v_mand_store+y_coord*max_x*2,last_tab+y_coord*max_x,max_x*sizeof(UWORD));
	}
	for (y_coord=16;y_coord<max_y-8;y_coord+=2) {
		v_pos_line(y_coord);
		for (x_coord=0;x_coord<max_x-8;x_coord++) {
			while (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
				class = message->Class;
				code  = message->Code;
				ReplyMsg(message);
				if (class == MENUPICK) {
					switch MENUNUM(code) {
						case MENU_PROJECT:
							ProjectMenu(code);
							break;
						case MENU_OPTIONS:
							switch ITEMNUM(code) {
								case OPTIONS_STOP:
									FreeMem(last_tab,max_x*4*sizeof(UWORD));
									reset_menus();
									return (0);
								case OPTIONS_CLOSE:
									FreeMem(last_tab,max_x*4*sizeof(UWORD));
									CloseDisplay();
									return (1);
							}
							break;
					}
				}
			}
			avg = 0;
			for (x2=x_coord;x2<x_coord+9;x2++)
				for (y2=-8;y2<1;y2++)
					if (y2)
						avg += *(last_tab+(y2+8)*max_x+x2);
					else
						avg += *(v_mand_store+(y_coord-v_starty)*max_x+x2);
			count = avg/81;
			avg /= 81*scale;
			if (count == 0)
				continue;
			SetAPen(rp,(((count - 1) / color_div) % modulus) + 3);
			y3 = ((max_y + y_coord + 8) >> 1) - avg;
			if (y3 < 0)
				y3 = 0;
			Move(rp,x_coord,((max_y + y_coord + 8) >> 1) + STARTY);
			Draw(rp,x_coord,y3 + STARTY);
		}
		for (y2=0;y2<7;y2++)
			for (x2=0;x2<max_x;x2++)
				*(last_tab+y2*max_x+x2) = *(last_tab+(y2+1)*max_x+x2);
		for (x2=0;x2<max_x;x2++)
			*(last_tab+7*max_x+x2) = *(v_mand_store+(y_coord-v_starty)*max_x+x2);
	}

	FreeMem(last_tab,max_x*8*sizeof(UWORD));
	reset_menus();
	return (0);
}

void write_out(count,x,y)
int count,x,y;
{
	void ham_write();
	SHORT modulus;
	int color;

	y += STARTY;

	if (!(color_mode & NOT_HOLDANDMODIFY)) {
		if (count == 0)
			color = color_inset;
		else
			color = count * color_inc + color_offset;
		ham_write(*(color_table+color),x,y);
	}
	else {
	/* we're not hold-and-modify, so ... */
		if (color_mode & HIRES_MODE)
			modulus = 13;
		else
			modulus = 29;

		if (count == 0)
			color = color_inset;
		else
			color = (((count - 1) / color_div) % modulus) + 3;
		SetAPen(rp, color);
		WritePixel(rp, x, y);
	}
}

void ham_write(color, x, y)
int color;
int x, y;
{
	int red,green,blue,redd,greend,blued;

	red = (color >> 8) & 0xf;
	green = (color >> 4) & 0xf;
	blue = color & 0xf;
	redd = iabs(((last_color >> 8) & 0xf) - red);
	greend = iabs(((last_color >> 4) & 0xf) - green);
	blued = iabs((last_color & 0xf) - blue);

	if (redd >= greend && redd >= blued) {
		SetAPen(rp,red + 0x20);
		WritePixel(rp,x,y);
		last_color = (last_color & 0xff) | (color & 0xf00);
		return;
	}

	if (greend >= redd && greend >= blued) {
		SetAPen(rp,green + 0x30);
		WritePixel(rp,x,y);
		last_color = (last_color & 0xf0f) | (color & 0xf0);
		return;
	}

	SetAPen(rp,blue + 0x10);
	WritePixel(rp,x,y);
	last_color = (last_color & 0xff0) | blue;
	return;
}


DrawZoomCenter()
{
	DrawCross(ZoomCenterX, ZoomCenterY);
}

DrawDisplayCenter()
{
	DrawCross(max_x >> 1, max_y >> 1);
}


DrawCross(x, y)
SHORT x, y;
{
	SetDrMd(rp, COMPLEMENT);
	Move(rp, 0, y+STARTY);
	Draw(rp, rangex(x-4), y+STARTY);
	Move(rp, rangex(x+4), y+STARTY);
	Draw(rp, max_x-1, y+STARTY);
	Move(rp, x, STARTY);
	Draw(rp, x, rangey(y-3)+STARTY);
	Move(rp, x, rangey(y+3)+STARTY);
	Draw(rp, x, max_y+STARTY-1);
}

rangex(i)
int i;
{
	if (i<0)
		return (0);
	if (i>max_x-1)
		return (max_x-1);
	return (i);
}

rangey(i)
int i;
{
	if (i<0)
		return (0);
	if (i>max_y-1)
		return (max_y-1);
	return (i);
}


RecalcZoomBox()
{
	ZoomBoxSizeY = (iabs(w->MouseY - ZoomCenterY) << 1) + 1;
	if (w->MouseY < ZoomCenterY)
		ZoomBoxStartY = w->MouseY;
	else
		ZoomBoxStartY = ZoomCenterY - ((ZoomBoxSizeY - 1) >> 1);

	/* if SetBoxProportional, size X proportional to Y */
	if (SetBoxProportional) {
		ZoomBoxSizeX = (ZoomBoxSizeY * 8) / 5;
		ZoomBoxStartX = ZoomCenterX - ((ZoomBoxSizeX - 1) >> 1);
	}
	else {
		ZoomBoxSizeX = (iabs(w->MouseX - ZoomCenterX) << 1) + 1;
		if (w->MouseX < ZoomCenterX)
			ZoomBoxStartX = w->MouseX;
		else
			ZoomBoxStartX = ZoomCenterX - ((ZoomBoxSizeX - 1) >> 1);
	}
}


DrawZoomBox()
{
	SetDrMd(rp, COMPLEMENT);
	DrawBox(rp, ZoomBoxStartX, ZoomBoxStartY,
	ZoomBoxStartX + ZoomBoxSizeX - 1,
	ZoomBoxStartY + ZoomBoxSizeY - 1);

}


CloseDisplay()
{
	if (w)
		CloseWindow(w);
	if (w2)
		CloseWindow(w2);
	if (ColorWindow)
		CloseWindow(ColorWindow);
	if (screen)
		CloseScreen(screen);
	w = NULL;
	w2 = NULL;
	ColorWindow = NULL;
	screen = NULL;
}



DrawBox(rp, x1, y1, x2, y2)
struct RastPort *rp;
SHORT x1, y1, x2, y2;
/* draws a box without overlapping the edges (in case of complement mode) */
{
	Move(rp, x1, y1 + SIGN(y2 - y1)+STARTY);
	Draw(rp, x1, y2+STARTY);
	Move(rp, x1 + SIGN(x2 - x1), y2+STARTY);
	Draw(rp, x2, y2+STARTY);
	Move(rp, x2, y2 - SIGN(y2 - y1)+STARTY);
	Draw(rp, x2, y1+STARTY);
	Move(rp, x2 - SIGN(x2 - x1), y1+STARTY);
	Draw(rp, x1, y1+STARTY);
}

