/** Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1991 by Olaf `Olsen' Barthel, all rights reserved
 *
 *	Name .....: MandelSquare
 *	Created ..: Monday 26-Aug-91 11:20
 *	Revision .: 3
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	16-Sep-91	Olsen		Added animation replay feature
 *	12-Sep-91	Olsen		Several enhancements & bug fixes
 *	26-Aug-91	Olsen		Created this file!
 *
 ***************************************************************************/

#include "MandelSquare.h"

UBYTE VersTag[]	= VERSTAG;

	/* Menu IDs */

enum	{	MEN_OPEN=17,MEN_SAVE,MEN_SAVEAS,MEN_PRI0,MEN_PRI1,MEN_PRI2,MEN_PLAY,MEN_START,MEN_STOP,MEN_SCROLL,MEN_FRAME,MEN_INCLUDE,MEN_RESOLUTION,MEN_ABOUT,MEN_QUIT,
		MEN_ZOOM,MEN_CLIP,MEN_RERUN,MEN_PRECISE,MEN_COORDS,MEN_PALETTE,MEN_SPEC,MEN_RUN };

	/* Screen width & height must be a multiple of 32. */

#define PLUS		32

	/* Dimensions of the coords control panel. */

#define COORDS_WIDTH	256
#define COORDS_HEIGHT	120

	/* Dimensions of the palette control panel. */

#define PALETTE_WIDTH	245
#define PALETTE_HEIGHT	53

	/* Dimensions of the scroll amount control panel. */

#define SCROLL_WIDTH	175
#define SCROLL_HEIGHT	45

	/* Two handy macros to set the window mouse pointer. */

#define			SetWait(Window)		SetPointer((Window),&Stopwatch[0],16,16,-6,0)
#define			SetClear(Window)	SetPointer((Window),&ClearData[0],1,16,0,0)

	/* Just to avoid enforcer hits... */

#define			BailOut()		if(!Gadget) return(NULL);

	/* In Plot.asm, Iterate.asm & WaitLine.asm */

extern VOID __asm	Plot(register __a0 struct BitMap *,register __d0 WORD X,register __d1 WORD Y,register __d2 WORD Colour);
extern BYTE __asm	Test(register __a0 struct BitMap *,register __d0 WORD X,register __d1 WORD Y);
extern BYTE __stdargs	Iterate(double RealValue,double ImaginaryValue);
extern VOID __asm	WaitLine(register __d1 Line);

	/* In Mandelbrot.c */

VOID __regargs		DrawSquare(struct RastPort *RPort,WORD X,WORD Y,WORD Width);
VOID __regargs		DrawLine(struct RastPort *RPort,UWORD FromX,UWORD FromY,UWORD ToX,UWORD ToY);
VOID			IterationSetup(VOID);
VOID			GetScreenPalette(VOID);
LONG __saveds		CycleServer(VOID);
VOID			GfxCleanup(VOID);
LONG			GfxSetup(VOID);
VOID __regargs		CloseAll(LONG ReturnCode);
VOID			OpenAll(VOID);
VOID __regargs		CopyLine(struct BitMap *BitMap,WORD Line);
VOID			GetTime(VOID);
VOID			BlockWindow(VOID);
VOID			ReleaseWindow(VOID);
BYTE			GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag);
WORD __stdargs		MyEasyRequest(UBYTE *Gadgets,UBYTE *Text,...);
VOID			FadeTo(struct ViewPort *VPort,UWORD *From,UWORD *To,UBYTE NumColours,UBYTE ToCol,UBYTE FromCol);
ULONG __regargs		Random(LONG MaxValue);
struct Gadget *		CreateAllCoordsGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
struct Gadget *		CreateAllPaletteGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
struct Gadget *		CreateAllScrollGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge);
VOID			InfoText(VOID);
WORD			CalculateFrames(double MinReal,double MinImaginary,double Size);
VOID			RunAnim(VOID);
VOID			StopAnim(VOID);
VOID			Coords(VOID);
VOID			Palette(VOID);
VOID			MaxScrollPanel(VOID);
VOID			AreaZoom(VOID);
VOID			Zoom(VOID);
VOID			HandleEvent(VOID);
VOID			Mandelbrot(double MinReal,double MinImaginary,double RealStep,double ImaginaryStep,LONG Left,LONG Top,LONG Width,LONG Height);
VOID			RunMandelbrot(double MinReal,double MaxReal,double MinImaginary,double MaxImaginary,LONG Width,LONG Height);
VOID			RunAreaMandelbrot(double RealStep,double ImaginaryStep);
VOID __stdargs		main(VOID);

	/* In playanim.lib */

VOID			ClosePreLoadIFF(VOID);
BYTE			OpenPreLoadIFF(BPTR file);
VOID			DeleteBuffer(VOID);
BYTE			CreateBuffer(VOID);
VOID			SwapBits(VOID);
BYTE			PlayAnim(VOID);

	/* In saveanim.lib */

BYTE			AnimOpen(UBYTE *Name,struct ViewPort *VPort,struct RastPort *RPort);
BYTE			AnimAdd(struct RastPort *RPort);
VOID			AnimClose(VOID);

	/* In ReadILBM.c */

struct IFFHandle * __regargs	OpenImageFile(UBYTE *Name);
VOID __regargs			CloseImageFile(struct IFFHandle *Handle);
BYTE __regargs			ReadImageBody(struct IFFHandle *Handle,struct BitMap *BitMap,struct BitMapHeader *BitMapHeader);
BYTE __regargs			ReadImageHeader(struct IFFHandle *Handle,ULONG *ViewModes,BYTE *NumCols,UWORD *Colours,struct BitMapHeader *BitMapHeader,struct MandelInfo *MandelInfo);
VOID __regargs			DeleteBitMap(struct BitMap *BitMap);
struct BitMap * __regargs	CreateBitMap(BYTE Depth,UWORD Width,UWORD Height);

	/* In WriteILBM.c */

LONG			SaveBitMap(struct BitMap *BitMap,struct ViewPort *VPort,LONG LeftEdge,LONG TopEdge,LONG Width,LONG Height,LONG ParentWidth,LONG ParentHeight,STRPTR Name,struct MandelInfo *MandelInfo);

	/* Shared and global library data. */

struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*GadToolsBase;
struct Library		*IFFParseBase;
struct Library		*AslBase;
struct Library		*TimerBase;
struct Library		*IconBase;

	/* Timer data. */

struct timerequest	*TimeRequest;
struct MsgPort		*TimePort;

	/* Window & Screen data. */

struct Screen		*Screen;
struct Window		*Window;
struct RastPort		*RPort;
struct ViewPort		*VPort;
APTR			 VisualInfo;
struct BitMap		*BackupBitMap;
struct Menu		*Menu;

	/* Current process and Window Pointer. */

struct Process		*ThisProcess;
APTR			 OldPtr;
BYTE			 OldPri;

	/* Colour cycling data. */

struct Interrupt	*CycleInterrupt;
BYTE			 DoCycle = FALSE,Forward,Wheel = 0,Add = 0;

	/* Some strings. */

UBYTE			 LastName[256],NameBuffer[256],DummyBuffer[256],AnimSaveName[256],AnimPlayName[256];

	/* Table of spectral colours. */

UWORD			 Table[80];

	/* Some more colour tables. */

UWORD			 ScreenPalette[32],SinPalette[32],WheelPalette[32];

	/* Calculation data. */

LONG			 MaxIteration = 32;
double			 RealStep,ImaginaryStep;
double			 RealWidth = 4.5,ImaginaryHeight = 4.5;
double			 MinReal,MinImaginary;
double			 MainMinReal,MainMinImaginary,MainRealWidth,MainImaginaryHeight;
double			 MaxScroll = 4;
double			 LastSize,LastMinReal,LastMinImaginary;

	/* Size and position of the screen. */

UWORD			 AreaLeft,AreaTop,AreaWidth,AreaHeight;

	/* Various flags. */

BYTE			 Running = FALSE,NewCoords = FALSE,AreaActive = FALSE,GotClip = FALSE,
			 NewMode = FALSE,Frame = FALSE,Include = TRUE,Faster = TRUE,UseWheel = FALSE,
			 Precise = FALSE,HoldIt = FALSE,AnimRunning = FALSE,FullPicture,AnimOpened = FALSE;

	/* A range of colour intensities and the colour transition table. */

BYTE			 Range[32],*Wave;

	/* The levels of the palette colour sliders. */

BYTE			 RedLevel = 27,GreenLevel = 22,BlueLevel = 17;

	/* A global MandelInfo structure to be prepared for the
	 * MAND chunk.
	 */

struct MandelInfo	 MandelInfo;

	/* Rendering data. */

LONG 			 Mode = LORES_KEY,Depth = 5,Square = 256;

	/* The TextFont structure contained in Font.c */

extern struct TextFont	 DigitFont;

	/* A single line bitmap whose planes are to be located
	 * in fast ram.
	 */

struct BitMap		 LineBitMap;

	/* Global image data. */

struct BitMap		*GlobalBitMap;
struct BitMapHeader	 GlobalHeader;
ULONG			 GlobalMode;

	/* The initial colour table the program uses. */

UWORD ColourTable[32] =
{
	0x0000,0x0FFF,0x04BE,0x03AE,
	0x019D,0x007D,0x016C,0x034B,
	0x043A,0x0619,0x0707,0x0916,
	0x0A34,0x0B43,0x0C61,0x0D70,
	0x0D91,0x0EA3,0x0EB4,0x0FC6,
	0x0FD7,0x0FD9,0x0EEA,0x0EEB,
	0x0DFC,0x0DFD,0x0CFD,0x0BEE,
	0x0AEE,0x09DF,0x07DF,0x06CF
};

	/* The standard font to be use throughout the
	 * whole program.
	 */

struct TextAttr DefaultFont =
{
	(UBYTE *)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT
};

	/* Definitions for the pull-down menu used by the program. */

struct NewMenu MandelbrotMenu[] =
{
	{ NM_TITLE, "Project",			 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Open Picture...",		"O", 0,			0,	(APTR)MEN_OPEN},
	{  NM_ITEM, "Save Picture",		 0 , 0,			0,	(APTR)MEN_SAVE},
	{  NM_ITEM, "Save Picture As...",	"S", 0,			0,	(APTR)MEN_SAVEAS},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Priority",			 0 , 0,			0,	(APTR)0},
	{   NM_SUB, "-5",			 0 , CHECKIT|MENUTOGGLE,~1,	(APTR)MEN_PRI0},
	{   NM_SUB, " 0",			 0 , CHECKIT|CHECKED|MENUTOGGLE,~2,	(APTR)MEN_PRI1},
	{   NM_SUB, "+5",			 0 , CHECKIT|MENUTOGGLE,~4,	(APTR)MEN_PRI2},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Replay Animation...",	"!", 0,			0,	(APTR)MEN_PLAY},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Begin Animation...",	"B", 0,			0,	(APTR)MEN_START},
	{  NM_ITEM, "End Animation",		"E", 0,			0,	(APTR)MEN_STOP},
	{  NM_ITEM, "Set Scroll Amount...",	"A", 0,			0,	(APTR)MEN_SCROLL},
	{  NM_ITEM, "Include Frame",		 0 , CHECKIT|MENUTOGGLE,0,	(APTR)MEN_FRAME},
	{  NM_ITEM, "Include Coordinates",	 0 , CHECKIT|CHECKED|MENUTOGGLE,0,	(APTR)MEN_INCLUDE},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Change Resolution",	"X", 0,			0,	(APTR)MEN_RESOLUTION},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "About...",			"?", 0,			0,	(APTR)MEN_ABOUT},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Quit...",			"Q", 0,			0,	(APTR)MEN_QUIT},

	{ NM_TITLE, "Calculation",		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Magnify...",		"M", 0,			0,	(APTR)MEN_ZOOM},
	{  NM_ITEM, "Detail...",		"D", 0,			0,	(APTR)MEN_CLIP},
	{  NM_ITEM, "Recalculate...",		"R", 0,			0,	(APTR)MEN_RERUN},
	{  NM_ITEM, "Precise Calculation",	 0 , CHECKIT|MENUTOGGLE,0,	(APTR)MEN_PRECISE},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Coordinates...",		"C", 0,			0,	(APTR)MEN_COORDS},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Palette...",		"P", 0,			0,	(APTR)MEN_PALETTE},
	{  NM_ITEM, "Spectral Colours",		 0 , CHECKIT|MENUTOGGLE,0,	(APTR)MEN_SPEC},
	{  NM_ITEM, NM_BARLABEL,		 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "Toggle Calculation",	".", 0,			0,	(APTR)MEN_RUN},

	{ NM_TITLE, "Depth",			 0 , 0,			0,	(APTR)0},
	{  NM_ITEM, "   32",			"0", CHECKIT|CHECKED,	~  1,	(APTR)5},
	{  NM_ITEM, "   64",			"1", CHECKIT,		~  2,	(APTR)6},
	{  NM_ITEM, "  128",			"2", CHECKIT,		~  4,	(APTR)7},
	{  NM_ITEM, "  256",			"3", CHECKIT,		~  8,	(APTR)8},
	{  NM_ITEM, "  512",			"4", CHECKIT,		~ 16,	(APTR)9},
	{  NM_ITEM, " 1024",			"5", CHECKIT,		~ 32,	(APTR)10},
	{  NM_ITEM, " 2048",			"6", CHECKIT,		~ 64,	(APTR)11},
	{  NM_ITEM, " 4096",			"7", CHECKIT,		~128,	(APTR)12},
	{  NM_ITEM, " 8192",			"8", CHECKIT,		~256,	(APTR)13},
	{  NM_ITEM, "16384",			"9", CHECKIT,		~512,	(APTR)14},
	{   NM_END, 0,				 0 , 0,			0,	(APTR)0}
};

	/* Run-dump of the stopwatch mouse pointer. */

UWORD __chip Stopwatch[(2 + 16) * 2] =
{
	0x0000,0x0000,

	0x0400,0x07C0,
	0x0000,0x07C0,
	0x0100,0x0380,
	0x0000,0x07E0,
	0x07C0,0x1FF8,
	0x1FF0,0x3FEC,
	0x3FF8,0x7FDE,
	0x3FF8,0x7FBE,
	0x7FFC,0xFF7F,
	0x7EFC,0xFFFF,
	0x7FFC,0xFFFF,
	0x3FF8,0x7FFE,
	0x3FF8,0x7FFE,
	0x1FF0,0x3FFC,
	0x07C0,0x1FF8,
	0x0000,0x07E0,

	0x0000,0x0000
};

	/* Definitions for a clear sprite. */

UWORD __chip ClearData[(2 + 1) * 2] =
{
	0x0000,0x0000,

	0x0000,0x0000,

	0x0000,0x0000
};

	/* Run-dump of the picture icon image data. */

