/****************************************************************
 * Fractal.c © John M. Olsen.   9-1-86
 * This is a fractal program which produces random square fractal
 * terrain, with resolution where the size of the sides is
 * 2^n + 1 where n is between 0 and 6.
 * The 3D view uses a light source off to the upper left.
 *
 * This program requires both c.lib and m.lib when linked.  It was written for
 * Aztec and I have no idea what it will do when compiled with Lettuce C.
 * 
 * This program is freely distributable as long as this notice remains
 * with it.  Do not use any part of this program in a commercial application
 * without written consent from the author:
 *
 * John M. Olsen
 * 1547 Jamestown Drive
 * Salt Lake City, UT  84121
 ****************************************************************/

#define MAXFRACT 65	/* This must be 2^something + 1 */
#define EVEN(a) (!(a & 1))
#define ODD(a)  (a & 1)

#include <exec/types.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/rastport.h>
#include <intuition/intuition.h>
#include <stdio.h>
#include <math.h>

void *OpenLibrary(), *ScreenToBack(), *ScreenToFront();
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Screen *OpenScreen(), *s;
struct Window *OpenWindow(), *w;
struct IntuiMessage *GetMsg();
float myrand();

BYTE *AllocRaster();
WORD areabuffer[250];
struct TmpRas tmpras;
struct AreaInfo myAreaInfo;

struct NewScreen ss =
{	0, 0, 320, 200,		/* Edges & size		*/
	5, 0, 1,		/* Depth, pens		*/
	NULL, CUSTOMSCREEN,	/* Viewmodes, type	*/
	NULL,			/* *font		*/
	(UBYTE *) "3D Random Fractal Terrain",
	NULL, NULL		/* Gadgets, bit map	*/
};

float p[MAXFRACT][MAXFRACT], divisor, scale;
int shadows, degree, size;

main()
{
	long sc, mc;

	printf("Fractal generator by John M. Olsen ©1986\n\n");
	printf("This program is freely distributable so long as the above\n");
	printf("notice remains in it.\n");
	printf("After a fractal has been computed and drawn, you need to\n");
	printf("push the picture screen back, bringing this screen foreward\n");
	printf("before trying to enter more data.\n");
	if(!(GfxBase = OpenLibrary("graphics.library",0l)))
		die(1);
	if(!(IntuitionBase = OpenLibrary("intuition.library")))
		die(2);
	if(!(s = OpenScreen(&ss)))
		die(3);
	InitArea(&myAreaInfo, areabuffer, 100l);
	s->RastPort.AreaInfo = &myAreaInfo;
	tmpras.RasPtr = AllocRaster(320l, 200l);
	tmpras.Size = (long) RASSIZE(320l, 200l);
	s->RastPort.TmpRas = &tmpras;
	ScreenToBack(s);
	docolors(&(s->RastPort));
	while(1)
	{
		CurrentTime(&sc,&mc);	/* Seed the random number by time */
		myrand(-(int)sc);
		getsize();	/* Exits from this routine. */
		compute();
		ScreenToFront(s);
		draw(&(s->RastPort));
	}
}

/* Compute the array. */
compute()
{
	float tempscale, rnum, t1, t2, t3, t4;
	int x, y, n;

	/* Zero out the corner of the array. */
	for(x = 0;x < 2; x++)
		for(y = 0; y < 2; y++)
			p[x][y] = 0;
	tempscale = 64.0;
	size = 1;
	for(n = 1; n <= degree; n++)
	{
		size *= 2;
		for(x = size; x >= 0; x--)
		{
			for(y = size; y >= 0; y--)
			{
				t1 = p[x / 2][y / 2];
				t2 = p[(x+1) / 2][(y+1)  / 2];
				t3 = p[x / 2][(y + 1) / 2];
				t4 = p[(x + 1) / 2][y / 2];
				rnum = myrand(0) - .5;
				rnum *= tempscale;
				if(ODD(x) && EVEN(y))
				{
					p[x][y] = (t1 + t4) / 2.0 + rnum;
				}
				else if(EVEN(x) && ODD(y))
				{
					p[x][y] = (t1 + t3) / 2.0 + rnum;
				}
				else if(ODD(x) && ODD(y))
				{
					p[x][y] = (t1+t2+t3+t4) / 4.0 + rnum;
				}
				else if(EVEN(x) && EVEN(y))
				{
					p[x][y] = t1;
				}
				else
					printf("Toasted.\n");
			}
		}
		tempscale /= divisor;
	}
	/* now run it through the scaler. */
	for(x = 0; x <= size; x++)
		for(y = 0; y <= size; y++)
			p[x][y] *= scale;
}

