/*
 * Mandel.java
 *      A simple Java Mandelbrot set
 *      Usage:  <applet code="Mandel.class width=100 height=100>
 *              </applet>
 *
 * Ken Shirriff         ken.shirriff@eng.sun.com
 *
 * Copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * Please refer to the file "license.txt"
 * for further important copyright and licensing information.
 */

import java.awt.*;
import java.awt.image.*;
import java.applet.Applet;

public class Mandel extends Applet implements Runnable {
    double x0= -2.5;
    double x1 = 1.5;
    double y0 = -2;
    double y1 = 2;
    double zoomfactor = 3;
    int max=50;
    byte data[];
    int px, py, pc;
    int width, height;
    int hh;
    int all=0;
    byte redm[] = new byte[10];
    byte greenm[] = new byte[10];
    byte bluem[] = new byte[10];
    MemoryImageSource mBitmap;
    Image img;
    IndexColorModel icm;
    Thread kicker = null;
    Choice button;
    boolean help = false;
    boolean changemax = false;
    boolean textfield = false;
    TextField tf;

	public void start() {
		if (kicker != null) {
			kicker.resume();
		}
	}
	public void stop() {
		if (kicker!=null) {
			kicker.suspend();
		}
	}
	public byte interp(int a,int b,double f) {
		return (byte)(a*(1-f)+b*f);
	}

	public void reset() {
	    x0= -2.5;
	    x1 = 1.5;
	    y0 = -2;
	    y1 = 2;
	    max=50;
	    if (kicker != null) {
		kicker.stop();
	    }
	    kicker = new Thread(this);
	    kicker.start();
	}
	public void help() {
		help = true;
		repaint();
	}
	public void changemax() {
		tf = new TextField(""+max);
		changemax = true;
		repaint();
		if (kicker!=null) {
			kicker.stop();
			kicker = null;
		}
	        add("North",tf);
	    layout();
	}

        public void init() {
	    int i;
	    if (getParameter("width") != null) {
		    width = Integer.parseInt(getParameter("width"));
	    }
	    if (getParameter("height") != null) {
		    height = Integer.parseInt(getParameter("height"));
	    }
	    if (getParameter("max") != null) {
		    max = Integer.parseInt(getParameter("max"));
	    }
	    if (getParameter("zoomfactor") != null) {
		    zoomfactor = Double.valueOf(getParameter("zoomfactor")).doubleValue();
	    }
	    button = new Choice();
	    button.addItem("Options");
	    button.addItem("Reset");
	    button.addItem("Change Max");
	    button.addItem("Help");
	    setLayout(new BorderLayout());
	    add("South",button);
	    layout();
	    hh = height-button.bounds().height;
	    y1 = y0+(x1-x0)/(double)width*hh;
	    redm[0] = 0; greenm[0] = 0; bluem[0] = 0;
	    for (i=0;i<5;i++) {
		    redm[i+1] = interp(255,0,i/4.);
		    greenm[i+1] = interp(0,0,i/4.);
		    bluem[i+1] = interp(255,0,i/4.);
	    }
	    for (i=0;i<4;i++) {
		    redm[i+6] = interp(0,255,i/3.);
		    greenm[i+6] = interp(0,255,i/3.);
		    bluem[i+6] = interp(255,0,i/3.);
	    }
	    icm = new IndexColorModel(8,10,redm,greenm,bluem);
	    kicker = new Thread(this);
	    kicker.start();
        }

	public void paint(Graphics g) {
	    if (help) {
		    g.setColor(Color.gray);
		    g.fillRect(0,0,width-1, height-1);
		    g.setColor(Color.black);
		    int fh = getFontMetrics(getFont()).getHeight();
		    int y = 1;
		    g.drawString("[Help]: help info", 10, (y++)*fh);
		    g.drawString("[Reset]: resets coordinates", 10, (y++)*fh);
		    g.drawString("[Change Max]: change max iteration", 10, (y++)*fh);
		    g.drawString("Click image to zoom", 10, (y++)*fh);
		    g.drawString("Click now to exit help", 10, (y++)*fh);
	    } else if (changemax) {
		    paintComponents(g);
	    } else {
		    if (img != null) {
			    g.drawImage(img,0,0,null);
		    }
		    g.drawRect(0,0,width-1, height-1);
		    if (kicker != null) {
			kicker.resume();
		    }
	    }
	}
	public void update(Graphics g) {
		if (!help && !changemax) {
			mBitmap = new MemoryImageSource(width,hh,icm,data,0,width);
			img = createImage(mBitmap);
		}
		paint(g);
	}
        public void run() {
	    all = 1;
	    data = new byte[hh*width];
	    mBitmap = null;
	    repaint();
	    all=0;
	    repaint();
	    for (int i=0;i<width;i++) {
		double x = x0+i/(double)width*(x1-x0);
		for (int j=0;j<hh;j++) {
			double y = y0+j/(double)hh*(y1-y0);
			double xv;
			double yv;
			double yt, xx, yy;
			xv = x;
			yv = y;
			int l;
			for (l=0;l<max;l++) {
				xx = xv*xv;
				yy = yv*yv;
				if (xx+yy>4) break;
				yv = 2*xv*yv+y;
				xv = xx-yy+x;
			}
			if (l==max) {
				data[i+j*width] = 0;
			} else {
				data[i+j*width] = (byte)((l%9)+1);
			}
		}
		if ((i%10)==0) {
		        mBitmap = new MemoryImageSource(width,hh,icm,data,0,width);
			repaint();
			kicker.suspend();
		}
	    }
	    mBitmap = new MemoryImageSource(width,hh,icm,data,0,width);
	    repaint();
	    kicker = null;
        }

	public void reparam() {
	}

	public boolean mouseDown(java.awt.Event evt, int xm, int ym) {
		if (help) {
			help = false;
			repaint();
			return true;
		} else if (changemax) {
			changemax = false;
		}
		if (ym>hh) {
			reparam();
			return true;
		}
		double xn = x0+xm/(double)width*(x1-x0);
		double yn = y0+ym/(double)hh*(y1-y0);
		double xw = x1-x0;
		double yw = y1-y0;
		xw /= zoomfactor*2;
		yw /= zoomfactor*2;
		x0 = xn-xw;
		x1 = xn+xw;
		y0 = yn-yw;
		y1 = yn+yw;
		max = (int)(max * 1.25);
		if (kicker != null) {
			kicker.stop();
			kicker = null;
		}
		kicker = new Thread(this);
		kicker.start();
		return true;
	}

	public boolean action(Event e, Object o) {
		if (e.target instanceof Choice) {
			help = false;
			changemax = false;
			if (((String)o).equals("Reset")) {
				reset();
			} else if (((String)o).equals("Help")) {
				help();
			} else if (((String)o).equals("Change Max")) {
				changemax();
			}
			((Choice)e.target).select("Options");
		} else if (e.target instanceof TextField) {
			changemax = false;
			max = Integer.parseInt(String.valueOf(o));
			remove(tf);
			tf = null;
			if (kicker != null) {
				kicker.stop();
				kicker = null;
			}
			kicker = new Thread(this);
			kicker.start();
		}
		return true;
	}

        public String getAppletInfo() {
                return "Mandel by Ken Shirriff  shirriff@eng.sun.com";
        }
 
        public String[][] getParameterInfo() {
                String [][] info = {
                        {"width      ","int        ", "image width"},
                        {"height     ","int        ", "image height"},
                        {"max        ","int        ", "max iterations"}
		};
		return info;
	}
    }