USHORT Picture1Data[700] =
{
	0x0000,0x0000,0x0000,0x0000,0x0010,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x0000,0x0000,0x0000,0x4000,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0400,0x0030,
	0x0000,0x0000,0x0000,0x1C00,0x0030,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x4800,0x0030,0x0000,
	0x0000,0x0000,0xC000,0x0030,0x0000,0x0000,0x0005,0x8000,
	0x0030,0x0000,0x0000,0x0001,0x0300,0x0030,0x0000,0x0000,
	0x0001,0x0200,0x0030,0x0000,0x0000,0x0001,0x0200,0x0030,
	0x0000,0x0000,0x0100,0x0020,0x0030,0x0000,0x0000,0x002F,
	0x8E80,0x8030,0x0000,0x0000,0x0328,0x0080,0x0030,0x0000,
	0x0000,0x0260,0x002E,0xC030,0x0000,0x0000,0x0200,0x0008,
	0x8030,0x0000,0x0000,0x0200,0x0000,0x8030,0x0000,0x0000,
	0x1600,0x0000,0x0030,0x0000,0x0000,0x1400,0x0000,0x8030,
	0x0000,0x2000,0x1000,0x0000,0x0030,0x0000,0x0040,0x3000,
	0x0000,0x9030,0x0000,0x4000,0x2000,0x0000,0x3030,0x0000,
	0x0080,0x6000,0x0000,0x6030,0x0000,0x0FB0,0x4000,0x0000,
	0x0030,0x0000,0x0800,0xC000,0x0000,0x2030,0x0000,0x0800,
	0x0000,0x0000,0x6030,0x0000,0x1800,0xC000,0x0000,0x4030,
	0x0000,0x3000,0x8000,0x0000,0x4030,0x0001,0x2000,0x8000,
	0x0000,0xC030,0x0003,0x6000,0x0000,0x0000,0x8030,0x0F82,
	0x0000,0x0000,0x0001,0x0030,0x0020,0x0000,0x0000,0x0000,
	0x0030,0x0008,0x0000,0x0000,0x0000,0x0030,0x0000,0x2000,
	0x0000,0x0000,0x0030,0x0003,0x8000,0x8000,0x0000,0x4030,
	0x0006,0x0000,0x8000,0x0000,0x4030,0x0000,0x4800,0x0000,
	0x0000,0x0030,0x0000,0x0802,0x4000,0x0000,0x2030,0x0000,
	0x0006,0x4000,0x0000,0x6030,0x0000,0x060C,0x0000,0x0000,
	0x0030,0x0000,0x3C78,0x0000,0x0000,0x0030,0x0000,0x20C0,
	0x0000,0x0000,0x1030,0x0000,0x2000,0x1800,0x0000,0xF030,
	0x0000,0x4000,0x0000,0x0001,0x8030,0x0000,0x0000,0x2000,
	0x0001,0x0030,0x0000,0x0000,0x0200,0x0000,0x0030,0x0000,
	0x0000,0x0200,0x0000,0x0030,0x0000,0x0000,0x0200,0x0000,
	0x4030,0x0000,0x0000,0x0080,0x003E,0xC030,0x0000,0x0000,
	0x0180,0x0120,0x8030,0x0000,0x0000,0x0D19,0x8360,0x8030,
	0x0000,0x0000,0x0101,0x0200,0x0030,0x0000,0x0000,0x0001,
	0x0200,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0300,0x0030,0x0000,0x0000,0x0005,0x0E00,
	0x0030,0x0000,0x0000,0x0001,0x1800,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x2000,0x0030,
	0x0000,0x0000,0x0000,0x2C00,0x0030,0x0000,0x0000,0x0000,
	0x2000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x7FFF,0xFFFF,0xFFFF,0xFFFF,0xFFF0,

	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0xD555,0x5555,0x5555,0x1555,0x5540,0xD555,0x5555,
	0x5555,0x3555,0x5540,0xD555,0x5555,0x5555,0x8155,0x5540,
	0xD555,0x5555,0x5555,0x8155,0x5540,0xD555,0x5555,0x5557,
	0x0555,0x5540,0xD555,0x5555,0x555C,0x0555,0x5540,0xD555,
	0x5555,0x5550,0x0155,0x5540,0xD555,0x5555,0x5558,0x5155,
	0x5540,0xD555,0x5555,0x5558,0x5855,0x5540,0xD555,0x5555,
	0x7558,0x59D5,0x5540,0xD555,0x5555,0x6DF8,0x7197,0x5540,
	0xD555,0x5555,0x6080,0x401E,0x5540,0xD555,0x5555,0x4000,
	0x5010,0x5540,0xD555,0x5555,0x5015,0x5500,0x5540,0xD555,
	0x5555,0x5115,0x5540,0x1540,0xD555,0x5555,0xF155,0x5554,
	0x5540,0xD555,0x5555,0x8155,0x5551,0x5540,0xD555,0xD555,
	0x8155,0x5554,0x5540,0xD555,0x1555,0x8155,0x5556,0x5540,
	0xD555,0x9755,0x8555,0x5554,0x1540,0xD555,0x1E35,0x0555,
	0x5555,0x0540,0xD555,0x0005,0x1555,0x5555,0x0540,0xD555,
	0x4005,0x1555,0x5555,0x1540,0xD555,0xC040,0x1555,0x5555,
	0x1540,0xD555,0x0550,0x1555,0x5555,0x1540,0xD555,0x8554,
	0x5555,0x5555,0x1540,0xD55B,0x8556,0x1555,0x5557,0x1540,
	0xD558,0x0554,0x5555,0x5556,0x1540,0xFFF0,0x1555,0x5555,
	0x5554,0x1540,0xC000,0x1555,0x5555,0x555D,0x5540,0xD001,
	0x5555,0x5555,0x5550,0x5540,0xD541,0xD555,0x5555,0x5555,
	0x5540,0xD550,0x1557,0x5555,0x5554,0x5540,0xD558,0x1556,
	0x5555,0x5555,0x5540,0xD550,0x1556,0x5555,0x5555,0x1540,
	0xD551,0x055C,0x5555,0x5555,0x1540,0xD555,0x05F0,0x5555,
	0x5555,0x9540,0xD555,0x4580,0x1555,0x5555,0x1540,0xD555,
	0xC000,0x1555,0x5555,0x1540,0xD555,0x8001,0x1555,0x5555,
	0x9540,0xD555,0x0005,0x1555,0x5557,0x1540,0xD555,0x1515,
	0x8555,0x5556,0x0540,0xD555,0x5555,0x8555,0x555C,0x0540,
	0xD555,0x1555,0x0555,0x5550,0x5540,0xD555,0x5555,0x4155,
	0x5574,0x5540,0xD555,0x5555,0x5155,0x5564,0x5540,0xD555,
	0x5555,0x7155,0x55C6,0x5540,0xD555,0x5555,0x403D,0x5F80,
	0x1540,0xD555,0x5555,0x7001,0x5800,0x1540,0xD555,0x5555,
	0x4040,0x5014,0x5540,0xD555,0x5555,0x5040,0x5015,0x5540,
	0xD555,0x5555,0x5458,0x5955,0x5540,0xD555,0x5555,0x5558,
	0x5955,0x5540,0xD555,0x5555,0x5558,0x7155,0x5540,0xD555,
	0x5555,0x5550,0x6055,0x5540,0xD555,0x5555,0x5550,0x0155,
	0x5540,0xD555,0x5555,0x5554,0x0555,0x5540,0xD555,0x5555,
	0x5555,0x8555,0x5540,0xD555,0x5555,0x5555,0x8555,0x5540,
	0xD555,0x5555,0x5555,0x1155,0x5540,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0x8000,0x0000,0x0000,0x0000,0x0000
};

	/* Definitions for the picture icon image. */

struct Image Picture1Image =
{
	0,0,
	76,70,2,
	(USHORT *)&Picture1Data[0],
	0x03,0x00,
	(struct Image *)NULL
};

	/* Tool types for the picture project icon. */

char *PictureToolTypes[] =
{
	"FILETYPE=ILBM",
	NULL
};

	/* The name of the project's default tool (will be filled
	 * in later).
	 */

UBYTE DefaultTool[256];

	/* Definitions for the picture project icon. */

struct DiskObject PictureIcon =
{
	WB_DISKMAGIC,
	WB_DISKVERSION,

	(struct Gadget *)NULL,
	0,0,
	76,70,
	0x0004,
	0x0003,
	0x0001,
	(APTR)&Picture1Image,
	(APTR)NULL,
	(struct IntuiText *)NULL,
	NULL,
	(APTR)NULL,
	NULL,
	(APTR)NULL,

	WBPROJECT,
	(char *)NULL,
	PictureToolTypes,
	NO_ICON_POSITION,
	NO_ICON_POSITION,
	(struct DrawerData *)NULL,
	(char *)NULL,
	20000
};

	/* Run-dump of the anim icon image data. */

UWORD Anim1Data[700] =
{
	0x0000,0x0000,0x0000,0x0000,0x0010,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x0000,0x0000,0x0000,0x4000,0x0030,0x0000,0x0000,
	0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0400,0x0030,
	0x0000,0x0000,0x0000,0x1C00,0x0030,0x0000,0x0000,0x0000,
	0x0000,0x0030,0x0000,0x0000,0x0000,0x4800,0x0030,0x0000,
	0x0000,0x0000,0xC000,0x0030,0x0000,0x0000,0x0005,0x8000,
	0x0030,0x0000,0x0000,0x0001,0x0300,0x0030,0x0000,0x0000,
	0x0001,0x0200,0x0030,0x0000,0x0000,0x0001,0x0200,0x0030,
	0x0000,0x0000,0x0100,0x0020,0x0030,0x0000,0x0000,0x002F,
	0x8E80,0x8030,0x0000,0x0000,0x0328,0x0080,0x0030,0x0000,
	0x0000,0x0260,0x002E,0xC030,0x0003,0xFFD0,0x0200,0x0008,
	0x8030,0x0002,0x0010,0x0200,0x0000,0x8030,0x0002,0x0010,
	0x1600,0x0000,0x0030,0x0002,0x0010,0x1400,0x0000,0x8030,
	0x0002,0x2010,0x1000,0x0000,0x0030,0x0002,0x0010,0x3000,
	0x0000,0x9030,0x0002,0x4010,0x2000,0x0000,0x3030,0x0002,
	0x0010,0x6000,0x0000,0x6030,0x0002,0x0F10,0x4000,0x0000,
	0x0030,0x0002,0x0810,0xC000,0x0000,0x2030,0x0002,0x0810,
	0x0000,0x0000,0x6030,0x0002,0x0010,0xC000,0x0000,0x4030,
	0x0000,0x0010,0x8000,0x0000,0x4030,0x0003,0xFFF0,0x8000,
	0x0000,0xC030,0x0003,0x6000,0x0000,0x0000,0x8030,0x0F82,
	0x0000,0x0000,0x0001,0x0030,0x0020,0x0000,0x0000,0x0000,
	0x0030,0x0008,0x0000,0x0000,0x0000,0x0030,0x0000,0x2000,
	0x0000,0x0000,0x0030,0x0003,0x8000,0x8000,0x0000,0x4030,
	0x0006,0x0000,0x8000,0x0000,0x4030,0x0000,0x4800,0x0000,
	0x0000,0x0030,0x0000,0x0802,0x4000,0x0000,0x2030,0x0000,
	0x0006,0x4000,0x0000,0x6030,0x0000,0x060C,0x0000,0x0000,
	0x0030,0x0000,0x3C78,0x0000,0x0000,0x0030,0x0000,0x20C0,
	0x0000,0x0000,0x1030,0x0000,0x2000,0x1800,0x0000,0xF030,
	0x0000,0x400C,0x0000,0x0C01,0x8030,0x0000,0x001C,0x2000,
	0x1E01,0x0030,0x0000,0x001C,0x0200,0x0C00,0x0030,0x0000,
	0x001E,0x0200,0x0000,0x0030,0x0000,0x003E,0x0DF0,0x7C1F,
	0xF830,0x0000,0x003E,0x0FF8,0xFC1F,0xFC30,0x0000,0x0037,
	0x0F18,0x0D1D,0xCC30,0x0000,0x0073,0x0E19,0x8D59,0xCC30,
	0x0000,0x0073,0x0E19,0x0C19,0xCC30,0x0000,0x0063,0x8C19,
	0x0C19,0xCC30,0x0000,0x00E1,0x8C18,0x0C19,0xCC30,0x0000,
	0x00FF,0x8C18,0x0D19,0xCC30,0x0000,0x00FF,0xCC19,0x0C19,
	0xCC30,0x0000,0x01C1,0xCC19,0x0C19,0xCC30,0x0000,0x01C0,
	0xCC18,0x0C19,0xCC30,0x0000,0x0180,0xEC18,0x2C19,0xCC30,
	0x0000,0x0180,0xEC18,0x2C19,0xCC30,0x0000,0x0100,0x6C18,
	0x2C18,0x8C30,0x0000,0x0000,0x0000,0x0000,0x0030,0x0000,
	0x0000,0x0000,0x0000,0x0030,0x0000,0x0000,0x0000,0x0000,
	0x0030,0x7FFF,0xFFFF,0xFFFF,0xFFFF,0xFFF0,

	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0xD555,0x5555,0x5555,
	0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,0x5540,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0xD555,0x5555,0x5555,0x1555,0x5540,0xD555,0x5555,
	0x5555,0x3555,0x5540,0xD555,0x5555,0x5555,0x8155,0x5540,
	0xD555,0x5555,0x5555,0x8155,0x5540,0xD555,0x5555,0x5557,
	0x0555,0x5540,0xD555,0x5555,0x555C,0x0555,0x5540,0xD555,
	0x5555,0x5550,0x0155,0x5540,0xD555,0x5555,0x5558,0x5155,
	0x5540,0xD555,0x5555,0x5558,0x5855,0x5540,0xD555,0x5555,
	0x7558,0x59D5,0x5540,0xD555,0x5555,0x6DF8,0x7197,0x5540,
	0xD555,0x5555,0x6080,0x401E,0x5540,0xD555,0x5555,0x4000,
	0x5010,0x5540,0xD55F,0xFFC5,0x5015,0x5500,0x5540,0xD558,
	0x0005,0x5115,0x5540,0x1540,0xD558,0x0005,0xF155,0x5554,
	0x5540,0xD559,0x5545,0x8155,0x5551,0x5540,0xD559,0xD545,
	0x8155,0x5554,0x5540,0xD559,0x1545,0x8155,0x5556,0x5540,
	0xD559,0x9745,0x8555,0x5554,0x1540,0xD559,0x1E45,0x0555,
	0x5555,0x0540,0xD559,0x0045,0x1555,0x5555,0x0540,0xD559,
	0x4045,0x1555,0x5555,0x1540,0xD559,0xC040,0x1555,0x5555,
	0x1540,0xD559,0x0540,0x1555,0x5555,0x1540,0xD559,0x8544,
	0x5555,0x5555,0x1540,0xD559,0xFFC6,0x1555,0x5557,0x1540,
	0xD550,0x0004,0x5555,0x5556,0x1540,0xFFF0,0x0005,0x5555,
	0x5554,0x1540,0xC000,0x1555,0x5555,0x555D,0x5540,0xD001,
	0x5555,0x5555,0x5550,0x5540,0xD541,0xD555,0x5555,0x5555,
	0x5540,0xD550,0x1557,0x5555,0x5554,0x5540,0xD558,0x1556,
	0x5555,0x5555,0x5540,0xD550,0x1556,0x5555,0x5555,0x1540,
	0xD551,0x055C,0x5555,0x5555,0x1540,0xD555,0x05F0,0x5555,
	0x5555,0x9540,0xD555,0x4580,0x1555,0x5555,0x1540,0xD555,
	0xC000,0x1555,0x5555,0x1540,0xD555,0x8001,0x1555,0x5555,
	0x9540,0xD555,0x0005,0x1555,0x5557,0x1540,0xD555,0x1515,
	0x8555,0x5556,0x0540,0xD555,0x5551,0x8555,0x515C,0x0540,
	0xD555,0x1545,0x0555,0x4150,0x5540,0xD555,0x5551,0x4155,
	0x5274,0x5540,0xD555,0x5551,0x5155,0x4D64,0x5540,0xD555,
	0x5542,0x7005,0x01C0,0x0540,0xD555,0x5528,0x4D15,0x7D9C,
	0xC140,0xD555,0x5528,0x6248,0x8002,0x1140,0xD555,0x5505,
	0x4100,0x0005,0x0140,0xD555,0x5550,0x4040,0x4001,0x4140,
	0xD555,0x5550,0x4240,0x4141,0x4140,0xD555,0x5502,0x8140,
	0x4141,0x4140,0xD555,0x54A0,0x0140,0x6141,0x4140,0xD555,
	0x5480,0x0140,0x6041,0x4140,0xD555,0x543F,0x4140,0x0141,
	0x4140,0xD555,0x5501,0x0140,0x0141,0x4140,0xD555,0x5554,
	0x0141,0x8141,0x4140,0xD555,0x5414,0xA141,0x8141,0x4140,
	0xD555,0x5414,0x8141,0x0141,0x4140,0xD555,0x5555,0x6D59,
	0x4D58,0x8D40,0xD555,0x5455,0x1145,0x5145,0x5140,0xD555,
	0x5555,0x5555,0x5555,0x5540,0xD555,0x5555,0x5555,0x5555,
	0x5540,0x8000,0x0000,0x0000,0x0000,0x0000
};

	/* Definitions for the anim icon image. */

