// File: fractal.cxx
// Written by: michael and Dmitry
// This is a start on a program to draw Mandelbrot's fractal.

//----------------------------------------------------------------------------
#include <graphics.h>
#include <iostream>
using namespace std;
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
const int S = 400; // Size of a graphics window
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Function prototypes

// double distance(double x0, double y0, double x1, double y1);
// Returns the distance between (x0, y0) and (x1, y1)
double distance(double x0, double y0, double x1, double y1);

// draw_fractal will draw a portion of Mandelbrot's fractal centered on
// (centerx, centery) with the specified width and height.
void draw_fractal(double centerx, double centery, double width, double height);

// int mandelbrot_color(double wx, double wy);
// Compute the number of jumps that are required to leave the circle of radius
// 2 that is centered around the origin when we:
// -- start at location (wx, wy)
// -- jump to a new location according to Mandelbrot's equations
// If we hit the LIMIT (declared as a constant) without leaving the circle,
// then we just return the number LIMIT rather than continuing to jump!
int mandelbrot_color(double wx, double wy);

// The shift_center function checks whether there has been a left mouse
// click. If so, then the location of that click is calculated
// in world coordinates, and (centerx, centery) is moved to that spot.
void shift_center(double& centerx, double& centery, double width, double height);

// This function waits until there is a left mouse click or keyboard hit.
void wait_for_input( );

// world(p, v0, v1, pmax) converts p from an interval that ranges from 0 to
// pmax into a double number interval that ranges from v0 to v1.
double world(int p, double v0, double v1, int pmax);

// The zoom function checks whether the + or - key has been pressed.
// If so, then the width and height are cut in half (for the + key) or
// doubled (for the - key).
void zoom(double& width, double& height);

//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
int main()
{
    double centerx, centery, width, height; // To define world coordinates

    
    // Open a graphics window and set the initial world coordinates:
    initwindow(S, S, "Mandelbrot's Fractal");
    centerx = centery = 0.0;
    width = height = 4.0;

    // Fill each pixel with a color that depends on Mandelbrot's fractal.
    for (;;)
    {
	// Draw the current fractal
	draw_fractal(centerx, centery, width, height);

	// Wait for some input:
	wait_for_input( );

	// Process the input:
	shift_center(centerx, centery, width, height);
	zoom(width, height);
    }
    delay(200000);
    return EXIT_SUCCESS;
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
double distance(double x0, double y0, double x1, double y1)
{
    double dx = x1 - x0;
    double dy = y1 - y0;
    return sqrt(dx*dx + dy*dy);
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
void draw_fractal(double centerx, double centery, double width, double height)
{
    int px, py;  // Pixel coordinates on the screen
    double wx, wy; // world coordinates
    int color; // Color depends on px and py
    
    for (px = 0; px < S; ++px)
    {
	for (py = 0; py < S; ++py)
	{
	    // convert px and py
	    wx = world(px, centerx - width/2, centerx + width/2, S);
	    wy = world(py, centery + height/2, centery - height/2, S);
	    color = mandelbrot_color(wx, wy);
	    // Draw the pixel with that much redness:
	    putpixel(px, py, color);
	}
    }
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
int mandelbrot_color(double wx, double wy)
{
    const int LIMIT = 15;           // Maximum number of jumps
    const double sx = wx, sy = wy;  // Where we started jumping
    double wx_new, wy_new;          // The next spot to jump to
    int jumps = 0;                  // How many jumps we've taken
    
    while ((distance(wx, wy, 0, 0) < 2) && (jumps < LIMIT))
    {
	wx_new = wx*wx - wy*wy + sx;
	wy_new = 2*wx*wy + sy;
	++jumps;
	wx = wx_new;
	wy = wy_new;
    }
    return jumps;
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
void shift_center(double& centerx, double& centery, double width, double height) 
{
    int px, py;
    if (ismouseclick(WM_LBUTTONDOWN))
    {
	getmouseclick(WM_LBUTTONDOWN, px, py);
	centerx = world(px, centerx - width/2, centerx + width/2, S);
	centery = world(py, centery + height/2, centery - height/2, S);
    }
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
void wait_for_input( )
{
    while (!kbhit() && !ismouseclick(WM_LBUTTONDOWN))
    {
	delay(100);
    }
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
double world(int p, double v0, double v1, int pmax)
{
    double percent = double(p)/pmax;
    return v0 + percent*(v1 - v0);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void zoom(double& width, double& height)
{
    char key;
    if (kbhit())
    {
	key = getch();
	if (key == '+')
	{
	    width = width/2;
	    height = height/2;
	}
	else if (key == '-')
	{
	    width = width*2;
	    height = height*2;
	}
    }
}
//----------------------------------------------------------------------------
