//mend&jul&targa  program    proj4, ver-c.
//
//by Adrian Porter
//Super Computer Applications



//  -----  header files here:


#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "glaux.h"
#include <gltk.h>


#define pi 3.141592

//  ----- global stuff here:


double ax=-2, ay=2, bx=2, by=-2;
double newax, neway;
//const int w = 960, h = 768;
const int w = 300, h = 300;
int screenw = 300, screenh = 300;

int doredraw=1;
int option=0;
double power=3;
double frame, frames, initpower, finalpower;
int initframe, finalframe;


int maxiterations=40;

double rpal[70],gpal[70],bpal[70];

//global targa struct
typedef struct {
	char id_len;
	char map_type;
	char img_type;
	int map_first;
	int map_len;
	char map_entry_size;
	int x, y, width, height;
	char bpp, misc;
} targa_header;
targa_header header;


//prototypes for targa file
void writeheader(FILE *);
void setupheader();
void putpixel(double, double, double, FILE *);







//prototype
static void display( );




double atanb(double y, double x);
double distance(double x1, double y1, double x2, double y2);
void godraw(void);







//  ------  initializing funcs here:


void coolpal (void) {

	//this initializes the pallet to a rainbow ROYGBV (no I)	


	int i=0;
	double r2=1,g2=0,b2=0;
	//red
	for(;i<10;i++) {
		g2+=1.0/10.0;
		rpal[i]=r2; gpal[i]=g2; bpal[i]=b2;
	}
	//yellow
	for(;i<20;i++) {
		r2-=1.0/10.0;
		rpal[i]=r2; gpal[i]=g2; bpal[i]=b2;
	}
	//green
	for(;i<30;i++) {
		g2-=1.0/10.0;
		b2+=1.0/10.0;
		rpal[i]=r2; gpal[i]=g2; bpal[i]=b2;
	}
	//blue
	for(;i<40;i++) {
		r2+=0.8/10.0;
		b2-=0.2/10.0;
		rpal[i]=r2; gpal[i]=g2; bpal[i]=b2;
	}
	//purple
	for(;i<50;i++) {
		r2+=0.2/10.0;
		b2-=0.8/10.0;
		rpal[i]=r2; gpal[i]=g2; bpal[i]=b2;
	}
	//red
	/////////////////////////////////
}	
	



void goinit(void) {
        
//outputs the instructions to screen, cool!

	int input;

        cout << "\n\n\n";
        cout << "q-zoom in"; 
        cout << "\na-zoom out"; 
        cout << "\nw-display coordinate values";
        cout << "\nuse the dirrectional keys to shift graph";
	cout << "\ne-restart coordinate values <set power to 3>";
	cout << "\nf-write picture to file (pic.tga)";
	cout << "\nr-manually run redraw func";
	cout << "\nt-input power";
	
	cout << "\n\n\nuse \"o\" and \"p\" to decrease and increase the
iterations by twenty\nuse \"k\" and \"l\" to decrease and increase the
iterations by five";

	cout << "\n\n\n\npress z to zoom with mouse"
	<< "\nuse left button to choose the top left coordinate"
	<< "\nuse right button to choose the right coodinate (bottom is ignored)"
	<< "\n\n\npress j and choose a point for julius set"
        << "\n\n\n"
	<< "\n(1)start with normal initial coordinate value"
	<< "\n(2)input inital coordinate and input power"
	<< "\n(3)do an animation";
	cout << "\n:";
        cin >> input;
	
	if(input==2 || input==3) {
		cout << "\n\n\n\n";
		cout << "\nmax iterations:" << maxiterations;
		cout << "\nax:" << ax;
		cout << "\nay:" << ay;
		cout << "\nbx:" << bx;
		cout << "\nby:" << by;
		cout << "\npower:" << power;
		cout << "\n\n\n\n";


		//get values
		cout << "max iterations:"; cin >> maxiterations;
        	cout << "ax:"; cin >> ax;
        	cout << "ay:"; cin >> ay;
        	cout << "bx:"; cin >> bx;
        	cout << "by:"; cin >> by;
		if(input==2)
			{cout << "power:"; cin >> power;}
	}
	if(input==3) {
		cout << "init power:"; cin >> initpower;
		cout << "final power:"; cin >> finalpower;
		cout << "frames:"; cin >> frames;
		cout << "initframe:"; cin >> initframe;
		cout << "finalframe:"; cin >> finalframe;
		option=5;
		frame=initframe;
		
	}
	cout << "\n";
}







