/************************************

 fractal.c - Symmetric Fractal Generator.

 Algorithm from "Symmetry in Chaos". Outputs in tga format.

 Code (c) 2002 Tim Hutt

 Usage:
 ./fractal <a11> <a12> <a21> <a22> <b1> <b2> <symmetry> <xres> <yres> <iterations> <filename.tga>
 Example:
 ./fractal -0.1 0.35 0.2 0.5 0.5 0.4 3 1024 768 10000000 bee.tga

************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

/**********************************************************/

#pragma pack(1)
typedef struct
{
	unsigned char ImageIDLength;
	unsigned char ColourMapType;
	unsigned char ImageTypeCode;
	unsigned char ColourMapOrigin; // Hmm conflicting header definitions. This works.
	unsigned short ColourMapLength;
	unsigned short ColourMapEntrySize;
	unsigned short ImageXOrigin;
	unsigned short ImageYOrigin;
	unsigned short ImageWidth;
	unsigned short ImageHeight;
	unsigned char ImageBitCount;
	unsigned char ImageDescriptor;
} TARGAHEADER;
#pragma pack()

/**********************************************************/

#define PI 3.1415927
#define ITER_IGNORE 10000
#define ITER_SIZE 10000

/**********************************************************/

float a11, a12, a21, a22, b1, b2;
float x, y;
float xmin, xmax, ymin, ymax;

float xnew, ynew, xtmp, ytmp;
int r;

float Scale;
float XOff, YOff;

unsigned int hitmax;
unsigned char Pel;
int i, j, k, l, m;

unsigned int n;
unsigned int XRes, YRes;
unsigned int nIterations;
char* szFilename;
unsigned int* nPixels;
FILE* pFile;
TARGAHEADER Header;

/**********************************************************/

void Iterate(float* x, float* y);

/**********************************************************/

int main(int argc, char* argv[])
{

/**************************/

	if (argc != 12)
	{
		printf("Usage: %s <a11> <a12> <a21> <a22> <b1> <b2> <symmetry> <xres> <yres> <iterations> <filename>\n", argv[0]);
		return 1;
	}
	a11 = (float)strtod(argv[1], NULL);
	a12 = (float)strtod(argv[2], NULL);
	a21 = (float)strtod(argv[3], NULL);
	a22 = (float)strtod(argv[4], NULL);
	b1 = (float)strtod(argv[5], NULL);
	b2 = (float)strtod(argv[6], NULL);
	n = atoi(argv[7]);
	XRes = atoi(argv[8]);
	YRes = atoi(argv[9]);
	nIterations = atoi(argv[10]);
	szFilename = argv[11];
	if (XRes <= 0)
		XRes = 1;
	if (YRes <= 0)
		YRes = 1;
	if (nIterations <= 0)
		nIterations = 1;
	if (n <= 0)
		n = 1;

/**************************/

	x = 0.0f;
	y = 0.0f;

	for (i = 0; i < ITER_IGNORE; i++)
		Iterate(&x, &y);

/**************************/

	xmin = x;
	xmax = x;
	ymin = y;
	ymax = y;

	for (j = 0; j < ITER_SIZE; j++)
	{
		Iterate(&x, &y);
		if (x < xmin)
			xmin = x;
		if (x > xmax)
			xmax = x;
		if (y < ymin)
			ymin = y;
		if (y > ymax)
			ymax = y;
	}

	if (xmax - xmin == 0.0f)
		xmax++;
	if (ymax - ymin == 0.0f)
		ymax++;

	if ( (ymax - ymin) * (float)XRes / (xmax - xmin) > (float)YRes)
	{
		Scale = (float)YRes / (ymax - ymin);
	}
	else
	{
		Scale = (float)XRes / (xmax - xmin);
	}
	XOff = ( (float)XRes - (xmax - xmin) ) / 2.0f;
	YOff = ( (float)YRes - (ymax - ymin) ) / 2.0f;

/**************************/

	nPixels = malloc(sizeof(unsigned int) * XRes * YRes);
	if (!nPixels)
	{
		printf("Error allocating memory for output image\n");
		return 1;
	}

	memset(nPixels, 0, sizeof(unsigned int) * XRes * YRes);
	
	for (k = 0; k < nIterations; k++)
	{
		if (!(k % (nIterations/100)))
			printf("%d%%\n", (100*k/nIterations));

		Iterate(&x, &y);
		if ((x * Scale + XOff) >= 0.0f && (x * Scale + XOff) < XRes && (y * Scale + YOff) >= 0.0f && (y * Scale + YOff) < YRes)
		{
			nPixels[(unsigned int)(y * Scale + YOff) * XRes  + (unsigned int)(x * Scale + XOff)]++;
		}
	}
	printf("\n");

/**************************/

	hitmax = 0;
	for (l = 0; l < XRes * YRes; l++)
		if (nPixels[l] > hitmax)
			hitmax = nPixels[l];

/**************************/

	pFile = fopen(szFilename, "wb");
	if (!pFile)
	{
		printf("Error opening output image file.\n");
		free(nPixels);
		return 1;
	}

	Header.ImageIDLength = 0;
	Header.ColourMapType = 0;
	Header.ImageTypeCode = 3;
	Header.ColourMapOrigin = 0;
	Header.ColourMapLength = 0;
	Header.ColourMapEntrySize = 0;
	Header.ImageXOrigin = 0;
	Header.ImageYOrigin = 0;
	Header.ImageWidth = XRes;
	Header.ImageHeight = YRes;
	Header.ImageBitCount = 8;
	Header.ImageDescriptor = 32;

	fwrite(&Header, sizeof(TARGAHEADER), 1, pFile);

	Pel = 0;

	if (hitmax == 0)
		hitmax++;	

	for (m = 0; m < XRes * YRes; m++)
	{
		Pel = (255 * nPixels[m]) / hitmax;
		fwrite(&Pel, sizeof(unsigned char), 1, pFile);
	}

	fclose(pFile);
	free(nPixels);

/**************************/

	return 0;
}

void Iterate(float* x, float* y)
{
	xnew = a11 * *x + a12 * *y + b1;
	ynew = a21 * *x + a22 * *y + b2;
	r = rand() % n;
	xtmp = xnew;
	ytmp = ynew;
	xnew = (float)cos(2.0 * PI * (double)r / (double)n) * xtmp - (float)sin(2 * PI * (double)r / (double)n) * ytmp;
	ynew = (float)sin(2.0 * PI * (double)r / (double)n) * xtmp + (float)cos(2 * PI * (double)r / (double)n) * ytmp;

	if (rand() % 2)
		ynew = -ynew;

	*x = xnew;
	*y = ynew;
}