struct Image Anim1Image =
{
	0,0,
	76,70,2,
	(UWORD *)&Anim1Data[0],
	0x03,0x00,
	(struct Image *)NULL
};

	/* Tool types for the anim project icon. */

char *AnimToolTypes[] =
{
	"FILETYPE=ANIM",
	NULL
};

	/* Definitions for the anim project icon. */

struct DiskObject AnimIcon =
{
	WB_DISKMAGIC,
	WB_DISKVERSION,

	(struct Gadget *)NULL,
	23,15,
	76,70,
	0x0004,
	0x0003,
	0x0001,
	(APTR)&Anim1Image,
	(APTR)NULL,
	(struct IntuiText *)NULL,
	NULL,
	(APTR)NULL,
	NULL,
	(APTR)NULL,

	WBPROJECT,
	(char *)NULL,
	AnimToolTypes,
	NO_ICON_POSITION,
	NO_ICON_POSITION,
	(struct DrawerData *)NULL,
	(char *)NULL,
	20000
};

	/* DrawSquare():
	 *
	 *	Draws a square rectangle at a given position.
	 */

VOID __regargs
DrawSquare(struct RastPort *RPort,WORD X,WORD Y,WORD Width)
{
	Move(RPort,X,Y);
	Draw(RPort,X + Width - 1,Y);
	Draw(RPort,X + Width - 1,Y + Width - 1);
	Draw(RPort,X,Y + Width - 1);
	Draw(RPort,X,Y);
}

	/* DrawLine():
	 *
	 *	Draw a simple line from a to b.
	 */

VOID __regargs
DrawLine(struct RastPort *RPort,UWORD FromX,UWORD FromY,UWORD ToX,UWORD ToY)
{
	Move(RPort,FromX,FromY);
	Draw(RPort,ToX,ToY);
}

	/* IterationSetup():
	 *
	 *	Sets up the iterations menu, checkmarks the currently
	 *	selected number of iterations, uncheckmarks all the
	 *	others.
	 */

VOID
IterationSetup()
{
	struct MenuItem *Item;

	Window -> Flags |= WFLG_RMBTRAP;

	if(Item = ItemAddress(Menu,FULLMENUNUM(2,0,0)))
	{
		while(Item)
		{
			if((1 << (ULONG)GTMENUITEM_USERDATA(Item)) == MaxIteration)
				Item -> Flags |= CHECKED;
			else
				Item -> Flags &= ~CHECKED;

			Item = Item -> NextItem;
		}
	}

	Window -> Flags &= ~WFLG_RMBTRAP;
}

	/* GetScreenPalette():
	 *
	 *	Copies the current screen palette to a buffer.
	 */

VOID
GetScreenPalette()
{
	WORD i;

	for(i = 0 ; i < 32 ; i++)
		ScreenPalette[i] = GetRGB4(VPort -> ColorMap,i);
}

	/* CycleServer():
	 *
	 *	The interrupt server routine which handles the
	 *	colour cycling.
	 */

LONG __saveds
CycleServer()
{
	STATIC BYTE Count = 0;

	if(Count++ == 2)
	{
		if(DoCycle && !HoldIt)
		{
			if(UseWheel)
			{
				BYTE AltWheel = Wheel,i;

				WheelPalette[0] = 0x000;
				WheelPalette[1] = 0xFFF;

				if(Forward)
				{
					for(i = 2 ; i < 32 ; i++)
					{
						WheelPalette[i] = Table[AltWheel];

						AltWheel = (AltWheel + 1) % 75;
					}

					Wheel = (Wheel + 1) % 75;
				}
				else
				{
					for(i = 2 ; i < 32 ; i++)
					{
						WheelPalette[i] = Table[AltWheel];

						AltWheel = (AltWheel + 1) % 75;
					}

					if(Wheel)
						Wheel--;
					else
						Wheel = 74;
				}

				LoadRGB4(VPort,WheelPalette,1 << Depth);
			}
			else
			{
				UWORD		NewPalette[32];
				BYTE		i,Wrap = (1 << Depth) - 2,AltAdd = Add;

				NewPalette[0] = 0x000;
				NewPalette[1] = 0xFFF;

				if(Forward)
				{
					for(i = 2 ; i < (1 << Depth) ; i++)
					{
						NewPalette[i] = SinPalette[AltAdd + 2];

						AltAdd = (AltAdd + 1) % Wrap;
					}

					Add = (Add + 1) % Wrap;
				}
				else
				{
					for(i = 2 ; i < (1 << Depth) ; i++)
					{
						NewPalette[i] = SinPalette[AltAdd + 2];

						AltAdd = (AltAdd + 1) % Wrap;
					}

					if(Add)
						Add--;
					else
						Add = Wrap - 1;
				}

				LoadRGB4(VPort,NewPalette,1 << Depth);
			}
		}

		Count = 0;
	}

	return(0);
}

	/* GfxCleanup():
	 *
	 *	Frees the graphics resources allocated by the
	 *	program.
	 */

VOID
GfxCleanup()
{
	ThisProcess -> pr_WindowPtr = OldPtr;

	if(LineBitMap . Planes[0])
	{
		FreeVec(LineBitMap . Planes[0]);

		LineBitMap . Planes[0] = NULL;
	}

	DoCycle = FALSE;

	Delay(5);

	if(Screen)
		ScreenToBack(Screen);

	if(Window)
	{
		ClearMenuStrip(Window);

		CloseWindow(Window);

		Window = NULL;
	}

	if(Menu)
	{
		FreeMenus(Menu);

		Menu = NULL;
	}

	if(VisualInfo)
	{
		FreeVisualInfo(VisualInfo);

		VisualInfo = NULL;
	}

	if(Screen)
	{
		CloseScreen(Screen);

		Screen = NULL;
	}
}

	/* GfxSetup():
	 *
	 *	Allocates the graphics resources the program requires.
	 */

LONG
GfxSetup()
{
	struct Rectangle	 DisplayClip;
	LONG			 Differ;
	WORD			 i,c = 0,r = 15,g = 0,b = 0;
	double			 a,p;
	BYTE			 AltWheel = Wheel,Wrap = (1 << Depth) - 2;

	struct ColorSpec	*ColorSpec;

	a = PI / (double)((1 << Depth) - 2);

		/* Build a range of colour intensities mapped to the
		 * sine wave amplitude.
		 */

	for(p = 0.0, i = 0 ; (p < PI) && (i < (1 << Depth) - 2) ; p += a, i++)
		Range[i] = (BYTE)(sin(p) * 15 - 0.3);

	SinPalette[0] = 0x000;
	SinPalette[1] = 0xFFF;

		/* Combine all three colour ranges into a single palette. */

	for(i = 0 ; i < Wrap ; i++)
	{
		SinPalette[2 + i]  = Range[(RedLevel   + i) % Wrap] << 8;
		SinPalette[2 + i] |= Range[(GreenLevel + i) % Wrap] << 4;
		SinPalette[2 + i] |= Range[(BlueLevel  + i) % Wrap];
	}

		/* Create the spectral colour table. */

	for(i = 0 ; i < 16 ; i++)
		Table[c++] = (r << 8) | ((g++) << 4) | b;

	g = 15;
	r--;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = ((r--) << 8) | (g << 4) | b;

	r = 0;
	g--;
	b++;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = (r << 8) | ((g--) << 4) | (b++);

	g = 0;
	b = 15;
	r++;

	for(i = 0 ; i < 15 ; i++)
		Table[c++] = ((r++) << 8) | (g << 4) | b;

	r = 15;
	b--;

	for(i = 0 ; i < 14 ; i++)
		Table[c++] = (r << 8) | (g << 4) | (b--);

		/* Set up the spectral colour palette. */

	WheelPalette[0] = 0x000;
	WheelPalette[1] = 0xFFF;

	for(i = 2 ; i < 32 ; i++)
	{
		WheelPalette[i] = Table[AltWheel];

		AltWheel = (AltWheel + 1) % 75;
	}

		/* Create the colour transition table. */

	for(i = 0 ; i < 16384 ; i++)
		Wave[i] = 2 + (i % ((1 << Depth) - 2));

		/* Create the main screen, first query how wide the
		 * current overscan settings allow a screen to be.
		 */

	if(!QueryOverscan(Mode,&DisplayClip,OSCAN_TEXT))
		return(RETURN_FAIL + 12);

		/* Open a centered screen. */

	Differ = ((DisplayClip . MaxX - DisplayClip . MinX + 1) - Square) >> 1;

	DisplayClip . MinX += Differ;
	DisplayClip . MaxX -= Differ;

		/* Allocate a couple of colour specification structures. */

	if(!(ColorSpec = (struct ColorSpec *)AllocVec(sizeof(struct ColorSpec) * ((1 << Depth) + 1),MEMF_PUBLIC|MEMF_CLEAR)))
	{
			/* Open a screen with monochrome system imagery. */

		if(!(Screen = OpenScreenTags(NULL,
			SA_Width,	Square,
			SA_Height,	Square,
			SA_DClip,	&DisplayClip,
			SA_Left,	DisplayClip . MinX,
			SA_Depth,	Depth,
			SA_DisplayID,	Mode,
			SA_Font,	&DefaultFont,
			SA_ShowTitle,	FALSE,
			SA_Quiet,	TRUE,
			SA_AutoScroll,	TRUE,
			SA_Behind,	TRUE,
		TAG_END)))
			return(RETURN_FAIL + 13);
	}
	else
	{
			/* Set up the colour specifications to open
			 * an entirely black screen.
			 */

		for(i = 0 ; i < (1 << Depth) ; i++)
			ColorSpec[i] . ColorIndex = i;

		ColorSpec[i] . ColorIndex = -1;

			/* Open a screen with monochrome system imagery. */

		if(!(Screen = OpenScreenTags(NULL,
			SA_Width,	Square,
			SA_Height,	Square,
			SA_DClip,	&DisplayClip,
			SA_Left,	DisplayClip . MinX,
			SA_Depth,	Depth,
			SA_DisplayID,	Mode,
			SA_Font,	&DefaultFont,
			SA_ShowTitle,	FALSE,
			SA_Quiet,	TRUE,
			SA_AutoScroll,	TRUE,
			SA_Behind,	TRUE,
			SA_Colors,	ColorSpec,
		TAG_END)))
		{
			FreeVec(ColorSpec);

			return(RETURN_FAIL + 13);
		}
		else
			FreeVec(ColorSpec);
	}

		/* Set up the remaining data. */

	if(!(VisualInfo = GetVisualInfo(Screen,TAG_DONE)))
		return(RETURN_FAIL + 14);

	if(!(Menu = CreateMenus(MandelbrotMenu,
		GTMN_FrontPen, 0,
	TAG_DONE)))
		return(RETURN_FAIL + 15);

	if(!LayoutMenus(Menu,	VisualInfo,
		GTMN_TextAttr,	&DefaultFont,
	TAG_DONE))
		return(RETURN_FAIL + 16);

	if(!(Window = OpenWindowTags(NULL,
		WA_Width,	Screen -> Width,
		WA_Height,	Screen -> Height,
		WA_Borderless,	TRUE,
		WA_Backdrop,	TRUE,
		WA_IDCMP,	IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_MENUPICK | IDCMP_RAWKEY | IDCMP_VANILLAKEY | IDCMP_MENUVERIFY,
		WA_RMBTrap,	TRUE,
		WA_CustomScreen,Screen,
	TAG_END)))
		return(RETURN_FAIL + 17);

		/* Create a bitmap which is exactly as wide as the
		 * main screen, but only a single line high.
		 */

	InitBitMap(&LineBitMap,Depth,Square,1);

		/* Allocate space for the bitmap, note that the
		 * bitplanes are to reside in fast ram.
		 */

	if(LineBitMap . Planes[0] = AllocVec(LineBitMap . BytesPerRow * LineBitMap . Depth,MEMF_PUBLIC|MEMF_CLEAR|MEMF_FAST))
	{
		for(i = 1 ; i < LineBitMap . Depth ; i++)
			LineBitMap . Planes[i] = &LineBitMap . Planes[i - 1][LineBitMap . BytesPerRow];
	}

	SetMenuStrip(Window,Menu);

	OffMenu(Window,FULLMENUNUM(0,9,0));

	RPort = &Screen -> RastPort;
	VPort = &Screen -> ViewPort;

	SetRast(RPort,0);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ThisProcess -> pr_WindowPtr = (APTR)Window;

	return(0);
}

	/* CloseAll(LONG ReturnCode):
	 *
	 *	Close all remaining resources and terminate the program.
	 */

