/* マンデルブロ集合全体図 2000.05.24 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Mandel extends Applet
	implements Runnable, MouseListener, MouseMotionListener, ActionListener {
	/* 色の定義 */
	Color[] col = {
		new Color(255,   0,   0),
		new Color(255, 128,   0),
		new Color(255, 255,   0),
		new Color(128, 255,   0),
		new Color(  0, 255,   0),
		new Color(  0, 255, 128),
		new Color(  0, 255, 255),
		new Color(  0, 128, 255),
		new Color(  0,   0, 255),
		new Color(128,   0, 255),
		new Color(255,   0, 255),
		new Color(255,   0, 128),
	};
	
	/* ボタン等 */
	public Label iterLabel;
	public TextField iterN;
	public Button drawButton;
	public Button undoButton;
	public Button initButton;
	
	/* 画面・スレッド */
	Image ScreenImage;
	Graphics ScreenGraph;
	Thread t;
	boolean running;
	
	/* 計算用変数 */
	double ox = -0.75;
	double oy = 0.0;
	double ow = 1.25;
	double xmin = -2.0;
	double xmax = 0.5;
	double ymin = -1.25;
	double ymax = 1.25;
	double newox = ox;
	double newoy = oy;
	double newow = ow;
	double newxmin = xmin;
	double newxmax = xmax;
	double newymin = ymin;
	double newymax = ymax;
	int n = 50;
	int wx;
	int wy;
	
	/* 選択範囲 */
	int left, top, right, bottom;
	
	/* 初期化 */
	public void init() {
		/* 画面サイズ取得 */
		Dimension d = getSize();
		wx = d.width;
		wy = d.height;
		
		/* ボタン等定義 */
		iterLabel = new Label("iter:");
		add(iterLabel);
		iterN = new TextField(Integer.toString(n));
		add(iterN);
		drawButton = new Button("Draw");
		add(drawButton);
		drawButton.addActionListener(this);
		undoButton = new Button("Undo");
		add(undoButton);
		undoButton.addActionListener(this);
		initButton = new Button("Init");
		add(initButton);
		initButton.addActionListener(this);
		addMouseListener(this);
		addMouseMotionListener(this);
		
		/* 画面定義 */
		ScreenImage = createImage(wx, wy);
		ScreenGraph = ScreenImage.getGraphics();
		
		/* 実行 */
		t = new Thread(this);
		running = true;
		t.start();
	}
	
	/* ボタン等チェック */
	public void actionPerformed(ActionEvent e) {
		/* "Draw"ボタン */
		if (e.getSource() == drawButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			if (left > right) {
				int tmp = left; left = right; right = tmp;
			}
			if (top > bottom) {
				int tmp = top; top = bottom; bottom = tmp;
			}
			
			/* 変数計算 */
			newxmin = xmin + (xmax - xmin) * (double)left / (double)wx;
			newxmax = xmin + (xmax - xmin) * (double)right / (double)wx;
			newymin = ymax - (ymax - ymin) * (double)bottom / (double)wy;
			newymax = ymax - (ymax - ymin) * (double)top / (double)wy;
			newox = (newxmin + newxmax) / 2.0;
			newoy = (newymin + newymax) / 2.0;
			if (newxmax - newxmin > newymax - newymin) {
				newow = (newxmax - newxmin) / 2.0;
			}
			else {
				newow = (newymax - newymin) / 2.0;
			}
			
			/* 変数交換 */
			double tmp;
			tmp = xmin; xmin = newxmin; newxmin = tmp;
			tmp = xmax; xmax = newxmax; newxmax = tmp;
			tmp = ymin; ymin = newymin; newymin = tmp;
			tmp = ymax; ymax = newymax; newymax = tmp;
			tmp = ox; ox = newox; newox = tmp;
			tmp = oy; oy = newoy; newoy = tmp;
			tmp = ow; ow = newow; newow = tmp;
			
			/* 再計算 */
			t = new Thread(this);
			running = true;
			t.start();
		}
		/* "Undo"ボタン */
		else if (e.getSource() == undoButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			/* 変数交換 */
			double tmp;
			tmp = xmin; xmin = newxmin; newxmin = tmp;
			tmp = xmax; xmax = newxmax; newxmax = tmp;
			tmp = ymin; ymin = newymin; newymin = tmp;
			tmp = ymax; ymax = newymax; newymax = tmp;
			tmp = ox; ox = newox; newox = tmp;
			tmp = oy; oy = newoy; newoy = tmp;
			tmp = ow; ow = newow; newow = tmp;
			
			/* 再計算 */
			t = new Thread(this);
			running = true;
			t.start();
		}
		/* "Init"ボタン */
		else if (e.getSource() == initButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			/* 変数初期化 */
			xmin = -2.0;
			xmax =  0.5;
			ymin = -1.25;
			ymax =  1.25;
			ox = -0.75;
			oy =  0.0;
			ow =  1.25;
			
			/* 再計算 */
			t = new Thread(this);
			running = true;
			t.start();
		}
	}
	
	/* 範囲選択開始 */
	public void mousePressed(MouseEvent e) {
		left = e.getX();
		top = e.getY();
		right = left + 1;
		bottom = top + 1;
		repaint();
	}
	
	/* 範囲選択中*/
	public void mouseDragged(MouseEvent e) {
		right = e.getX();
		bottom = e.getY();
		repaint();
	}
	
	/* 範囲選択終了*/
	public void mouseReleased(MouseEvent e) {
		right = e.getX();
		bottom = e.getY();
		repaint();
	}
	
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseMoved(MouseEvent e) {}
	
	/* 実行 */
	public void run() {
		/* 選択範囲クリア */
		left = 0;
		top = 0;
		right = wx;
		bottom = wy;
		
		/* 変数表示・取得 */
		showStatus("ox=" + ox + " / oy=" + oy + " / ow=" + ow);
		n = Integer.valueOf(iterN.getText()).intValue();
		
		/* 計算開始 */
		makeMandel();
		
		/* 計算終了*/
		running = false;
		t = null;
	}
	
	/* 画面更新 */
	public void update(Graphics g) {
		paint(g);
	}
	
	/* 表示処理 */
	public void paint(Graphics g) {
		// NT+IE では synchronized が必要だった
		synchronized(this) {
			g.drawImage(ScreenImage, 0, 0, this);
			if (left != 0 && top != 0 && right != wx && bottom != wy) {
				int w = (left < right ? left : right);
				int h = (top < bottom ? top : bottom);
				int ww = (left < right ? right - left : left - right);
				int hh = (top < bottom ? bottom - top : top - bottom);
				g.setXORMode(Color.white);
				g.drawRect(w, h, ww, hh);
			}
		}
	}
	
	/* 計算 */
	public void makeMandel() {
		double dx, dy, u, v, x, y, x2, y2;
		int i, j, k, l;
		
		/* 表示領域クリア */
		ScreenGraph.setColor(Color.lightGray);
		ScreenGraph.fillRect(0, 0, wx, wy);
		
		/* パラメータ範囲取得 */
		if (wx >= wy) {
			xmax = ox + ow * (double)wx / (double)wy;
			xmin = ox - ow * (double)wx / (double)wy;
			ymax = oy + ow;
			ymin = oy - ow;
		}
		else {
			xmax = ox + ow;
			xmin = ox - ow;
			ymax = oy + ow * (double)wy / (double)wx;
			ymin = oy - ow * (double)wy / (double)wx;
		}
		dx = (xmax - xmin) / (wx - 1);
		dy = (ymax - ymin) / (wy - 1);
		
		/* 計算ループ */
		for (j = 0; j < wy; j++) {
			v = ymax - dy * j;
			for (i = 0; i < wx; i++) {
				u = xmin + dx * i;
				x = 0.0;
				y = 0.0;
				l = 0;
				for (k = 1; k <= n; k++) {
					x2 = x * x;
					y2 = y * y;
					if (x2 + y2 > 4.0) {
						l = k;
						break;
					}
					y = 2.0 * x * y + v;
					x = x2 - y2 + u;
				}
				
				/* 中断か？ */
				if (!running) {
					return;
				}
				
				/* 色設定・点表示 */
				if (l > 0) {
					ScreenGraph.setColor(col[l % col.length]);
				}
				else {
					ScreenGraph.setColor(Color.black);
				}
				// NT+IE では synchronized が必要だった
				synchronized(this) {
					ScreenGraph.fillRect(i, j, 1, 1);
				}
			}
			repaint();
		}
	}
}

