/* 
 *
 *    This application has was created by Timothy A. Graupmann. For
 *    any questions or comments send email to tgraupmann@yahoo.com.
 *
 *    This application displays the Julia 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 <math.h>
#include <GL/glut.h>

typedef struct { float x, y; } Complex;
static int max_level=10;
static double hscal=0.0045;
static double wscal=0.0045;
static double htran=0.0;
static double wtran=0.0;

void gfxInit(void);
void reshape(int w, int h);
void keyboard(unsigned char key,int x, int y);
double magnitude(Complex c);
void julia (int w, int h, float max_level);
void display(void);
Complex complexSquare (Complex c);


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 '*':
			max_level++;
			display();
			break;
		case '/':
			max_level--;
			if(max_level<=2)
				max_level=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':
			max_level=20;
			hscal=0.0016;
			wscal=0.0016;
			htran=0.0;
			wtran=0.0;
			display();
	}	
}
double magnitude(Complex c){
	return sqrt(c.x*c.x+c.y*c.y);
}
void julia (int w, int h) {
	Complex c,z;
	double color;
	int level=0;	
	c.x=.5;
	c.y=.5;
	int x,y=0;
	glBegin(GL_POINTS);
	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;	
	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,level=0) {
			while ((z.x * z.x + z.y * z.y <= 4.0) && (level< max_level)) {
				z = complexSquare (z);
				z.x += c.x;
				z.y += c.y;
				level++;
			}
			while((magnitude(z)<magnitude(c))&&(level<max_level-1)) {
				z.x+=magnitude(z)-magnitude(c);
				z.y+=magnitude(z)-magnitude(c);
				level++;
			}			
			color=double(level)/double(max_level);			
			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);
	julia(w,h);
	glFlush();
	glutSwapBuffers();
}
int main(int argc, char** argv){
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
	glutInitWindowSize(640,480);
	glutInitWindowPosition(1, 1);
	glutCreateWindow("Julia Fractal");
	gfxInit();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);	
  	glutMainLoop();
	return 0;
}

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);
}