VOID __regargs
CloseAll(LONG ReturnCode)
{
	SetTaskPri(ThisProcess,OldPri);

	if(GlobalBitMap)
	{
		DeleteBitMap(GlobalBitMap);

		GlobalBitMap = NULL;
	}

	StopAnim();

	if(CycleInterrupt)
	{
		DoCycle = FALSE;

		if(CycleInterrupt -> is_Code)
			RemIntServer(INTB_VERTB,CycleInterrupt);

		FreeVec(CycleInterrupt);
	}

	if(Wave)
		FreeVec(Wave);

	GfxCleanup();

	if(BackupBitMap)
		DeleteBitMap(BackupBitMap);

	if(TimeRequest)
	{
		if(TimeRequest -> tr_node . io_Device)
			CloseDevice(TimeRequest);

		DeleteIORequest(TimeRequest);
	}

	if(TimePort)
		DeleteMsgPort(TimePort);

	if(IconBase)
		CloseLibrary(IconBase);

	if(AslBase)
		CloseLibrary(AslBase);

	if(IFFParseBase)
		CloseLibrary(IFFParseBase);

	if(GadToolsBase)
		CloseLibrary(GadToolsBase);

	if(GfxBase)
		CloseLibrary(GfxBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	exit(ReturnCode);
}

	/* OpenAll():
	 *
	 *	Open libraries and other data which does not
	 *	require changing when the display modes are
	 *	altered.
	 */

VOID
OpenAll()
{
	ThisProcess = (struct Process *)FindTask(NULL);

	OldPri = SetTaskPri(ThisProcess,0);

	OldPtr = ThisProcess -> pr_WindowPtr;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)))
		CloseAll(RETURN_FAIL + 0);

	if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37)))
		CloseAll(RETURN_FAIL + 1);

	if(!(GadToolsBase = OpenLibrary("gadtools.library",37)))
		CloseAll(RETURN_FAIL + 2);

	if(!(IFFParseBase = OpenLibrary("iffparse.library",37)))
		CloseAll(RETURN_FAIL + 3);

	if(!(AslBase = OpenLibrary("asl.library",37)))
		CloseAll(RETURN_FAIL + 4);

	if(!(TimePort = (struct MsgPort *)CreateMsgPort()))
		CloseAll(RETURN_FAIL + 5);

	if(!(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest))))
		CloseAll(RETURN_FAIL + 6);

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,0))
		CloseAll(RETURN_FAIL + 7);

	if(!(BackupBitMap = CreateBitMap(5,25 * 8,8)))
		CloseAll(RETURN_FAIL + 8);

	if(!(CycleInterrupt = AllocVec(sizeof(struct Interrupt),MEMF_PUBLIC|MEMF_CLEAR)))
		CloseAll(RETURN_FAIL + 9);

	if(!(Wave = (BYTE *)AllocVec(16384,MEMF_PUBLIC|MEMF_CLEAR)))
		CloseAll(RETURN_FAIL + 10);

	if(!(IconBase = OpenLibrary("icon.library",37)))
		CloseAll(RETURN_FAIL + 11);

	TimerBase = &TimeRequest -> tr_node . io_Device -> dd_Library;

	CycleInterrupt -> is_Node . ln_Name	= "Mandelbrot Cycling";
	CycleInterrupt -> is_Node . ln_Type	= NT_INTERRUPT;
	CycleInterrupt -> is_Code		= (APTR)CycleServer;

	AddIntServer(INTB_VERTB,CycleInterrupt);
}

	/* CopyLine(struct BitMap *BitMap,WORD Line):
	 *
	 *	Copies the contents of the single line bitmap to a given
	 *	line on screen.
	 */

VOID __regargs
CopyLine(struct BitMap *BitMap,WORD Line)
{
	WORD i;

	Line *= BitMap -> BytesPerRow;

	for(i = 0 ; i < BitMap -> Depth ; i++)
		CopyMemQuick(BitMap -> Planes[i],&RPort -> BitMap -> Planes[i][Line],BitMap -> BytesPerRow);
}

	/* GetTime():
	 *
	 *	Asks the timer.device for the current system time.
	 */

VOID
GetTime()
{
	TimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

	DoIO(TimeRequest);
}

	/* BlockWindow():
	 *
	 *	Blocks the menu bar of the window and sets the busy
	 *	mouse pointer.
	 */

VOID
BlockWindow()
{
	HoldIt = TRUE;

	SetWait(Window);

	Window -> Flags |= WFLG_RMBTRAP;
}

	/* ReleaseWindow():
	 *
	 *	Reenables the menu bar of the window and resets the
	 *	mouse pointer to the default mouse pointer.
	 */

VOID
ReleaseWindow()
{
	ClearPointer(Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	HoldIt = FALSE;
}

	/* GetFile():
	 *
	 *	Get a file name using the asl.library file requester.
	 */

BYTE
GetFile(UBYTE *Title,UBYTE *Directory,UBYTE *Name,UBYTE *Buffer,UBYTE *Pattern,BYTE SaveFlag)
{
	struct FileRequester	*AslFileRequest;
	BYTE			 Result = FALSE;
	LONG			 Flags;

	if(!Directory[0])
		NameFromLock(ThisProcess -> pr_CurrentDir,Directory,256);

	if(Pattern)
	{
		Flags = FILF_PATGAD;

		if(!Pattern[0])
			Pattern = "~(#?.info)";
	}
	else
	{
		Flags = 0;

		Pattern = "~(#?.info)";
	}

	if(SaveFlag)
		Flags |= FILF_SAVE;

	if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
		ASL_Window,	Window,
		ASL_File,	Name,
		ASL_Dir,	Directory,
		ASL_Hail,	Title,
		ASL_FuncFlags,	Flags,
		ASL_Pattern,	Pattern,
		ASL_OKText,	SaveFlag ? "Save" : "Open",
		ASL_LeftEdge,	0,
		ASL_TopEdge,	0,
		ASL_Width,	Screen -> Width,
		ASL_Height,	Screen -> Height,
	TAG_DONE))
	{
		if(RequestFile(AslFileRequest))
		{
			if(AslFileRequest -> rf_File[0])
			{
				strcpy(Buffer,AslFileRequest -> rf_Dir);

				AddPart((UBYTE *)Buffer,(UBYTE *)AslFileRequest -> rf_File,256);

				Result = TRUE;
			}
		}
	}

	FreeFileRequest(AslFileRequest);

	return(Result);
}

	/* MyEasyRequest():
	 *
	 *	EasyRequestArgs stub routine.
	 */

WORD __stdargs
MyEasyRequest(UBYTE *Gadgets,UBYTE *Text,...)
{
	struct EasyStruct	Easy;
	WORD			Result;
	ULONG			IDCMP = NULL;
	va_list	 		VarArgs;

	Easy . es_StructSize	= sizeof(struct EasyStruct);
	Easy . es_Flags		= NULL;
	Easy . es_Title		= (UBYTE *)"MandelSquare";
	Easy . es_TextFormat	= (UBYTE *)Text;
	Easy . es_GadgetFormat	= (UBYTE *)Gadgets;

	va_start(VarArgs,Text);
	Result = EasyRequestArgs(Window,&Easy,&IDCMP,VarArgs);
	va_end(VarArgs);

	return(Result);
}

	/* FadeTo():
	 *
	 *	Fades from one colour table to another.
	 */

VOID
FadeTo(struct ViewPort *VPort,UWORD *From,UWORD *To,UBYTE NumColours,UBYTE ToCol,UBYTE FromCol)
{
	UWORD	FromTemp[32];
	UWORD	ToTemp[32];
	UBYTE	i,j;
	BYTE	R1,G1,B1,R2,G2,B2;

	if(NumColours > 32)
		NumColours = 32;

	if(From)
		CopyMem(&From[0],&FromTemp[0],sizeof(UWORD) * NumColours);
	else
		memset(&FromTemp[0],FromCol,sizeof(UWORD) * NumColours);

	if(To)
		CopyMem(&To[0],&ToTemp[0],sizeof(UWORD) * NumColours);
	else
		memset(&ToTemp[0],ToCol,sizeof(UWORD) * NumColours);

	for(j = 0 ; j < 16 ; j++)
	{
		for(i = 0 ; i < NumColours ; i++)
		{
			R1 = (FromTemp[i] >> 8)	& 0xF;
			G1 = (FromTemp[i] >> 4)	& 0xF;
			B1 = (FromTemp[i])	& 0xF;

			R2 = (ToTemp[i] >> 8)	& 0xF;
			G2 = (ToTemp[i] >> 4)	& 0xF;
			B2 = (ToTemp[i])	& 0xF;

			if(R1 > R2)
				R1--;

			if(G1 > G2)
				G1--;

			if(B1 > B2)
				B1--;

			if(R1 < R2)
				R1++;

			if(G1 < G2)
				G1++;

			if(B1 < B2)
				B1++;

			FromTemp[i] = (R1 << 8) | (G1 << 4) | B1;
		}

		Delay(2);

		LoadRGB4(VPort,FromTemp,NumColours);
	}
}

	/* Random(LONG MaxValue)
	 *
	 *	Small random number generator.
	 */

ULONG __regargs
Random(LONG MaxValue)
{
	STATIC ULONG RandomSeed = 213567657;

	RandomSeed = RandomSeed * custom . vhposr + 8754331;

	return(RandomSeed % MaxValue);
}

	/* CreateAllCoordsGadgets():
	 *
	 *	Creates all gadgets required by the coordinates control panel.
	 */

struct Gadget *
CreateAllCoordsGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;
	UBYTE			 Buffer[40];

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= COORDS_WIDTH - 20;
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetText	= "Real Part (X-Coordinate)";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_ABOVE;
		NewGadget . ng_LeftEdge		= 10;
		NewGadget . ng_TopEdge		= 2 + TopEdge + 14;

		sprintf(Buffer,"%g",MinReal);

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		BailOut();

		NewGadget . ng_GadgetText	= "Imaginary Part (Y-Coordinate)";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 4 + 15;

		sprintf(Buffer,"%g",MinImaginary);

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Width & Height";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 4 + 15;

		sprintf(Buffer,"%g",RealWidth);

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,		40,
			GTST_String,		Buffer,
			STRINGA_Justification,	STRINGCENTER,
		TAG_DONE);

		NewGadget . ng_Width		= 68;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Accept";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_TopEdge		= COORDS_HEIGHT - 3 - NewGadget . ng_Height;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);

		NewGadget . ng_GadgetText	= "Cancel";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= COORDS_WIDTH - 10 - NewGadget . ng_Width;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);
	}

	return(Gadget);
}

	/* CreateAllPaletteGadgets():
	 *
	 *	Creates all gadgets required by the palette control panel.
	 */

struct Gadget *
CreateAllPaletteGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= 60;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Red  ";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= PLACETEXT_LEFT;
		NewGadget . ng_LeftEdge		= 18 + 8 * strlen(NewGadget . ng_GadgetText);
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		RedLevel,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Green";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 3;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		GreenLevel,
		TAG_DONE);

		NewGadget . ng_GadgetText	= "Blue ";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_TopEdge		= Gadget -> TopEdge + Gadget -> Height + 3;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(SLIDER_KIND,Gadget,&NewGadget,
			GTSL_Min,		0,
			GTSL_Max,		(1 << Depth) - 3,
			GTSL_Level,		BlueLevel,
		TAG_DONE);
	}

	return(Gadget);
}

	/* CreateAllScrollGadgets():
	 *
	 *	Creates all the gadgets required by the scroll amount
	 *	control panel.
	 */

struct Gadget *
CreateAllScrollGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo,UWORD TopEdge)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;
	UWORD			 Counter = 0;

	if(Gadget = CreateContext(GadgetList))
	{
		NewGadget . ng_Width		= 44;
		NewGadget . ng_Height		= 14;
		NewGadget . ng_GadgetText	= "Scroll Amount";
		NewGadget . ng_TextAttr		= &DefaultFont;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= (strlen(NewGadget . ng_GadgetText) + 2) * 8 + 1;
		NewGadget . ng_TopEdge		= 1 + TopEdge;

		GadgetArray[Counter++] = Gadget = CreateGadget(INTEGER_KIND,Gadget,&NewGadget,
			GTIN_MaxChars,	3,
			GTIN_Number,	(LONG)MaxScroll,
		TAG_DONE);

		NewGadget . ng_Width		= 52;
		NewGadget . ng_Height		= 12;
		NewGadget . ng_GadgetText	= "Accept";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= 10;
		NewGadget . ng_TopEdge		= SCROLL_HEIGHT - 3 - NewGadget . ng_Height;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);

		NewGadget . ng_GadgetText	= "Cancel";
		NewGadget . ng_GadgetID		= Counter;
		NewGadget . ng_LeftEdge		= SCROLL_WIDTH - 10 - NewGadget . ng_Width;

		BailOut();

		GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,
			TAG_DONE);
	}

	return(Gadget);
}

	/* InfoText():
	 *
	 *	Renders a small position and size information text into
	 *	the left bottom corner of the screen.
	 */

VOID
InfoText()
{
	struct TextFont	*OldFont;
	UBYTE		 Buffer[80];
	WORD		 i;
	struct RastPort	*RPort = Window -> RPort;

	BlockWindow();

	OldFont = RPort -> Font;

	SetFont(RPort,&DigitFont);
	SetAPen(RPort,1);

	sprintf(Buffer,"(/%1.04e )/%1.04e %1.04e*%1.04e",MinReal,MinImaginary,RealWidth,ImaginaryHeight);

	for(i = 0 ; i < strlen(Buffer) ; i++)
	{
		if(Buffer[i] == 'e')
			Buffer[i] = '$';
	}

	SetDrMd(RPort,JAM1);

	Move(RPort,1,Window -> Height - 2);
	Text(RPort,Buffer,strlen(Buffer));

	SetFont(RPort,OldFont);
	SetDrMd(RPort,JAM2);
}

	/* CalculateFrames():
	 *
	 *	Precalculates the number of pictures to be generated
	 *	when zooming to a specific location in the Mandelbrot
	 *	set.
	 */

WORD
CalculateFrames(double MinReal,double MinImaginary,double Size)
{
	double	Data[3],Div,Value,FourPixels,GlobalSize,GlobalMinReal,GlobalMinImaginary;
	BYTE	i,Render;
	WORD	Count = 0;

	GlobalSize		= LastSize;
	GlobalMinReal		= LastMinReal;
	GlobalMinImaginary	= LastMinImaginary;

	do
	{
		Data[0]	= GlobalSize - Size;
		Data[1]	= MinReal - GlobalMinReal;
		Data[2] = MinImaginary - GlobalMinImaginary;

		Value	= fabs(Data[0]);
		Render	= FALSE;

		for(i = 0 ; i < 3 ; i++)
		{
			if(Data[i] != 0.0)
				Render = TRUE;

			if(fabs(Data[i]) > Value)
				Value = fabs(Data[i]);
		}

		if(Render)
		{
			FourPixels	= MaxScroll * (GlobalSize / (double)Screen -> Width);
			Div		= Value;

			if(Value > FourPixels)
				Value = FourPixels;

			Div /= Value;

			GlobalSize		-= Data[0] / Div;
			GlobalMinReal		+= Data[1] / Div;
			GlobalMinImaginary	+= Data[2] / Div;

			Count++;
		}
	}
	while(Render);

	return(Count);
}

	/* CheckAbort():
	 *
	 *	Provides a safe callback routine to allow the anim
	 *	player to check for a user abort.
	 */

BYTE
CheckAbort()
{
	if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
	{
		struct IntuiMessage	*Massage;
		ULONG			 Class,Code;

		while(Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			GT_ReplyIMsg(Massage);

			if((Class == IDCMP_MOUSEBUTTONS && !(Code & IECODE_UP_PREFIX)) || Class == IDCMP_VANILLAKEY)
				return(TRUE);
		}
	}
	else
		return(FALSE);
}

	/* ReplayAnim():
	 *
	 *	Loads and replays an animation file.
	 */