//  ------  redraw funcs here:



void godraw(void)  // The Drawing Routine
{
	//this function draws the mendelbrot with the current values
	//of ax, bx, ay, by

	//double x, y, rad, x1, y1;
	double Fx1, Fy1, Fx2, Fy2, Fx3, Fy3, Fx4, Fy4;
	double Zx, Zy, rad, ZN, theta;
	double curw=bx-ax, curh=by-ay;
	int n;
	int i, j;
	FILE *tga;
	char filename[20], num[8];


	if(option==4) {
		setupheader();
		tga=fopen("pic.tga", "wb");
		writeheader(tga);
		cout << "targa file . . . please wait";
		cout << "\n";
	}
	
	if(option==5) {
		
		setupheader();
		power = (frame - 1) / (frames - 1) * (finalpower - initpower) + initpower;
		
		strcpy(filename, "pic");
		if(frame<=9) strcat(filename, "0");
		sprintf(num, "%d", int(frame));
		strcat(filename, num);
		strcat(filename, ".tga");
		


		tga=fopen(filename, "wb");
		writeheader(tga);
		cout << "targa file [frame : " << frame << "] . . . please wait";
		cout << "\n";
	}
	//glColor3f(0.0, 0.0, 0.0);
	glBegin(GL_POINTS);

	for(j = 0; j<=screenh-1; j++) {
		for(i = 0;i<= screenw-1;i++) {
			Zx = double(i)/(screenw-1)*curw+ax;
			Zy = double(j)/(screenh-1)*curh+ay;

			for(n = 1;n<= maxiterations; n++) {
				
				ZN=pow(distance(0,0,Zx,Zy), power); 
				
				
				theta=atanb(Zy, Zx);
				Fx1 = ZN * cos(power * theta) - 1;
				Fy1 = ZN * sin(power * theta);

				
				ZN=pow(distance(0,0,Zx,Zy), power - 1);
				Fx2 = power * ZN * cos((power - 1) * theta);
				Fy2 = power * ZN * sin((power - 1) * theta);

				
				ZN=pow(distance(0,0,Fx2,Fy2), -1);
				theta=atanb(Fy2, Fx2);
				Fx3 = ZN * cos(-1 * theta);
				Fy3 = ZN * sin(-1 * theta);


				Fx4 = Fx1 * Fx3 - Fy1 * Fy3;
				Fy4 = Fy1 * Fx3 + Fx1 * Fy3;

				rad=distance(Zx, Zy, Zx - Fx4, Zy - Fy4);

				if(rad <= .01) break;
				Zx -= Fx4; Zy -= Fy4;
			}

			if(option==4 || option==5) {
				if(rad<=.01) putpixel(rpal[(n)%50], gpal[(n)%50], bpal[(n)%50], tga);
				else putpixel(0.0,0.0,0.0, tga);
			}
			
			if(rad<=.01) glColor3f(rpal[(n)%50], gpal[(n)%50], bpal[(n)%50]);
			else glColor3f(0.0, 0.0, 0.0);

			glVertex3f(double(i)/(screenw-1), 1.0-double(j)/(screenh-1), 0.0);
		}
	}
	
	glEnd();
	
	if(option==4) {
		fclose(tga);
		option=0;
		cout << "targa file done";
		cout << "\n";
	}

	if(option==5) {
		fclose(tga);
		
		
		if(frame==finalframe) option=0;

		cout << "frame " << frame << " done";
		cout << "\n";
		frame++;
	}
}









//  ------  targa file funcs here:



