/*<pre>
Murphy Stein
10/23/2006
Graduate Computer Graphics, NYU

This class implements a mandelbrot explorer in Java.
*/

import java.awt.*;

public class FractalExplorer extends MISApplet {

	double zoom = 1.5;
	double centerX = -0.5;
	double centerY = 0.0;
	int maxiteration = 255 * 3;
	int colors = 255;
	int colorScheme = 1;

	public void initFrame(double time) { // INITIALIZE ONE ANIMATION FRAME
		if (this.getCursor().getType() != Cursor.CROSSHAIR_CURSOR) {
			this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
     	}
    }

    public void setPixel(int x, int y, int rgb[]) { // SET ONE PIXEL'S COLOR

			int iteration = 0;
			double x1,x0,x2;
			double y1,y0,y2;
			
			// CONVERT SCREEN COORDINATES TO MANDELBROT COORDINATES
			x1 = x0 = getX(x);
			y1 = y0 = getY(y);
			
			x2 = x1*x1;
			y2 = y1*y1;
		 	
		 	// ITERATE UNTIL X,Y DIVERGES OR CONVERGES
			while ( ((x2 + y2) < 4)  &&  (iteration <= maxiteration)) {
			 
				y1 = 2*x1*y1 + y0;
				x1 = x2 - y2 + x0;
				
				x2 = x1*x1;   
				y2 = y1*y1;
				
				iteration++;
			}
		 	
			// SET COLOR OF PIXEL DEPENDING ON COLOR SCHEME
		 	if (colorScheme == 1) {					// GRAYSCALE
				rgb[0] = iteration;
				rgb[1] = iteration;
				rgb[2] = iteration;
			} else if (colorScheme == 2) {
				rgb[0] = iteration;
				rgb[1] = iteration - 255;
				rgb[2] = iteration - 510;
		 	} else if (colorScheme == 3) {
		 		iteration = iteration*colors;
				rgb[0] = unpack(iteration,0);
				rgb[1] = unpack(iteration,1);
				rgb[2] = unpack(iteration,2);
		 	} else if (colorScheme == 4) {
		 		iteration = iteration;
				rgb[0] = unpack(iteration,0);
				rgb[1] = unpack(iteration,1);
				rgb[2] = unpack(iteration,2);
			} else if (colorScheme == 5) {
				rgb[0] = (int)(colors*x2/4.0);
				rgb[1] = (int)(colors*y2/4.0);
				rgb[2] = iteration;
			}
    }
    	
	public boolean mouseUp(Event e, int x, int y) {
		centerX = getX(x);
		centerY = getY(y);
		zoom(0.9);
		return true;
	}
	
	// THESE TWO METHODS RETURN MANDELBROT COORDINATES FROM SCREEN COORDINATES
	public double getX(int x) {
		return ( ((double) x)*(2*zoom)/W - zoom + centerX);
	}
	
	public double getY(int y) {
		return ( ((double) y)*(2*zoom)/H - zoom + centerY);
	}
	
	// ZOOM IN OR OUT
	public void zoom(double z) {
		zoom *= z;
	}
		
	// handle key events
   	public boolean keyDown(Event e, int key) {
      switch (key) {
      	case '\u03EC': centerY -= zoom/(H/5); break;						// UP ARROW -- MOVE UP
      	case '\u03ED': centerY += zoom/(H/5); break;						// DOWN ARROW -- MOVE DOWN
      	case '\u03EE': centerX -= zoom/(W/5); break;						// LEFT ARROW -- MOVE LEFT
      	case '\u03EF': centerX += zoom/(W/5); break;						// DOWN ARROW -- MOVE RIGHT
		case 'a'	 : zoom(0.9); break;									// ZOOM IN
		case 'z'	 : zoom(1.1); break;									// ZOOM OUT
		case '1': 		{
      		colorScheme = 1;				// Set color scheme
			maxiteration = colors;
			break;
		}
      	case '2': 		{
      		colorScheme = 2;				// Set color scheme
			maxiteration = colors*3;
			break;
		}
      	case '3': 		{
      		colorScheme = 3;				// Set color scheme
			maxiteration = colors*colors;
			break;
		}
      	case '4': 		{
      		colorScheme = 4;				// Set color scheme
			maxiteration = colors*colors*colors;
			break;
		}
      	case '5': 		{
      		colorScheme = 5;				// Set color scheme
			maxiteration = colors;
			break;
		}
		default: break;
		}

		return true;
	}

	// NOT USED.  DETERMINES IF INTEGER LIES IN SPECIFIED CLOSED INTERVAL
	public boolean isBetween(int x, int a, int b) {
		if (a > b) {
			return ((x >= b) && (x <= a));
		} else {
			return ((x >= a) && (x <= b));
		}
	}

}