BYTE
ReplayAnim()
{
	BYTE	 Success = FALSE;
	UBYTE	*DummyChar;

	strcpy(DummyBuffer,AnimPlayName);

	DummyChar = PathPart(DummyBuffer);

	*DummyChar = 0;

	if(GetFile("Replay Animation",DummyBuffer,FilePart(AnimPlayName),DummyBuffer,"~(#?.info)",FALSE))
	{
		BPTR File;

		if(File = Open(DummyBuffer,MODE_OLDFILE))
		{
			if(CreateBuffer())
			{
				if(OpenPreLoadIFF(File))
				{
					strcpy(AnimPlayName,DummyBuffer);

					SetClear(Window);

					SwapBits();

					Success = TRUE;

					while(PlayAnim());

					ClosePreLoadIFF();
				}
				else
					MyEasyRequest("Continue","Could not read file\n%s.",FilePart(DummyBuffer));

				SetWait(Window);

				DeleteBuffer();
			}
			else
				MyEasyRequest("Continue","Not enough memory for\ndisplay buffer.");

			Close(File);
		}
		else
			MyEasyRequest("Continue","Error opening file\n%s.",FilePart(DummyBuffer));
	}

	return(Success);
}

	/* RunAnim():
	 *
	 *	This routine calculates the single animation frames and
	 *	stores them in the anim file.
	 */

VOID
RunAnim()
{
	double	Data[3],Value,Div,SomePixels;
	BYTE	Render,i;

	LastSize		= RealWidth;
	LastMinReal		= MinReal;
	LastMinImaginary	= MinImaginary;

	NewCoords = FALSE;

	while(AnimRunning && !NewCoords)
		Zoom();

/*	Coords();*/

	if(NewCoords)
	{
		if(!AnimOpened)
		{
			UBYTE *DummyChar;

			AnimRunning = FALSE;

			strcpy(DummyBuffer,AnimSaveName);

			DummyChar = PathPart(DummyBuffer);

			*DummyChar = 0;

			if(GetFile("Begin Animation",DummyBuffer,FilePart(AnimSaveName),DummyBuffer,"~(#?.info)",FALSE))
			{
				if(Frame)
				{
					WORD	X,Y,Width;
					double	Pixel;

					Pixel	= LastSize / (double)Screen -> Width;

					X	= (WORD)(fabs(LastMinReal - MainMinReal) / Pixel);
					Y	= (WORD)(fabs(LastMinImaginary - MainMinImaginary) / Pixel);
					Width	= (WORD)(MainRealWidth / Pixel);

					if(X || Y || Width != Screen -> Width)
					{
						SetAPen(Window -> RPort,1);
						DrawSquare(Window -> RPort,X,Y,Width);
					}
				}

				if(Include)
					InfoText();

				if(AnimOpened = AnimOpen(DummyBuffer,VPort,RPort))
				{
					if(!ThisProcess -> pr_CLI)
						PutDiskObject(DummyBuffer,&AnimIcon);

					OffMenu(Window,FULLMENUNUM(0, 0,0));
					OffMenu(Window,FULLMENUNUM(0, 1,0));
					OffMenu(Window,FULLMENUNUM(0, 2,0));
					OffMenu(Window,FULLMENUNUM(0, 8,0));
					OnMenu (Window,FULLMENUNUM(0, 9,0));
					OffMenu(Window,FULLMENUNUM(0,11,0));
					OffMenu(Window,FULLMENUNUM(0,12,0));
					OffMenu(Window,FULLMENUNUM(0,14,0));

					OffMenu(Window,FULLMENUNUM(1, 0,0));
					OffMenu(Window,FULLMENUNUM(1, 1,0));
					OffMenu(Window,FULLMENUNUM(1, 2,0));
					OffMenu(Window,FULLMENUNUM(1, 5,0));
					OffMenu(Window,FULLMENUNUM(1, 7,0));
					OffMenu(Window,FULLMENUNUM(1, 8,0));
					OffMenu(Window,FULLMENUNUM(1,10,0));

					AnimRunning = TRUE;

					strcpy(AnimSaveName,DummyBuffer);
				}
			}
		}

		if(AnimRunning)
		{
			RealWidth		= LastSize;
			ImaginaryHeight		= LastSize;

			MinReal			= LastMinReal;
			MinImaginary		= LastMinImaginary;

			LastSize		= MainRealWidth;
			LastMinReal		= MainMinReal;
			LastMinImaginary	= MainMinImaginary;

			do
			{
				Data[0]	= RealWidth - LastSize;
				Data[1]	= LastMinReal - MinReal;
				Data[2] = LastMinImaginary - MinImaginary;

				Value	= fabs(Data[0]);
				Render	= FALSE;

				for(i = 0 ; i < 3 ; i++)
				{
					if(Data[i] != 0.0)
						Render = TRUE;

					if(fabs(Data[i]) > Value)
						Value = fabs(Data[i]);
				}

				if(Render)
				{
					SomePixels	= MaxScroll * (RealWidth / (double)Screen -> Width);
					Div		= Value;

					if(Value > SomePixels)
						Value = SomePixels;

					Div /= Value;

					RealWidth	-= Data[0] / Div;
					ImaginaryHeight	-= Data[0] / Div;
					MinReal		+= Data[1] / Div;
					MinImaginary	+= Data[2] / Div;

					RealStep	= RealWidth / (double)Window -> Width;
					ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

					AreaActive	= FALSE;
					Running		= TRUE;

					FullPicture	= TRUE;

					SetRast(Window -> RPort,0);

					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,0,Window -> Width >> 1,Window -> Height >> 1);
					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,0,Window -> Width >> 1,Window -> Height >> 1);

					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);
					Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);

					if(FullPicture)
					{
						BlockWindow();

						if(Frame)
						{
							WORD	X,Y,Width;
							double	Pixel;

							Pixel	= RealWidth / (double)Screen -> Width;

							X	= (WORD)(fabs(MinReal - LastMinReal) / Pixel);
							Y	= (WORD)(fabs(MinImaginary - LastMinImaginary) / Pixel);
							Width	= (WORD)(LastSize / Pixel);

							if(X || Y || Width != Screen -> Width)
							{
								SetAPen(Window -> RPort,1);
								DrawSquare(Window -> RPort,X,Y,Width);
							}
						}

						if(Include)
							InfoText();

						AnimAdd(RPort);

						ReleaseWindow();
					}

					if(!AnimRunning)
						break;
				}
			}
			while(Render);

			if(!Render)
			{
				MoveScreen(Screen,0,-Screen -> TopEdge);

				ScreenToFront(Screen);

				ActivateWindow(Window);

				Delay(10);

				DisplayBeep(Screen);
			}
		}

		AreaActive	= FALSE;
		Running		= FALSE;
	}
	else
		StopAnim();
}

	/* StopAnim():
	 *
	 *	Stops the animation generation.
	 */

VOID
StopAnim()
{
	if(AnimRunning)
	{
		AnimRunning = FALSE;

		OnMenu (Window,FULLMENUNUM(0, 0,0));
		OnMenu (Window,FULLMENUNUM(0, 1,0));
		OnMenu (Window,FULLMENUNUM(0, 2,0));
		OnMenu (Window,FULLMENUNUM(0, 8,0));
		OffMenu(Window,FULLMENUNUM(0, 9,0));
		OnMenu (Window,FULLMENUNUM(0,11,0));
		OnMenu (Window,FULLMENUNUM(0,12,0));
		OnMenu (Window,FULLMENUNUM(0,14,0));

		OnMenu (Window,FULLMENUNUM(1, 0,0));
		OnMenu (Window,FULLMENUNUM(1, 1,0));
		OnMenu (Window,FULLMENUNUM(1, 2,0));
		OnMenu (Window,FULLMENUNUM(1, 5,0));
		OnMenu (Window,FULLMENUNUM(1, 7,0));
		OnMenu (Window,FULLMENUNUM(1, 8,0));
		OnMenu (Window,FULLMENUNUM(1,10,0));

		AnimClose();

		AnimOpened = FALSE;
	}
}

	/* Coords():
	 *
	 *	This routine implements the coordinates control panel.
	 */

VOID
Coords()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[5];
	struct Window	*CoordsWindow;

	double		 TempMinReal,TempMinImaginary,
			 TempRealWidth,TempImaginaryHeight;

	BYTE		 MadeChanges = FALSE;

	TempMinReal		= MinReal;
	TempMinImaginary	= MinImaginary;
	TempRealWidth		= RealWidth;
	TempImaginaryHeight	= ImaginaryHeight;

	if(CreateAllCoordsGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(CoordsWindow = OpenWindowTags(NULL,
			WA_Width,	COORDS_WIDTH,
			WA_Height,	COORDS_HEIGHT,
			WA_Top,		(Screen -> Height - COORDS_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - COORDS_WIDTH) >> 1,
			WA_Activate,	TRUE,
			WA_DragBar,	FALSE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_IDCMP,	IDCMP_CLOSEWINDOW | BUTTONIDCMP | STRINGIDCMP,
			WA_Title,	"Coordinates",
			WA_CustomScreen,Screen,
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			AddGList(CoordsWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,CoordsWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(CoordsWindow,NULL);

			ActivateGadget(GadgetArray[0],CoordsWindow,NULL);

			while(!Terminated)
			{
				WaitPort(CoordsWindow -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(CoordsWindow -> UserPort)))
				{
					Class	= Massage -> Class;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_GADGETUP)
					{
						double Value;

						switch(Gadget -> GadgetID)
						{
							case 3:	if(MadeChanges)
								{
									MinReal		= TempMinReal;
									MinImaginary	= TempMinImaginary;
									RealWidth	= TempRealWidth;
									ImaginaryHeight	= TempImaginaryHeight;

									RealStep	= RealWidth / (double)Window -> Width;
									ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

									MainMinReal		= MinReal;
									MainMinImaginary	= MinImaginary;
									MainRealWidth		= RealWidth;
									MainImaginaryHeight	= ImaginaryHeight;

									NewCoords	= TRUE;
									Running		= FALSE;
									AreaActive	= FALSE;
								}

								Terminated = TRUE;
								break;

							case 4:	Terminated = TRUE;
								break;

							default:Value = atof(((struct StringInfo *)Gadget -> SpecialInfo) -> Buffer);

								MadeChanges = TRUE;

								switch(Gadget -> GadgetID)
								{
									case 0:	TempMinReal		= Value;
										break;

									case 1:	TempMinImaginary	= Value;
										break;

									case 2:	TempRealWidth		= Value;
										TempImaginaryHeight	= Value;
										break;
								}

								break;
						}
					}
				}
			}

			CloseWindow(CoordsWindow);
		}
	}

	FreeGadgets(GadgetList);
}

	/* Palette():
	 *
	 *	This routine implements the palette control panel.
	 */

VOID
Palette()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[3];
	struct Window	*Window;

	if(CreateAllPaletteGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(Window = OpenWindowTags(NULL,
			WA_Width,	PALETTE_WIDTH,
			WA_Height,	PALETTE_HEIGHT,

			WA_Top,		(Screen -> Height - PALETTE_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - PALETTE_WIDTH) >> 1,

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_CloseGadget, TRUE,
			WA_RMBTrap,	TRUE,

			WA_IDCMP,	IDCMP_CLOSEWINDOW | SLIDERIDCMP,
			WA_CustomScreen,Screen,

			WA_Title,	"Palette",
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class,Code;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;
			WORD			 i,Width = 120 / ((1 << Depth) - 2),Top = Screen -> WBorTop + Screen -> Font -> ta_YSize + 2;

			AddGList(Window,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,Window,NULL,(UWORD)-1);
			GT_RefreshWindow(Window,NULL);

			for(i = 2 ; i < (1 << Depth) ; i++)
			{
				SetAPen(Window -> RPort,i);

				RectFill(Window -> RPort,111 + 8 + (i - 2) * Width,Top,111 + 8 + (i - 1) * Width - 1,Window -> Height - Window -> BorderBottom - 2);
			}

			if(DoCycle)
			{
				DoCycle = FALSE;

				GetScreenPalette();

				FadeTo(&Screen -> ViewPort,ScreenPalette,SinPalette,1 << Depth,0,0);
			}
			else
				DoCycle = FALSE;

			UseWheel = FALSE;

			while(!Terminated)
			{
				WaitPort(Window -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort)))
				{
					Class	= Massage -> Class;
					Code	= Massage -> Code;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_MOUSEMOVE)
					{
						WORD Wrap = (1 << Depth) - 2;

						switch(Gadget -> GadgetID)
						{
							case 0:	RedLevel = Code;
								break;

							case 1:	GreenLevel = Code;
								break;

							case 2:	BlueLevel = Code;
								break;
						}

						SinPalette[0] = 0x000;
						SinPalette[1] = 0xFFF;

						for(i = 0 ; i < Wrap ; i++)
						{
							SinPalette[2 + i]  = Range[(RedLevel + i) % Wrap] << 8;
							SinPalette[2 + i] |= Range[(GreenLevel + i) % Wrap] << 4;
							SinPalette[2 + i] |= Range[(BlueLevel + i) % Wrap];
						}

						LoadRGB4(&Screen -> ViewPort,SinPalette,1 << Depth);
					}
				}
			}

			CloseWindow(Window);
		}
	}

	FreeGadgets(GadgetList);
}

	/* MaxScrollPanel():
	 *
	 *	This routine implements the scroll amount control panel.
	 */