void writeheader(FILE *tga) {  //writes the header to file tga
	fputc(header.id_len, tga);
	fputc(header.map_type, tga);
	fputc(header.img_type, tga);
	fputc(header.map_first % 256, tga);
	fputc(header.map_first / 256, tga);
	fputc(header.map_len % 256, tga);
	fputc(header.map_len / 256, tga);
	fputc(header.map_entry_size, tga);
	fputc(header.x % 256, tga);
	fputc(header.x / 256, tga);
	fputc(header.y % 256, tga);
	fputc(header.y / 256, tga);
	fputc(header.width % 256, tga);
	fputc(header.width / 256, tga);
	fputc(header.height % 256, tga);
	fputc(header.height / 256, tga);
	fputc(header.bpp, tga);
	fputc(header.misc, tga);
}

void setupheader(void) {   //sets up the header
	header.id_len=0;
	header.map_type=0;
	header.img_type=2;
	header.map_first=0;
	header.map_len=0;
	header.map_entry_size=0;
	header.x=0;
	header.y=0;
	header.width=screenw;
	header.height=screenh;
	header.bpp=24;
	header.misc=0x20;
}

void putpixel(double red3, double green3, double blue3, FILE *tga) {  //puts a pixel to file
	fputc(char(int(blue3*255)), tga);
	fputc(char(int(green3*255)), tga);
	fputc(char(int(red3*255)), tga);
}













//random prototype
void key_w(void);







//  ------  mouse funcs here:



static GLenum gomouse(int mouseX, int mouseY, GLenum button)
{
	double curw = bx - ax, curh = by - ay;	
	if(button!=TK_LEFTBUTTON) return GL_FALSE;
	if (option==1) {  //first button
	    	newax=double(mouseX)/screenw*curw+ax;
	    	neway=double(mouseY)/screenh*curh+ay;
		option=2;
	}
	else if (option==2) { //second button
	    bx=double(mouseX)*curw/screenw+ax; by=double(mouseY)*curh/screenh+ay;
	    ax=newax;ay=neway;
	    doredraw=1;
	    option=0;
	}
	return GL_TRUE;
}











//  ------  window funcs here:



// This Section creates a Window, but uses only GL commands rather than X.

//3.
static void Init( void )
{
   /* one-time init (clearColor, set palette, etc) */
   glClearColor(0.5, 0.5, 0.5, 1.0 );
   glShadeModel( GL_FLAT );
}



static void Reshape( int width, int height )  // Essential Magic again!
{

   glViewport(0, 0, (GLint)width, (GLint)height);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0 );
   glMatrixMode(GL_MODELVIEW);
   screenw=width+8;screenh=height+2;
   // doredraw=1;  do not redraw everytime the user switches screens!!!!
                    //the user may press r to redraw screen
}














//  -----  key funcs here:



//when a button is pressed, the values of
//ax, bx, ay, & by are changed and the screen
//is updated when the display() is run.



void key_a(void) {
	double curw = bx - ax, curh = by - ay;
	double centerx=ax+curw / 2.0;
	double centery=ay+curh / 2.0;
	
	ax = (ax - centerx) * 2.0 + centerx;
	bx = (bx - centerx) * 2.0 + centerx;
 	ay = (ay - centery) * 2.0 + centery;
	by = (by - centery) * 2.0 + centery;
	//Reshape(screenw-8, screenh-2);
	doredraw=1;
}

void key_q(void) {
	double curw = bx - ax, curh = by - ay;
	double centerx=ax+curw / 2.0;
	double centery=ay+curh / 2.0;
	
	ax = (ax - centerx) * .5 + centerx;
	bx = (bx - centerx) * .5 + centerx;
 	ay = (ay - centery) * 0.5 + centery;
	by = (by - centery) * 0.5 + centery;
	//Reshape(w-8, h-2);
	doredraw=1;
}

void key_w(void) {
	cout << "max iterations, ax, ay, bx, by, power\n";
	cout << "\n" << maxiterations;
	cout << "\n" << ax;
	cout << "\n" << ay;
	cout << "\n" << bx;
	cout << "\n" << by;
	cout << "\n" << power;
	cout << "\n";
}


static void key_up()  //  A Function accessing the UP ARROW Key
{
	double curw = bx - ax, curh = by - ay;
	ay -= curh/8.0;
	by -= curh/8.0;
	doredraw=1;
}