die(kind)
int kind;
{
	static char *errortext[] = {	"No error.\n",
					"Unable to open graphics.\n",
					"Unable to open Intuition.\n",
					"Unable to open screen.\n",
					"4\n",
					"5\n",
					"6\n"
				};

	if(kind)
		puts(errortext[kind]);
	if(tmpras.RasPtr)	FreeRaster(tmpras.RasPtr,320l,200l);
	if(s)			CloseScreen(s);
	if(GfxBase)		CloseLibrary(GfxBase);
	if(IntuitionBase)	CloseLibrary(IntuitionBase);
	exit(kind);
}

float myrand(seed)
int seed;
{
	static unsigned int val;

	if(seed < 0)
		val = - seed;
	val *= 25173;
	val += 13849;
	val %= 32768;
	return((float) val / 32768.0);
}

draw(r)
struct RastPort *r;
{
	int h, x, y, cl, sz;
	float slopex, slopey;

	SetAPen(r, 0l);
	RectFill(r, 0l,10l,320l,200l);
/*	SetRast(r,0l);  this line took out the title bar.*/
	h = 150;	/* use this many of the 200 lines. */
	for(y = 1; y <= size; y++)
	{
		for(x = 1; x <= size; x++)
		{
			if(shadows)
			{
				slopex = p[x][y-1];
				slopex += p[x][y];
				slopex -= p[x-1][y-1];
				slopex -= p[x-1][y];

				slopey = p[x][y];
				slopey -= p[x][y-1];
				slopey -= p[x-1][y-1];
				slopey += p[x-1][y];
				cl = (int) (slopex + slopey) + 15;
			}
			else
			{
				cl = (int) p[x][y];
				cl += (int) p[x-1][y];
				cl += (int) p[x][y-1];
				cl += (int) p[x-1][y-1];
				cl /= 8;
				cl += 16;
			}
			sz = h / (size + 1);
			if(cl > 31)
				cl = 31;
			if(cl < 1)
				cl = 1;
			SetAPen(r, (long)cl);
			AreaMove(r, (long)(50+((x-1)*sz+(size-y+1)*60/size)+1),
				(long)(((y-1)*sz-(int)p[x-1][y-1])+30));
/*+1extra*/		AreaDraw(r, (long)(50+(x*sz+(size-y+1)*60/size)+1),
				(long)(((y-1)*sz-(int)p[x][y-1])+30));
/*+1 extra*/		AreaDraw(r, (long)(50+(x*sz+(size-y)*60/size)+1),
/*+1 extra*/			(long)((y*sz-(int)p[x][y])+30));
			AreaDraw(r, (long)(50+((x-1)*sz+(size-y)*60/size)+1),
				(long)((y*sz-(int)p[x-1][y])+30));
			AreaEnd(r);
		}
	}
}

/*********************
Set up the colors to 32 gray shades.
*********************/
docolors(r)
struct RastPort *r;
{
	long loop;

	r = &(s->RastPort);
	for(loop = 1l; loop < 32l; loop++)
		SetRGB4(&s->ViewPort, loop, loop/2l, (loop - 1)/2l, loop/2l);
}

getsize()
{
	printf("Enter the degree(1-6 or invalid to quit): ");
	scanf(" %d", &degree);
	if(degree < 1 || degree > 6)
		die(0);
	printf("Enter the scale(0.0 to 3.0, 1.0 is standard): ");
	scanf(" %f", &scale);
	if(scale > 3.0 || scale < 0.0)
		die(0);
	printf("Enter the divisor(1.0 to 4.0, 2.0 is standard): ");
	scanf(" %f", &divisor);
	if(divisor > 4.0 || divisor < 1.0)
		die(0);
	printf("Altitudes (0) or shadows(non-zero)? ");
	scanf(" %d", &shadows);
}