VOID
MaxScrollPanel()
{
	struct Gadget	*GadgetList = NULL;
	struct Gadget	*GadgetArray[3];
	struct Window	*ScrollWindow;

	if(CreateAllScrollGadgets(&GadgetArray[0],&GadgetList,VisualInfo,Screen -> WBorTop + Screen -> Font -> ta_YSize + 1))
	{
		if(ScrollWindow = OpenWindowTags(NULL,
			WA_Width,	SCROLL_WIDTH,
			WA_Height,	SCROLL_HEIGHT,

			WA_Top,		(Screen -> Height - SCROLL_HEIGHT) >> 1,
			WA_Left,	(Screen -> Width - SCROLL_WIDTH) >> 1,

			WA_Activate,	TRUE,
			WA_DragBar,	TRUE,
			WA_DepthGadget,	TRUE,
			WA_CloseGadget,	TRUE,
			WA_RMBTrap,	TRUE,
			WA_CustomScreen,Screen,

			WA_IDCMP,	IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | BUTTONIDCMP | INTEGERIDCMP,

			WA_Title,	"Set Scroll Amount",
		TAG_DONE))
		{
			struct IntuiMessage	*Massage;
			ULONG			 Class;
			struct Gadget		*Gadget;
			BYTE			 Terminated = FALSE;

			AddGList(ScrollWindow,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
			RefreshGList(GadgetList,ScrollWindow,NULL,(UWORD)-1);
			GT_RefreshWindow(ScrollWindow,NULL);

			ActivateGadget(GadgetArray[0],ScrollWindow,NULL);

			while(!Terminated)
			{
				WaitPort(ScrollWindow -> UserPort);

				while(!Terminated && (Massage = (struct IntuiMessage *)GT_GetIMsg(ScrollWindow -> UserPort)))
				{
					Class	= Massage -> Class;
					Gadget	= (struct Gadget *)Massage -> IAddress;

					GT_ReplyIMsg(Massage);

					if(Class == IDCMP_CLOSEWINDOW)
						Terminated = TRUE;

					if(Class == IDCMP_ACTIVEWINDOW)
						ActivateGadget(GadgetArray[0],ScrollWindow,NULL);

					if(Class == IDCMP_GADGETUP)
					{
						if(Gadget -> GadgetID == 2)
							Terminated = TRUE;
						else
						{
							LONG Value = ((struct StringInfo *)GadgetArray[0] -> SpecialInfo) -> LongInt;
							BYTE GoodValue = TRUE;

							if(Value < 1)
							{
								GoodValue	= FALSE;
								Value		= 1;
							}

							if(Value >= Screen -> Width)
							{
								GoodValue	= FALSE;
								Value		= Screen -> Width - 1;
							}

							if(GoodValue)
							{
								MaxScroll	= (double)Value;
								Terminated	= TRUE;
							}
							else
							{
								GT_SetGadgetAttrs(GadgetArray[0],ScrollWindow,NULL,
									GTIN_Number,Value,
								TAG_DONE);

								DisplayBeep(Screen);

								ActivateGadget(GadgetArray[0],ScrollWindow,NULL);
							}
						}
					}
				}
			}

			CloseWindow(ScrollWindow);
		}
	}

	FreeGadgets(GadgetList);
}

	/* AreaZoom():
	 *
	 *	This routine handles the selection of an area on screen
	 *	to recalculate later.
	 */

VOID
AreaZoom()
{
	struct IntuiMessage	*Massage;
	ULONG			 Class,Code;
	UWORD			 X,Y,LastX,LastY;

	SetClear(Window);

	Code = NULL;

	Window -> Flags |= WFLG_RMBTRAP;

	ReportMouse(TRUE,Window);

	SetAPen(RPort,1);
	SetDrMd(RPort,COMPLEMENT);

	LastX	= Window -> MouseX;
	LastY	= Window -> MouseY;

	DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
	DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

	while(Code != SELECTUP)
	{
		WaitPort(Window -> UserPort);

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			X	= Massage -> MouseX;
			Y	= Massage -> MouseY;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MOUSEMOVE)
			{
				if(LastX != X)
				{
					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,X,0,X,Screen -> Height - 1);
				}

				if(LastY != Y)
				{
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);
					DrawLine(RPort,0,Y,Screen -> Width - 1,Y);
				}

				LastX = X;
				LastY = Y;
			}

			if(Class == IDCMP_MOUSEBUTTONS)
			{
				if(Code == SELECTDOWN)
				{
					UWORD	Left,Top,Width = 0,Height = 0;
					BYTE	DidBox = FALSE;

					Left	= X;
					Top	= Y;

					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

					while(Code != SELECTUP)
					{
						WaitPort(Window -> UserPort);

						while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
						{
							Class	= Massage -> Class;
							Code	= Massage -> Code;

							Y	= Massage -> MouseY;
							X	= Massage -> MouseX;

							ReplyMsg(&Massage -> ExecMessage);

							if(Class == IDCMP_MOUSEMOVE)
							{
								if(Y > Top && X > Left)
								{
									if(DidBox)
									{
										Move(RPort,Left,Top);
										Draw(RPort,Left + Width - 1,Top);
										Draw(RPort,Left + Width - 1,Top + Height - 1);
										Draw(RPort,Left,Top + Height - 1);
										Draw(RPort,Left,Top + 1);
									}

									Width	= X - Left + 1;
									Height	= Y - Top + 1;

									Move(RPort,Left,Top);
									Draw(RPort,Left + Width - 1,Top);
									Draw(RPort,Left + Width - 1,Top + Height - 1);
									Draw(RPort,Left,Top + Height - 1);
									Draw(RPort,Left,Top + 1);

									DidBox = TRUE;
								}

								Code = NULL;
							}
						}
					}

					if(DidBox)
					{
						Move(RPort,Left,Top);
						Draw(RPort,Left + Width - 1,Top);
						Draw(RPort,Left + Width - 1,Top + Height - 1);
						Draw(RPort,Left,Top + Height - 1);
						Draw(RPort,Left,Top + 1);
					}

					SetDrMd(RPort,JAM1);

					if(Width >= 2 && Height >= 2)
					{
						AreaLeft	= Left;
						AreaTop		= Top;

						AreaWidth	= Width;
						AreaHeight	= Height;

						NewCoords	= TRUE;
						Running		= FALSE;
						AreaActive	= TRUE;
					}
				}
			}
		}
	}

	ReportMouse(FALSE,Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ClearPointer(Window);
}

	/* Zoom():
	 *
	 *	This routine handles the selection of an area on screen
	 *	to zoom to later. Note that the area will be square.
	 */

VOID
Zoom()
{
	struct IntuiMessage	*Massage;
	ULONG			 Class,Code;
	UWORD			 X,Y,LastX,LastY;

	SetClear(Window);

	Code = NULL;

	Window -> Flags |= WFLG_RMBTRAP;

	ReportMouse(TRUE,Window);

	SetAPen(RPort,1);
	SetDrMd(RPort,COMPLEMENT);

	LastX	= Window -> MouseX;
	LastY	= Window -> MouseY;

	DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
	DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

	while(Code != SELECTUP)
	{
		WaitPort(Window -> UserPort);

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class	= Massage -> Class;
			Code	= Massage -> Code;

			X	= Massage -> MouseX;
			Y	= Massage -> MouseY;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MOUSEMOVE)
			{
				if(LastX != X)
				{
					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,X,0,X,Screen -> Height - 1);
				}

				if(LastY != Y)
				{
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);
					DrawLine(RPort,0,Y,Screen -> Width - 1,Y);
				}

				LastX = X;
				LastY = Y;
			}

			if(Class == IDCMP_MOUSEBUTTONS)
			{
				if(Code == SELECTDOWN)
				{
					UWORD	Left,Top,Width = 0,Height = 0;
					BYTE	DidBox = FALSE;

					DrawLine(RPort,LastX,0,LastX,Screen -> Height - 1);
					DrawLine(RPort,0,LastY,Screen -> Width - 1,LastY);

					Left	= X;
					Top	= Y;

					SetAPen(RPort,1);
					SetDrMd(RPort,COMPLEMENT);

					while(Code != SELECTUP)
					{
						WaitPort(Window -> UserPort);

						while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
						{
							Class	= Massage -> Class;
							Code	= Massage -> Code;

							Y	= Massage -> MouseY;

							ReplyMsg(&Massage -> ExecMessage);

							if(Class == IDCMP_MOUSEMOVE)
							{
								if(Y > Top)
								{
									if(DidBox)
									{
										Move(RPort,Left,Top);
										Draw(RPort,Left + Width - 1,Top);
										Draw(RPort,Left + Width - 1,Top + Height - 1);
										Draw(RPort,Left,Top + Height - 1);
										Draw(RPort,Left,Top + 1);
									}

									Width = Height = Y - Top + 1;

									Move(RPort,Left,Top);
									Draw(RPort,Left + Width - 1,Top);
									Draw(RPort,Left + Width - 1,Top + Height - 1);
									Draw(RPort,Left,Top + Height - 1);
									Draw(RPort,Left,Top + 1);

									DidBox = TRUE;
								}

								Code = NULL;
							}
						}
					}

					if(DidBox)
					{
						Move(RPort,Left,Top);
						Draw(RPort,Left + Width - 1,Top);
						Draw(RPort,Left + Width - 1,Top + Height - 1);
						Draw(RPort,Left,Top + Height - 1);
						Draw(RPort,Left,Top + 1);
					}

					SetDrMd(RPort,JAM1);

					if(Width >= 2 && Height >= 2)
					{
						BYTE Val;

						SetWait(Window);

						if(AnimRunning)
							Val = MyEasyRequest("Yes|Abort|No","Transition will consist\nof %ld images.\n\nDo you wish to use the\ncurrent coordinates?",CalculateFrames(MinReal + (double)Left * RealStep,MinImaginary + (double)Top * ImaginaryStep,(double)Width * RealStep));
						else
							Val = MyEasyRequest("Yes|No","Do you wish to use the\ncurrent coordinates?");

						switch(Val)
						{
							case 0:	break;

							case 1:	MinReal		= MinReal + (double)Left * RealStep;
								MinImaginary	= MinImaginary + (double)Top * ImaginaryStep;

								RealWidth	= (double)Width * RealStep;
								ImaginaryHeight	= (double)Height * ImaginaryStep;

								RealStep	= RealWidth / (double)Window -> Width;
								ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;

								MainMinReal		= MinReal;
								MainMinImaginary	= MinImaginary;
								MainRealWidth		= RealWidth;
								MainImaginaryHeight	= ImaginaryHeight;

								NewCoords	= TRUE;
								Running		= FALSE;
								AreaActive	= FALSE;
								break;

							case 2:	Running = FALSE;
								StopAnim();
								break;
						}
					}
				}
			}
		}
	}

	ReportMouse(FALSE,Window);

	Window -> Flags &= ~WFLG_RMBTRAP;

	ClearPointer(Window);
}

	/* HandleEvent():
	 *
	 *	The global input event (menus, key, mouse, etc.) handler.
	 */