static void key_down()  // A Function accessing the DOWN ARROW Key
{
	double curw = bx - ax, curh = by - ay;
	ay += curh/8.0;
	by += curh/8.0;
	doredraw=1;
}	

static void key_left () {
	double curw = bx - ax, curh = by - ay;
	ax -= curw/8.0;
	bx -= curw/8.0;	
	doredraw=1;
}

static void key_right() {
	double curw = bx - ax, curh = by - ay;
	ax += curw/8.0;
	bx += curw/8.0;	
	doredraw=1;
}

static void key_esc()  // A Function Accessing the ESCAPE key
{
   auxQuit();          // If pressed, the window QUITS
}


static void key_e() {
	ax=-2; ay=2; bx=2; by=-2;
	doredraw=1;
	power=3;
}


static void key_z() {
	option=1;
}

static void key_o() {
	maxiterations-=20;
	doredraw=1;
}
static void key_p() {
	maxiterations+=20;
	doredraw=1;
}
static void key_k() {
	maxiterations-=5;
	doredraw=1;
}
static void key_l() {
	maxiterations+=5;
	doredraw=1;
}

static void key_f() {
	doredraw=1;
	option=4;
}

static void key_r() {
	doredraw=1;
}

static void key_t() {
	//get first values to get viewed
	cout << "\n\n\n\n";
	cout << "max iterations:"; cin >> maxiterations;
       	cout << "ax:"; cin >> ax;
       	cout << "ay:"; cin >> ay;
       	cout << "bx:"; cin >> bx;
       	cout << "by:"; cin >> by;
	cout << "power:"; cin >> power;
        cout << "\n";
	doredraw=1;
}







//  ------  redraw screen func:



static void display( )  // Main display routine called from event loop
{
   
   if(doredraw || option==5) {
	doredraw=0;	
   	glClear( GL_COLOR_BUFFER_BIT );
	godraw();
	glFlush();
   	auxSwapBuffers();
   }	


}





//  -----  main func:



int main( int argc, char **argv )
{  
	
   
   goinit();
   coolpal();	

   auxInitDisplayMode( AUX_RGBA );  // Initialize Display in RGB mode

   auxInitPosition( 0,0, w-8, h-2 );  // Draw a window starting at (50,50)

   if (auxInitWindow("My Window") == GL_FALSE)  // Create window and give it 
    { 						// a name.  If problems, quit. 
      auxQuit();
    }

   Init();  // Call Init routine

   auxExposeFunc(Reshape);  //  More Magic

   auxReshapeFunc(Reshape);
   //key events
   auxKeyFunc( AUX_UP, key_up );
   auxKeyFunc( AUX_DOWN, key_down );
   auxKeyFunc( AUX_LEFT, key_left );
   auxKeyFunc( AUX_RIGHT, key_right );
   auxKeyFunc( 'a', key_a );
   auxKeyFunc( 'q', key_q );
   auxKeyFunc( 'w', key_w );
   auxKeyFunc( 'e', key_e );
   auxKeyFunc( 'z', key_z );
   auxKeyFunc( 'o', key_o);
   auxKeyFunc( 'p', key_p);
   auxKeyFunc( 'k', key_k);
   auxKeyFunc( 'l', key_l);
   auxKeyFunc( 'f', key_f);
   auxKeyFunc( 'r', key_r);
   auxKeyFunc( 't', key_t);


   tkMouseDownFunc(gomouse);

   auxMainLoop( display );  // Call display loop with display function
   //display();


   return 0;
}




// --- arc tan b
double atanb(double y, double x) {
	double ans;
	if(y==0) {
		if(x>=0) return 0;
		else return pi;
	}
	else if(x==0) {
		if(y>=0) return pi/2.0;
		else return 3.0 * pi / 2.0;
	}
	else {
		ans = atan(y / x);
		if(x>=0) {
			if(y>=0) return ans;
			else return pi * 2.0 + ans;
		}
		if(x<=0) return pi + ans;
	}
}


// --- find pthag theorm
double distance(double x1, double y1, double x2, double y2) {
	return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}

