/* 
 *
 *    This application has was created by Timothy A. Graupmann. For
 *    any questions or comments send email to tgraupmann@yahoo.com.
 *
 *    This application displays the Mandelbrot Fractal while allowing
 *    the user to navigate and manipulate the fractal. The arrow keys
 *    scroll accordingly while "*" and "/" increase and decrease
 *    fractal iterations. "+" and "-" zoom in and out.
 *
 */


#include <iostream.h>
#include <stdlib.h>
#include <GL/glut.h>

typedef struct { float x, y; } Complex;
static int nIter = 20;
static double hscal=0.0045;
static double wscal=0.0045;
static double htran=0.0;
static double wtran=0.0;

void display(void);

void gfxInit(void){
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glShadeModel(GL_FLAT);
	glClear(GL_COLOR_BUFFER_BIT);
}
void reshape(int w, int h){
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
}
void keyboard(unsigned char key,int x, int y) {
	switch(key) {
		case 'q':case 'Q':
			exit(0);
			break;
				case '*':
			nIter++;
			display();
			break;
		case '/':
			nIter--;
			if(nIter<=2)
				nIter=3;
			display();
			break;
		case '8':
			htran+=50.0*hscal;
			display();
			break;
		case '6':
			wtran+=50.0*wscal;
			display();
			break;
		case '4':
			wtran-=50.0*wscal;
			display();
			break;
		case '2':
			htran-=50.0*hscal;
			display();
			break;
		case '+':
			hscal*=0.50;
			wscal*=0.50;
			display();
			break;
		case '-':
			hscal/=0.50;
			wscal/=0.50;
			display();
			break;
		case '5':
			hscal=0.0045;
			wscal=0.0045;
			htran=0.0;
			wtran=0.0;
			display();
			break;
	}
}
Complex complexSquare (Complex c){
	Complex cSq;
	cSq.x = c.x * c.x - c.y * c.y;
	cSq.y = 2 * c.x * c.y;
	return (cSq);
}

int iterate (Complex zInit, int maxIter){
	Complex z = zInit;
	int cnt = 0;	
	while ((z.x * z.x + z.y * z.y <= 4.0) && (cnt < maxIter)) {
		z = complexSquare (z);
		z.x += zInit.x;
		z.y += zInit.y;
		cnt++;
	}
	return (cnt);
}
void mandelbrot (int w, int h, int maxIter) {
	Complex z;
	int x;
	int y=0;
	int cnt=0;
	double color=0.0;
	double aa=-hscal*h+htran;
	double ab=hscal*h+htran;
	double ac=hscal*2.0;
	double ba=-wscal*w+wtran;
	double bb=wscal*w+wtran;
	double bc=wscal*2.0;
	glBegin (GL_POINTS);
	for(double j=aa;j<ab;j+=ac,x=0,y++) {		
		for(double i=ba;i<=bb;i+=bc,x++,z.x=i,z.y=j,cnt=0) {
			color=double(iterate (z, maxIter))/double(maxIter);
			if(color<0.3)
				glColor3d(color/0.3,0,0);
			else
				if(color<0.6)
					glColor3d(0,(color-0.3)/0.3,0);
				else
					if(color<0.9)
						glColor3d(0,0,(color-0.6)/0.3);
					else
						glColor3d(0,0,0);			
			glVertex2i(x,y);
		}
	}
	glEnd ();
}
void display(void){	
	int w = glutGet(GLUT_WINDOW_WIDTH);
	int h = glutGet(GLUT_WINDOW_HEIGHT);	
	mandelbrot(w,h,nIter);
	glFlush();
}
int main(int argc, char** argv){
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(640, 480);
	glutInitWindowPosition(1, 1);
	glutCreateWindow("Mandelbrot Fractal");
	gfxInit();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);	
	glutKeyboardFunc(keyboard);
  	glutMainLoop();
	return 0;
}