VOID
HandleEvent()
{
	struct IntuiMessage	*Massage;
	struct MenuItem		*Item;
	ULONG			 Class,Code,Qualifier,MenuItem,Selected;
	UBYTE			*DummyChar;
	BYTE			 Waiting = FALSE;

	SetSignal(0,1 << Window -> UserPort -> mp_SigBit);

	do
	{
		while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class		= Massage -> Class;
			Code		= Massage -> Code;
			Qualifier	= Massage -> Qualifier;

			ReplyMsg(&Massage -> ExecMessage);

			if(Class == IDCMP_MENUVERIFY)
				Waiting = HoldIt = TRUE;

			if(Class == IDCMP_VANILLAKEY)
			{
				if(Code == '\t')
				{
					if(!DoCycle)
					{
						Forward = TRUE;

						DoCycle = TRUE;
					}
					else
						DoCycle = FALSE;
				}
			}

			if(Class == IDCMP_RAWKEY)
			{
				UWORD	Palette[32];
				BYTE	AltWheel = Wheel,i;

				if(Code == CURSORLEFT)
				{
					DoCycle = FALSE;

					Palette[0] = 0x000;
					Palette[1] = 0xFFF;

					if(UseWheel)
					{
						if(Wheel)
							Wheel--;
						else
							Wheel = 74;

						for(i = 2 ; i < 32 ; i++)
						{
							Palette[i] = Table[AltWheel];

							AltWheel = (AltWheel + 1) % 75;
						}
					}
					else
					{
						BYTE AltAdd = Add,Wrap = (1 << Depth) - 2;

						if(Add)
							Add--;
						else
							Add = Wrap - 1;

						for(i = 2 ; i < (1 << Depth) ; i++)
						{
							Palette[i] = SinPalette[AltAdd + 2];

							AltAdd = (AltAdd + 1) % Wrap;
						}
					}

					LoadRGB4(VPort,Palette,1 << Depth);
				}

				if(Code == CURSORRIGHT)
				{
					DoCycle = FALSE;

					Palette[0] = 0x000;
					Palette[1] = 0xFFF;

					if(UseWheel)
					{
						Wheel = (Wheel + 1) % 75;

						for(i = 2 ; i < 32 ; i++)
						{
							Palette[i] = Table[AltWheel];

							AltWheel = (AltWheel + 1) % 75;
						}
					}
					else
					{
						BYTE AltAdd = Add,Wrap = (1 << Depth) - 2;

						Add = (Add + 1) % Wrap;

						for(i = 2 ; i < (1 << Depth) ; i++)
						{
							Palette[i] = SinPalette[AltAdd + 2];

							AltAdd = (AltAdd + 1) % Wrap;
						}
					}

					LoadRGB4(VPort,Palette,1 << Depth);
				}

				if(Code == CURSORUP)
				{
					Forward = TRUE;

					DoCycle = TRUE;
				}

				if(Code == CURSORDOWN)
				{
					Forward = FALSE;

					DoCycle = TRUE;
				}
			}

			if(Class == IDCMP_MENUPICK)
			{
				Waiting = HoldIt = FALSE;

				if(GotClip)
				{
					BltBitMap(BackupBitMap,0,0,RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,8 * 25,8,0xC0,0xFF,NULL);

					GotClip = FALSE;
				}

				MenuItem = Code;

				while(MenuItem != MENUNULL)
				{
					if(Item = ItemAddress(Menu,MenuItem))
					{
						Selected = (ULONG)MENU_USERDATA(Item);

						switch(Selected)
						{
							case MEN_OPEN:	BlockWindow();

									strcpy(NameBuffer,LastName);

									DummyChar = PathPart(NameBuffer);

									*DummyChar = 0;

									if(GetFile("Open Picture",NameBuffer,FilePart(LastName),NameBuffer,"~(#?.info)",FALSE))
									{
										struct IFFHandle	*Handle;
										struct BitMap		*BitMap;

										if(Handle = OpenImageFile(NameBuffer))
										{
											struct BitMapHeader	BitMapHeader;
											struct MandelInfo	TempMandelInfo;
											BYTE			Colours = 1 << Depth,GotImage = FALSE;

											if(ReadImageHeader(Handle,&GlobalMode,&Colours,ColourTable,&BitMapHeader,&TempMandelInfo))
											{
												if(BitMapHeader . w == Window -> Width && BitMapHeader . h == Window -> Height)
												{
													if(BitMap = CreateBitMap(BitMapHeader . nPlanes,BitMapHeader . w,BitMapHeader . h))
													{
														if(ReadImageBody(Handle,BitMap,&BitMapHeader))
														{
															WORD i,Height = Screen -> Height;

															if(GetVPModeID(&Screen -> ViewPort) & LACE)
																Height >>= 1;

															DoCycle = FALSE;

															GetScreenPalette();

															FadeTo(&Screen -> ViewPort,ScreenPalette,ColourTable,1 << Depth,0,0);

															switch(Random(6))
															{
																case 0:	for(i = 0 ; i < (Square >> 1) ; i++)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,0,2 * i,      RPort,0,2 * i,      Screen -> Width,1,0xC0);
																		BltBitMapRastPort(BitMap,0,Square - 1 - 2 * i,RPort,0,Square - 1 - 2 * i,Screen -> Width,1,0xC0);
																	}

																	break;

																case 1:	for(i = 0 ; i < (Square >> 1) ; i++)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,2 * i,0,      RPort,2 * i,0,1,      Screen -> Height,0xC0);
																		BltBitMapRastPort(BitMap,Square - 1 - 2 * i,0,RPort,Square - 1 - 2 * i,0,1,Screen -> Height,0xC0);
																	}

																	break;

																case 2:	for(i = 0 ; i < (Square >> 1) ; i++)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,i,i,RPort,i,i,((Square >> 1) - i) * 2,1,0xC0);
																		BltBitMapRastPort(BitMap,i,Square - 1 - i,RPort,i,Square - 1 - i,((Square >> 1) - i) * 2,1,0xC0);

																		BltBitMapRastPort(BitMap,i,i,RPort,i,i,1,((Square >> 1) - i) * 2,0xC0);
																		BltBitMapRastPort(BitMap,Square - 1 - i,i,RPort,Square - 1 - i,i,1,((Square >> 1) - i) * 2,0xC0);
																	}

																	break;

																case 3:	for(i = (Square >> 1) - 1 ; i >= 0 ; i--)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,i,i,RPort,i,i,((Square >> 1) - i) * 2,1,0xC0);
																		BltBitMapRastPort(BitMap,i,Square - 1 - i,RPort,i,Square - 1 - i,((Square >> 1) - i) * 2,1,0xC0);

																		BltBitMapRastPort(BitMap,i,i,RPort,i,i,1,((Square >> 1) - i) * 2,0xC0);
																		BltBitMapRastPort(BitMap,Square - 1 - i,i,RPort,Square - 1 - i,i,1,((Square >> 1) - i) * 2,0xC0);
																	}

																	break;

																case 4:	for(i = 0 ; i < Square ; i++)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,      i,  0,RPort,      i,  0,1,(Square >> 1),0xC0);
																		BltBitMapRastPort(BitMap,Square - 1 - i,(Square >> 1),RPort,Square - 1 - i,(Square >> 1),1,(Square >> 1),0xC0);
																	}

																	break;

																case 5:	for(i = 0 ; i < Square ; i++)
																	{
																		WaitLine(Height);

																		BltBitMapRastPort(BitMap,  0,      i,RPort,  0,      i,(Square >> 1),1,0xC0);
																		BltBitMapRastPort(BitMap,(Square >> 1),Square - 1 - i,RPort,(Square >> 1),Square - 1 - i,(Square >> 1),1,0xC0);
																	}

																	break;
															}

															Wheel = 0;

															for(i = 0 ; i < 75 ; i++)
															{
																if(ColourTable[2] == Table[i])
																{
																	Wheel = i;
																	break;
																}
															}

															if(TempMandelInfo . Iterations)
															{
																MainMinReal		= MinReal		= TempMandelInfo . MinReal;
																MainMinImaginary	= MinImaginary		= TempMandelInfo . MinImaginary;
																MainRealWidth		= RealWidth		= TempMandelInfo . RealWidth;
																MainImaginaryHeight	= ImaginaryHeight	= TempMandelInfo . ImaginaryHeight;
																MaxIteration					= TempMandelInfo . Iterations;

																IterationSetup();

																RealStep	= RealWidth / (double)Window -> Width;
																ImaginaryStep	= ImaginaryHeight / (double)Window -> Height;
															}
															else
																MyEasyRequest("Continue","Could not read image\ncoordinates, previous values\nremain untouched.");

															strcpy(LastName,NameBuffer);

															Running = FALSE;

															AreaActive = FALSE;

															GotImage = TRUE;
														}
														else
															MyEasyRequest("Continue","Error reading file\n%s.",FilePart(NameBuffer));

														DeleteBitMap(BitMap);
													}
													else
														MyEasyRequest("Continue","Not enough memory!");
												}
												else
												{
													if(BitMapHeader . w == BitMapHeader . h && !(BitMapHeader . h % 4))
													{
														if(BitMap = CreateBitMap(BitMapHeader . nPlanes,BitMapHeader . w,BitMapHeader . h))
														{
															if(ReadImageBody(Handle,BitMap,&BitMapHeader))
															{
																WORD i;

																Wheel = 0;

																for(i = 0 ; i < 75 ; i++)
																{
																	if(ColourTable[2] == Table[i])
																	{
																		Wheel = i;
																		break;
																	}
																}

																if(TempMandelInfo . Iterations)
																{
																	MainMinReal		= MinReal		= TempMandelInfo . MinReal;
																	MainMinImaginary	= MinImaginary		= TempMandelInfo . MinImaginary;
																	MainRealWidth		= RealWidth		= TempMandelInfo . RealWidth;
																	MainImaginaryHeight	= ImaginaryHeight	= TempMandelInfo . ImaginaryHeight;
																	MaxIteration					= TempMandelInfo . Iterations;

																	RealStep		= RealWidth / (double)Window -> Width;
																	ImaginaryStep		= ImaginaryHeight / (double)Window -> Height;
																}
																else
																	MyEasyRequest("Continue","Could not read image\ncoordinates, previous values\nremain untouched.");

																strcpy(LastName,NameBuffer);

																Running		= FALSE;
																AreaActive	= FALSE;
																NewMode		= TRUE;
																DoCycle		= FALSE;

																GlobalBitMap	= BitMap;
																GlobalHeader	= BitMapHeader;
															}
															else
															{
																MyEasyRequest("Continue","Error reading file\n%s.",FilePart(NameBuffer));

																DeleteBitMap(BitMap);
															}
														}
														else
															MyEasyRequest("Continue","Not enough memory!");
													}
													else
														MyEasyRequest("Continue","Picture has wrong size!");
												}
											}
											else
												MyEasyRequest("Continue","Error reading file\n%s.",FilePart(NameBuffer));

											CloseImageFile(Handle);
										}
										else
											MyEasyRequest("Continue","Could not open file\n%s.",FilePart(NameBuffer));
									}

									ReleaseWindow();

									break;

							case MEN_SAVE:	BlockWindow();

									if(NameBuffer[0])
										goto SaveIt;

							case MEN_SAVEAS:BlockWindow();

									strcpy(NameBuffer,LastName);

									DummyChar = PathPart(NameBuffer);

									*DummyChar = 0;

									if(GetFile("Save Picture As",NameBuffer,FilePart(LastName),NameBuffer,"~(#?.info)",TRUE))
									{
SaveIt:										MandelInfo . MinReal		= MinReal;
										MandelInfo . MinImaginary	= MinImaginary;
										MandelInfo . RealWidth		= RealWidth;
										MandelInfo . ImaginaryHeight	= ImaginaryHeight;
										MandelInfo . Iterations		= MaxIteration;

										if(!SaveBitMap(RPort -> BitMap,&Screen -> ViewPort,0,0,Window -> Width,Window -> Height,Screen -> Width,Screen -> Height,NameBuffer,&MandelInfo))
											MyEasyRequest("Continue","Could not save file\n%s.",FilePart(NameBuffer));
										else
										{
											if(!ThisProcess -> pr_CLI)
												PutDiskObject(NameBuffer,&PictureIcon);

											strcpy(LastName,NameBuffer);
										}
									}

									ReleaseWindow();
									break;

							case MEN_PRI0:	SetTaskPri(ThisProcess,-5);
									break;

							case MEN_PRI1:	SetTaskPri(ThisProcess,0);
									break;

							case MEN_PRI2:	SetTaskPri(ThisProcess,5);
									break;

							case MEN_PLAY:	BlockWindow();

									ReplayAnim();

									ReleaseWindow();

									break;

							case MEN_START:	if(!AnimRunning)
									{
										Running		= FALSE;
										FullPicture	= FALSE;
										AnimRunning	= TRUE;
									}

									break;

							case MEN_STOP:	BlockWindow();

									StopAnim();

									Running = FALSE;

									FullPicture = FALSE;

									ReleaseWindow();

									break;

							case MEN_SCROLL:BlockWindow();

									MaxScrollPanel();

									ReleaseWindow();

									break;

							case MEN_FRAME:	if(Item -> Flags & CHECKED)
										Frame = TRUE;
									else
										Frame = FALSE;

									break;

							case MEN_INCLUDE:
									if(Item -> Flags & CHECKED)
										Include = TRUE;
									else
										Include = FALSE;

									break;

							case MEN_RESOLUTION:
									NewMode = TRUE;
									Running = FALSE;
									DoCycle = FALSE;
									break;

							case MEN_ABOUT:	BlockWindow();

									MyEasyRequest("So what?","       MandelSquare\n\nYet another Mandelbrot set\n   exploration program\n\n        Written by\n   Olaf `Olsen' Barthel\n\n     © Copyright 1991\n");

									ReleaseWindow();
									break;

							case MEN_QUIT:	BlockWindow();

									if(MyEasyRequest("Yes|No","Do you really wish to\nleave this program?"))
										CloseAll(RETURN_OK);

									ReleaseWindow();
									break;

							case MEN_ZOOM:	Zoom();
									break;

							case MEN_CLIP:	AreaZoom();
									Faster = TRUE;
									break;

							case MEN_RERUN:	AreaZoom();
									Faster = FALSE;
									break;

							case MEN_PRECISE:
									if(Item -> Flags & CHECKED)
										Precise = TRUE;
									else
										Precise = FALSE;

									break;

							case MEN_COORDS:BlockWindow();
									Coords();
									ReleaseWindow();
									break;

							case MEN_PALETTE:
									BlockWindow();
									Palette();
									ReleaseWindow();
									break;

							case MEN_SPEC:	DoCycle = FALSE;

									if(Item -> Flags & CHECKED)
										UseWheel = TRUE;
									else
										UseWheel = FALSE;

									GetScreenPalette();

									if(UseWheel)
										FadeTo(&Screen -> ViewPort,ScreenPalette,WheelPalette,1 << Depth,0,0);
									else
										FadeTo(&Screen -> ViewPort,ScreenPalette,SinPalette,1 << Depth,0,0);

									break;

							case MEN_RUN:	Running ^= TRUE;

									if(!Running)
										FullPicture = FALSE;

									break;

							default:	if(Selected >= 1 && Selected <= 16)
										MaxIteration = (1 << Selected);

									break;
						}

						MenuItem = Item -> NextSelect;
					}
					else
						break;
				}
			}
		}

		if(Waiting)
			WaitPort(Window -> UserPort);
	}
	while(Waiting);
}

#if 0

	/* Iterate(double RealValue,double ImaginaryValue):
	 *
	 *	This is the `C' version of the Iterate routine
	 *	present in Iterate.asm. I've commented it out
	 *	and included it only for historical reasons.
	 */

BYTE
Iterate(double RealValue,double ImaginaryValue)
{
	double	Real = 0,Imaginary = 0,RealTemp,ImaginaryTemp;
	WORD	i = 0;

	for(;;)
	{
		ImaginaryTemp 	= Imaginary * Imaginary;
		RealTemp	= Real * Real;

		if(fabs(RealTemp + ImaginaryTemp) > 4.0)
			return(Wave[i]);

		if(++i >= MaxIteration)
			return(0);

		Imaginary	= 2.0 * Real * Imaginary + ImaginaryValue;
		Real		= RealTemp - ImaginaryTemp + RealValue;
	}
}

#endif

	/* Mandelbrot():
	 *
	 *	The recursive image generation routine.
	 */

VOID
Mandelbrot(double MinReal,double MinImaginary,double RealStep,double ImaginaryStep,LONG Left,LONG Top,LONG Width,LONG Height)
{
	if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
		HandleEvent();

	if(Running)
	{
		BYTE Colours[4],LastColour,Ident = TRUE;
		WORD Positions[4][2],i;

			/* Set up pixel positions. */

		Positions[0][0] = Left;
		Positions[0][1] = Top;

		Positions[1][0] = Left + Width - 1;
		Positions[1][1] = Top;

		Positions[2][0] = Left + Width - 1;
		Positions[2][1] = Top + Height - 1;

		Positions[3][0] = Left;
		Positions[3][1] = Top + Height - 1;

		for(i = 0 ; i < 4 ; i++)
		{
				/* Calculate colour values for each pixel. */

			Colours[i] = Iterate(MinReal + RealStep * (double)Positions[i][0],MinImaginary + ImaginaryStep * (double)Positions[i][1]);

				/* Are all colour identical? */

			if(Ident)
			{
				if(!i)
					LastColour = Colours[i];
				else
				{
					if(Colours[i] != LastColour)
						Ident = FALSE;
					else
						LastColour = Colours[i];
				}
			}
		}

			/* If all colour happen to be identical, fill the
			 * are in the given colour.
			 */

		if(Ident)
		{
				/* If extra precision is selected, also check
				 * if all the pixels on the margin of the
				 * current square are in the same colour.
				 */

			if(Precise)
			{
				WORD j,PositionBackup[4][2];

				CopyMem(Positions,PositionBackup,sizeof(PositionBackup));

				for(i = 0 ; i < Width - 1 ; i++)
				{
					PositionBackup[0][0]++;
					PositionBackup[1][1]++;
					PositionBackup[2][0]--;
					PositionBackup[3][1]--;

					for(j = 0 ; j < 4 ; j++)
					{
						if(Iterate(MinReal + RealStep * (double)PositionBackup[j][0],MinImaginary + ImaginaryStep * (double)PositionBackup[j][1]) != Colours[0])
							goto Full;
					}
				}
			}

				/* If necessary fill the square. */

			if(Colours[0])
			{
				if(RPort -> FgPen != Colours[0])
					SetAPen(RPort,Colours[0]);

				RectFill(RPort,Left,Top,Left + Width - 1,Top + Height - 1);
			}
		}
		else
		{
				/* If not already down at minimum level,
				 * split the square again.
				 */

Full:			if(Width > 2 && Height > 2)
			{
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left,Top,Width >> 1,Height >> 1);
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left + (Width >> 1),Top,Width >> 1,Height >> 1);

				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left,Top + (Height >> 1),Width >> 1,Height >> 1);
				Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Left + (Width >> 1),Top + (Height >> 1),Width >> 1,Height >> 1);
			}
			else
			{
				register struct BitMap *BitMap = RPort -> BitMap;

					/* Set the pixels. */

				for(i = 0 ; i < 4 ; i++)
				{
					if(Colours[i])
						Plot(BitMap,Positions[i][0],Positions[i][1],Colours[i]);
				}
			}
		}
	}
}

	/* RunMandelbrot():
	 *
	 *	This is the recursive image generation driver, it
	 *	remembers the time it took to generate the image and
	 *	calls the recursive image generation routine.
	 */

VOID
RunMandelbrot(double MinReal,double MaxReal,double MinImaginary,double MaxImaginary,LONG Width,LONG Height)
{
	double		RealStep,ImaginaryStep;
	UBYTE		TimeBuffer[40];
	struct timeval	TimeVal;

	GetTime();

	TimeVal = TimeRequest -> tr_time;

	RealStep	= (MaxReal - MinReal) / (double)Width;
	ImaginaryStep	= (MaxImaginary - MinImaginary) / (double)Height;

	FullPicture = TRUE;

	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,0,Window -> Width >> 1,Window -> Height >> 1);
	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,0,Window -> Width >> 1,Window -> Height >> 1);

	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,0,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);
	Mandelbrot(MinReal,MinImaginary,RealStep,ImaginaryStep,Window -> Width >> 1,Window -> Height >> 1,Window -> Width >> 1,Window -> Height >> 1);

	GetTime();

	SubTime(&TimeRequest -> tr_time,&TimeVal);

	BltBitMap(RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,BackupBitMap,0,0,8 * 25,8,0xC0,0xFF,NULL);

	GotClip = TRUE;

	SetAPen(RPort,1);
	SetDrMd(RPort,JAM1);

	TimeVal = TimeRequest -> tr_time;

	sprintf(TimeBuffer,"Duration %2d:%02d:%02d Minutes",(TimeVal . tv_secs % 86400) / 3600,(TimeVal . tv_secs % 3600) / 60,TimeVal . tv_secs % 60);

	Move(RPort,(Square - 25 * 8) >> 1,((Square - 8) >> 1) + 6);
	Text(RPort,TimeBuffer,25);

	Running = FALSE;
}

	/* RunAreaMandelbrot(double RealStep,double ImaginaryStep):
	 *
	 *	This is the iterative image generation routine,
	 *	unlike the routines above it generates the image
	 *	pixel by pixel and is not limited to a square
	 *	drawing area.
	 */

VOID
RunAreaMandelbrot(double RealStep,double ImaginaryStep)
{
	double		 RealValue,ImaginaryValue;
	WORD		 x,y;
	UBYTE		 TimeBuffer[40];
	struct BitMap	*BitMap = RPort -> BitMap;
	struct timeval	 TimeVal;
	BYTE		 Colour;

	GetTime();

	TimeVal = TimeRequest -> tr_time;

	ImaginaryValue = MinImaginary + ImaginaryStep * (double)AreaTop;

		/* `Faster' actually only means that before a pixel
		 * is drawn the approriate spot is checked to see
		 * if there is not already a pixel in place.
		 */

	if(Faster)
	{
		for(y = 0 ; (y < AreaHeight) && Running ; y++)
		{
			RealValue = MinReal + RealStep * (double)AreaLeft;

			for(x = 0 ; (x < AreaWidth) && Running ; x++)
			{
				if(!Test(BitMap,AreaLeft + x,AreaTop + y))
				{
					if((Colour = Iterate(RealValue,ImaginaryValue)))
						Plot(BitMap,AreaLeft + x,AreaTop + y,Colour);
				}

				RealValue += RealStep;

				if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
					HandleEvent();
			}

			ImaginaryValue += ImaginaryStep;
		}
	}
	else
	{
			/* These loops will calculate the image pixel
			 * by pixel without skipping already set areas.
			 * The first check insures that the drawing area
			 * is as wide as the screen which means that
			 * we are able to generate the pixels in fast
			 * ram and to copy them to chip ram when done.
			 */

		if(AreaWidth == Window -> Width && LineBitMap . Planes[0])
		{
			BitMap = &LineBitMap;

			for(y = 0 ; (y < AreaHeight) && Running ; y++)
			{
				RealValue = MinReal + RealStep * (double)AreaLeft;

				for(x = 0 ; (x < AreaWidth) && Running ; x++)
				{
					if((Colour = Iterate(RealValue,ImaginaryValue)))
						Plot(BitMap,AreaLeft + x,0,Colour);
					else
						Plot(BitMap,AreaLeft + x,0,0);

					RealValue += RealStep;

					if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
						HandleEvent();
				}

				if(Running)
					CopyLine(BitMap,AreaTop + y);

				ImaginaryValue += ImaginaryStep;
			}
		}
		else
		{
			for(y = 0 ; (y < AreaHeight) && Running ; y++)
			{
				RealValue = MinReal + RealStep * (double)AreaLeft;

				for(x = 0 ; (x < AreaWidth) && Running ; x++)
				{
					if((Colour = Iterate(RealValue,ImaginaryValue)))
						Plot(BitMap,AreaLeft + x,AreaTop + y,Colour);

					RealValue += RealStep;

					if(SetSignal(0,0) & (1 << Window -> UserPort -> mp_SigBit))
						HandleEvent();
				}

				ImaginaryValue += ImaginaryStep;
			}
		}
	}

	GetTime();

	SubTime(&TimeRequest -> tr_time,&TimeVal);

	BltBitMap(RPort -> BitMap,(Square - 25 * 8) >> 1,(Square - 8) >> 1,BackupBitMap,0,0,8 * 25,8,0xC0,0xFF,NULL);

	GotClip = TRUE;

	SetAPen(RPort,1);
	SetDrMd(RPort,JAM1);

	TimeVal = TimeRequest -> tr_time;

	sprintf(TimeBuffer,"Duration %2d:%02d:%02d Minutes",(TimeVal . tv_secs % 86400) / 3600,(TimeVal . tv_secs % 3600) / 60,TimeVal . tv_secs % 60);

	Move(RPort,(Square - 25 * 8) >> 1,((Square - 8) >> 1) + 6);
	Text(RPort,TimeBuffer,25);

	Running = FALSE;

	Faster = TRUE;
}

	/* main():
	 *
	 *	The notorious main routine.
	 */

VOID __stdargs
main()
{
	struct IFFHandle	*Handle;
	struct BitMap		*BitMap;
	BYTE			 GotImage = FALSE,IsAnim = FALSE;

		/* Open the required resources. */

	OpenAll();

	MinReal		= -RealWidth / 2;
	MinImaginary	= -ImaginaryHeight / 2;

	DummyBuffer[0] = 0;

		/* Pick up any selected images if run from Workbench. */

	if(!ThisProcess -> pr_CLI)
	{
		extern struct WBStartup *WBenchMsg;

		DefaultTool[0] = 0;

			/* Build new default tool name. */

		if(NameFromLock(WBenchMsg -> sm_ArgList[0] . wa_Lock,DefaultTool,256))
		{
			if(!AddPart(DefaultTool,WBenchMsg -> sm_ArgList[0] . wa_Name,256))
				DefaultTool[0] = 0;
		}

		if(!DefaultTool[0])
			strcpy(DefaultTool,"MandelSquare");

		PictureIcon . do_DefaultTool	= DefaultTool;
		AnimIcon . do_DefaultTool	= DefaultTool;

			/* Called with Workbench arguments? */

		if(WBenchMsg -> sm_NumArgs > 1)
		{
				/* Construct the full name. */

			if(NameFromLock(WBenchMsg -> sm_ArgList[1] . wa_Lock,DummyBuffer,256))
			{
				if(!AddPart(DummyBuffer,WBenchMsg -> sm_ArgList[1] . wa_Name,256))
					DummyBuffer[0] = 0;
				else
				{
					struct DiskObject *Icon;

						/* Read the icon and try to figure
						 * out whether the corresponding
						 * file is an animation or just
						 * a plain image.
						 */

					if(Icon = GetDiskObject(DummyBuffer))
					{
						UBYTE *Value;

						if(Value = FindToolType(Icon -> do_ToolTypes,"FILETYPE"))
						{
							if(!stricmp(Value,"ANIM"))
								IsAnim = TRUE;
						}

						FreeDiskObject(Icon);
					}
				}
			}
			else
				DummyBuffer[0] = 0;
		}
	}

		/* No chance, use the default image. */

	if(!DummyBuffer[0])
		strcpy(DummyBuffer,"MandelSquare.Title");

		/* Try to read the selected image. */

	if(Handle = OpenImageFile(DummyBuffer))
	{
		struct BitMapHeader	BitMapHeader;
		struct MandelInfo	TempMandelInfo;
		BYTE			Colours = 1 << Depth;
		ULONG			MyMode = Mode;

		if(ReadImageHeader(Handle,&MyMode,&Colours,ColourTable,&BitMapHeader,&TempMandelInfo))
		{
			LONG Error;

			if(TempMandelInfo . Iterations)
			{
				MinReal		= TempMandelInfo . MinReal;
				MinImaginary	= TempMandelInfo . MinImaginary;
				RealWidth	= TempMandelInfo . RealWidth;
				ImaginaryHeight	= TempMandelInfo . ImaginaryHeight;

				MaxIteration	= TempMandelInfo . Iterations;
			}

			if(BitMapHeader . w == BitMapHeader . h && !(BitMapHeader . h % 4))
			{
				Square	= BitMapHeader . w;
				Mode	= MyMode;
				Depth	= BitMapHeader . nPlanes;
			}

			if(BitMap = CreateBitMap(BitMapHeader . nPlanes,BitMapHeader . w,BitMapHeader . h))
			{
				if(ReadImageBody(Handle,BitMap,&BitMapHeader))
					GotImage = TRUE;
				else
					DeleteBitMap(BitMap);
			}

			if(Error = GfxSetup())
			{
				if(GotImage)
					DeleteBitMap(BitMap);

				CloseImageFile(Handle);

				CloseAll(Error);
			}
			else
			{
				if(GotImage)
				{
					BltBitMapRastPort(BitMap,0,0,RPort,0,0,Screen -> Width,Screen -> Height,0xC0);

					DeleteBitMap(BitMap);
				}
			}

			IterationSetup();
		}

		CloseImageFile(Handle);
	}
	else
	{
		struct Rectangle	DisplayClip;
		LONG			Error,Height;

			/* No title image, so let's produce one.
			 * At first determine the maximum screen
			 * size.
			 */

		if(QueryOverscan(Mode,&DisplayClip,OSCAN_TEXT))
		{
			WORD p = 0;

			Height	= (((DisplayClip . MaxY - DisplayClip . MinY + 1) + 3) >> 2) << 2;
			Square	= 0;

			while(Square < Height)
			{
				p++;

				Square += PLUS;
			}

			if(Square > Height)
			{
				p--;

				Square -= PLUS;
			}

			if(p & 1)
				Square -= PLUS;
		}
		else
		{
			if(Mode == LORES_KEY)
				Square = 256;
			else
				Square = 512;
		}

		if(Error = GfxSetup())
			CloseAll(Error);
	}

		/* Are we supposed to load and replay an animation file? */

	if(IsAnim)
	{
		BPTR File;

		SetWait(Window);

		Window -> Flags |= WFLG_RMBTRAP;

		if(File = Open(DummyBuffer,MODE_OLDFILE))
		{
			if(CreateBuffer())
			{
				if(OpenPreLoadIFF(File))
				{
					strcpy(AnimPlayName,DummyBuffer);

					SetClear(Window);

					SwapBits();

					ActivateWindow(Window);

					ScreenToFront(Screen);

					while(PlayAnim());

					ClosePreLoadIFF();
				}

				DeleteBuffer();

				LoadRGB4(&Screen -> ViewPort,ColourTable,1 << Depth);
			}

			Close(File);
		}

		Window -> Flags &= ~WFLG_RMBTRAP;

		ClearPointer(Window);
	}
	else
	{
		if(!GotImage)
		{
				/* Set up working data. */

			MainMinReal		= MinReal;
			MainMinImaginary	= MinImaginary;
			MainRealWidth		= RealWidth;
			MainImaginaryHeight	= ImaginaryHeight;

				/* Present the screen. */

			LoadRGB4(&Screen -> ViewPort,ColourTable,1 << Depth);

			ScreenToFront(Screen);

			ActivateWindow(Window);

			Running		= TRUE;
			NewCoords	= FALSE;

				/* Generate the image. */

			RunMandelbrot(MinReal,MinReal + RealWidth,MinImaginary,MinImaginary + ImaginaryHeight,Window -> Width,Window -> Height);
		}
		else
		{
			strcpy(LastName,DummyBuffer);

				/* Present the screen. */

			ScreenToFront(Screen);

			BlockWindow();

			ActivateWindow(Window);

				/* Fade in the image. */

			FadeTo(&Screen -> ViewPort,NULL,ColourTable,1 << Depth,0,0);

			ReleaseWindow();
		}

		RealStep	= (RealWidth) / (double)Window -> Width;
		ImaginaryStep	= (ImaginaryHeight) / (double)Window -> Height;
	}

		/* The main input loop. */

	FOREVER
	{
		WaitPort(Window -> UserPort);

		HandleEvent();

			/* New display mode? */

		if(NewMode)
		{
			struct Rectangle	DisplayClip;
			LONG			Error,Height;

				/* Remember colour table. */

			CopyMem(ColourTable,SinPalette,sizeof(UWORD) * 32);

				/* We have a bitmap to copy to the
				 * screen.
				 */

			if(GlobalBitMap)
			{
				GfxCleanup();

				Mode	= GlobalMode;
				Depth	= GlobalHeader . nPlanes;
				Square	= GlobalHeader . w;

				NewMode	= FALSE;

				if(Error = GfxSetup())
					CloseAll(Error);
				else
				{
					BltBitMapRastPort(GlobalBitMap,0,0,RPort,0,0,Screen -> Width,Screen -> Height,0xC0);

					DeleteBitMap(GlobalBitMap);

					GlobalBitMap = NULL;

					ScreenToFront(Screen);

					BlockWindow();

					ActivateWindow(Window);

					if(UseWheel)
						FadeTo(&Screen -> ViewPort,NULL,WheelPalette,1 << Depth,0,0);
					else
						FadeTo(&Screen -> ViewPort,NULL,SinPalette,1 << Depth,0,0);

					NewCoords	= FALSE;
					Running		= FALSE;
					AreaActive	= FALSE;

					IterationSetup();

					ReleaseWindow();
				}
			}
			else
			{
					/* Open new screen with maximum
					 * available text overscan dimensions.
					 */

				if(Mode == LORES_KEY)
				{
					Mode	= HIRESLACE_KEY;
					Depth	= 4;
				}
				else
				{
					Mode	= LORES_KEY;
					Depth	= 5;
				}

				if(QueryOverscan(Mode,&DisplayClip,OSCAN_TEXT))
				{
					WORD p = 0;

					Height	= (((DisplayClip . MaxY - DisplayClip . MinY + 1) + 3) >> 2) << 2;
					Square	= 0;

					while(Square < Height)
					{
						p++;

						Square += PLUS;
					}

					if(Square > Height)
					{
						p--;

						Square -= PLUS;
					}

					if(p & 1)
						Square -= PLUS;
				}
				else
				{
					if(Mode == LORES_KEY)
						Square = 256;
					else
						Square = 512;
				}

				NewMode	= FALSE;

				GfxCleanup();

				if(Error = GfxSetup())
					CloseAll(Error);
				else
				{
					if(UseWheel)
						LoadRGB4(&Screen -> ViewPort,WheelPalette,1 << Depth);
					else
						LoadRGB4(&Screen -> ViewPort,SinPalette,1 << Depth);

					ScreenToFront(Screen);

					ActivateWindow(Window);

					MinReal		= MainMinReal;
					MinImaginary	= MainMinImaginary;
					RealWidth	= MainRealWidth;
					ImaginaryHeight	= MainImaginaryHeight;

					RealStep	= (RealWidth) / (double)Window -> Width;
					ImaginaryStep	= (ImaginaryHeight) / (double)Window -> Height;

					NewCoords	= TRUE;
					AreaActive	= FALSE;

					IterationSetup();
				}
			}
		}

			/* Run the animation routine? */

		while(AnimRunning)
		{
			RunAnim();

			if(!AnimRunning)
			{
				Running		= FALSE;
				NewCoords	= FALSE;
			}
		}

			/* Restart with new coordinates. */

		if(NewCoords)
		{
			Running		= TRUE;
			NewCoords	= FALSE;
		}

			/* Run the image generation routines. */

		if(Running)
		{
			if(AreaActive)
				RunAreaMandelbrot(RealStep,ImaginaryStep);
			else
			{
				SetRast(RPort,0);

				RunMandelbrot(MinReal,MinReal + RealWidth,MinImaginary,MinImaginary + ImaginaryHeight,Window -> Width,Window -> Height);
			}
		}
	}
}
