Ryoichi Mizuno - Scientific Computer Graphics | Upper Directory |

//////////////////////////////////////////////////////////
/* Bezier Surface, Copyright 2001-2010 Ryoichi Mizuno   */
/* ryoichi[at]mizuno.org                                */
/* Dept. of Complexity Science and Engineering          */
/* at The University of Tokyo                           */
//////////////////////////////////////////////////////////
spacer
import java.awt.*;
import java.applet.*;
import java.awt.event.*;

public class mandelbrotSet extends Applet implements Runnable
{
	// マンデルブロー集合
	// Z(n+1) = Z(n) * Z + C
	// Z(0) = a + b * i
	// C = p + q * i

	Thread th = null;
		
	Image mainImg, bufImg, infoImg;
	Graphics mainGraph, bufGraph, infoGraph;
	
	float a = 0.0f;								// ジュリア実部
	float b = 0.0f;								// ジュリア虚部
	float p, q;									// マンデルブロ実部, 虚部
	
	int iteration = 200;						// 反復回数
	float threshold = 4.0f;						// 発散閾値
	
	limit range = new limit();					// 計算範囲
	limit rangePrev = new limit();				// 前回の計算範囲
	
	int sX0, sX1, sY0, sY1, sBuf;				// 選択領域
	
	int w, h;									// ウィンドウサイズ
	int margin = 50;							// 余白

	int mX, mY;									// マウスポインタの位置
	
	boolean goFlag = true, dragFlag = false;	// フラグ
	boolean colorTblFlag = true;				// フラグ
	boolean undoFlag = false;					// フラグ
	
	Button redraw, undo, db, hf, colorMethod;	// GUI ボタン
	Button reset;								// GUI ボタン
	Button apply, initParam;					// GUI ボタン
	TextField juliaRe, juliaIm;					// GUI テキストフィールド
	TextField thresholdTF, iterationTF;			// GUI テキストフィールド
	Label juliaReLab, juliaImLab;				// GUI ラベル
	Label thresholdLab, iterationLab;			// GUI ラベル
	int gap = 10;								// GUI 間の距離
	
	Applet applet = this;
	
	// 初期化
	public void init()
	{
		// ウィンドウサイズの取得
		w = getSize().width;
		h = getSize().height;

		// メインフレームの設定
		mainImg = createImage(w, h);
		mainGraph = mainImg.getGraphics();
		
		// バッファフレームの設定
		bufImg = createImage(w, h);
		bufGraph = bufImg.getGraphics();
		
		// インフォメーションフレームの設定
		infoImg = createImage(100, 10);
		infoGraph = infoImg.getGraphics();
		
		// 計算範囲の初期化
		initRange();
		
		// 前回の計算範囲の初期化
		initRangePrev();
				
		// GUIの設定
		setGUI();
		
		// マウスモーションの設定
		setMouseMotion();
		
		// 背景色の設定
		setBackground(Color.white);
	}
	
	// 計算範囲の初期化
	public void initRange()
	{
		range.min.x = -2.0f;
		range.max.x = 1.0f;
		range.min.y = -1.5f;
		range.max.y = 1.5f;
	}
	
	// 前回の計算範囲の初期化
	public void initRangePrev()
	{
		rangePrev.min.x = -2.0f;
		rangePrev.max.x = 1.0f;
		rangePrev.min.y = -1.5f;
		rangePrev.max.y = 1.5f;
	}
		
	// GUIの設定
	public void setGUI()
	{
		// レイアウトの設定
		setLayout(null);

		// ボタンの作成
		add(redraw = new Button("Redraw"));
		add(undo = new Button("Undo"));
		add(db = new Button("X2"));
		add(hf = new Button("/2"));
		add(colorMethod = new Button("Hue Coloration"));
		add(reset = new Button("Reset"));
		
		// ボタンの配置
		redraw.setBounds(margin * 2 + 50 * 0 + gap * 0, h - margin * 2 + 25, 50, 20);
		undo.setBounds(margin * 2 + 50 * 1 + gap * 2, h - margin * 2 + 25, 50, 20);
		db.setBounds(margin * 2 + 50 * 2 + 25 * 0 + gap * 4, h - margin * 2 + 25, 25, 20);
		hf.setBounds(margin * 2 + 50 * 2 + 25 * 1 + gap * 5, h - margin * 2 + 25, 25, 20);
		colorMethod.setBounds(margin * 2 + 50 * 2 + 25 * 2 + gap * 7, h - margin * 2 + 25, 100, 20);
		reset.setBounds(w - margin * 2 - 50, h - margin * 2 + 25, 50, 20);

		// undo ボタンの無効化
		setEnableDisableUndoButton();
		
		// colorMethod ボタンのラベルの設定
		setColorMethodLabel();
		
		// アクションリスナーの設定
		redraw.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actRedraw();}});
		undo.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actUndo();}});
		db.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actDb();}});
		hf.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actHf();}});
		colorMethod.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actColorMethod();}});
		reset.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actReset();}});
		
		// テキストフィールドなどの作成
		add(juliaRe = new TextField(java.lang.Float.toString(a), 50));
		add(juliaReLab = new Label("Julia Re:"));
		add(juliaIm = new TextField(java.lang.Float.toString(b), 50));
		add(juliaImLab = new Label("Julia Im:"));
		add(thresholdTF = new TextField(java.lang.Float.toString(threshold), 50));
		add(thresholdLab = new Label("Threshold:"));
		add(iterationTF = new TextField(java.lang.Integer.toString(iteration), 50));
		add(iterationLab = new Label("Iteration:"));
		add(apply = new Button("Apply"));
		add(initParam = new Button("Initialize"));
		
		// テキストフィールドなどの配置
		juliaRe.setBounds(margin * 2 + 50 * 0 + gap * 0, h - margin * 2 + 70, 50, 20);
		juliaReLab.setBounds(margin * 2 + 50 * 0 + gap * 0, h - margin * 2 + 50, 50, 20);
		juliaIm.setBounds(margin * 2 + 50 * 1 + gap * 1, h - margin * 2 + 70, 50, 20);
		juliaImLab.setBounds(margin * 2 + 50 * 1 + gap * 1, h - margin * 2 + 50, 50, 20);
		thresholdTF.setBounds(margin * 2 + 50 * 2 + gap * 3, h - margin * 2 + 70, 50, 20);
		thresholdLab.setBounds(margin * 2 + 50 * 2 + gap * 3, h - margin * 2 + 50, 60, 20);
		iterationTF.setBounds(margin * 2 + 50 * 3 + gap * 5, h - margin * 2 + 70, 50, 20);
		iterationLab.setBounds(margin * 2 + 50 * 3 + gap * 5, h - margin * 2 + 50, 50, 20);
		apply.setBounds(w - margin * 2 - 50 - 60 - gap * 2, h - margin * 2 + 70, 50, 20);
		initParam.setBounds(w - margin * 2 - 60, h - margin * 2 + 70, 60, 20);
		
		// アクションリスナーの設定
		apply.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actApply();}});
		initParam.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){actInitParam();}});
	}
	
	// redraw の実行
	public void actRedraw()
	{
		goFlag = true;
		repaint();
	}
	
	// undo の実行
	public void actUndo()
	{
		if(undoFlag)
		{
			undoFlag = false;
			setEnableDisableUndoButton();
			
			restoreRange();
			
			initRangePrev();
			
			actRedraw();
		}
	}
	
	coordinate rangeWidht = new coordinate();
	
	// db の実行
	public void actDb()
	{
		bcackupRange();
		
		undoFlag = true;
		setEnableDisableUndoButton();
		
		rangeWidht.x = (range.max.x - range.min.x) / 4.0f;
		rangeWidht.y = (range.max.y - range.min.y) / 4.0f;
		
		range.min.x += rangeWidht.x; range.max.x -= rangeWidht.x;
		range.min.y += rangeWidht.y; range.max.y -= rangeWidht.y;
		
		actRedraw();
	}
	
	// hf の実行
	public void actHf()
	{
		bcackupRange();
		
		undoFlag = true;
		setEnableDisableUndoButton();
		
		rangeWidht.x = (range.max.x - range.min.x) / 2.0f;
		rangeWidht.y = (range.max.y - range.min.y) / 2.0f;
		
		range.min.x -= rangeWidht.x; range.max.x += rangeWidht.x;
		range.min.y -= rangeWidht.y; range.max.y += rangeWidht.y;
		
		actRedraw();
	}
	
	// colorMethod の実行
	public void actColorMethod()
	{
		if(colorTblFlag) colorTblFlag = false;
		else colorTblFlag = true;
		
		setColorMethodLabel();
		
		actRedraw();
	}
	
	// apply の実行
	public void actApply()
	{
		a = Float.valueOf(juliaRe.getText()).floatValue();
		b = Float.valueOf(juliaIm.getText()).floatValue();
		threshold = Float.valueOf(thresholdTF.getText()).floatValue();
		iteration = Integer.valueOf(iterationTF.getText()).intValue();
		
		actRedraw();
	}
	
	// initParam の実行
	public void actInitParam()
	{
		a = 0.0f; b = 0.0f;
		threshold = 4.0f;
		iteration = 200;
		
		juliaRe.setText(java.lang.Float.toString(a));
		juliaIm.setText(java.lang.Float.toString(b));
		thresholdTF.setText(java.lang.Float.toString(threshold));
		iterationTF.setText(java.lang.Integer.toString(iteration));
		
		actRedraw();
	}
	
	// 計算範囲のバックアップ
	public void bcackupRange()
	{
		rangePrev.min.x = range.min.x;
		rangePrev.max.x = range.max.x;
		rangePrev.min.y = range.min.y;
		rangePrev.max.y = range.max.y;
	}
	
	// 計算範囲のレストア
	public void restoreRange()
	{
		range.min.x = rangePrev.min.x;
		range.max.x = rangePrev.max.x;
		range.min.y = rangePrev.min.y;
		range.max.y = rangePrev.max.y;
	}

	
	// reset の実行
	public void actReset()
	{
		// フラグの初期化
		goFlag = true; dragFlag = false; colorTblFlag = true; undoFlag = false;

		// 計算範囲の初期化
		initRange();
		
		// 前回の計算範囲の初期化
		initRangePrev();
		
		// undo ボタンの無効化
		setEnableDisableUndoButton();
		
		// colorMethod ボタンのラベルの設定
		setColorMethodLabel();
		
		actRedraw();
	}
	
	// ボタンの有効化・無効化
	public void setEnableDisableButton()
	{
		if(goFlag)
		{
			redraw.setEnabled(false);
			undo.setEnabled(false);
			db.setEnabled(false);
			hf.setEnabled(false);
			colorMethod.setEnabled(false);
			reset.setEnabled(false);
			apply.setEnabled(false);
			initParam.setEnabled(false);
		}
		else
		{
			redraw.setEnabled(true);
			setEnableDisableUndoButton();
			db.setEnabled(true);
			hf.setEnabled(true);
			colorMethod.setEnabled(true);
			reset.setEnabled(true);
			apply.setEnabled(true);
			initParam.setEnabled(true);
		}
	}
	
	// undo ボタンの有効化・無効化
	public void setEnableDisableUndoButton()
	{
		if(undoFlag) undo.setEnabled(true);
		else undo.setEnabled(false);
	}
	
	// colorMethod ボタンのラベルの設定
	public void setColorMethodLabel()
	{
		if(colorTblFlag) colorMethod.setLabel("Hue Coloration");
		else colorMethod.setLabel("Color Table");
	}
		
	// マウスモーションの設定
	public void setMouseMotion()
	{		
		addMouseMotionListener
			(
				new MouseMotionAdapter()
				{
					// マウスムーブ
					public void mouseMoved(MouseEvent e)
					{
						// マウスポインタの位置の取得
						mX = e.getX(); mY = e.getY();
						
						// グラフに入っている場合
						if(mX > margin && mX < margin + (w - margin * 2) && mY > margin && mY < margin + (h - margin * 3))
						{
							// マウスポインタの形状を変える
							setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
							
							// 座標の表示
							initInfo();
							infoGraph.setColor(Color.black);
							infoGraph.drawString("C = " + java.lang.Float.toString(getWdX(mX)).substring(0,5) + " + " + java.lang.Float.toString(getWdY(mY)).substring(0,5) + "i", 0, 10);
							repaint();
						}
						// グラフに入っていない場合
						else
						{
							// マウスポインタの形状をデフォルトに戻す
							setCursor(Cursor.getDefaultCursor());
							
							// 座標の表示(無効化)
							initInfo();
							infoGraph.setColor(Color.black);
							infoGraph.drawString("C = 0.000 + 0.000i", 0, 10);
							repaint();
						}
					}
					
					// マウスドラッグ
					public void mouseDragged(MouseEvent e)
					{
						// マウスポインタの位置の取得
						mX = e.getX(); mY = e.getY();
						
						// フラグを立てる
						dragFlag = true;
						
						// bufImg に mainImg を挿入
						bufGraph.drawImage(mainImg, 0, 0, applet);
						
						// 矩形領域を強制的にグラフ内におさめる
						if(mX < margin) mX = margin;
						else if(mX > margin + (w - margin * 2)) mX = margin + (w - margin * 2);
						if(mY < margin) mY = margin;
						else if(mY > margin + (h - margin * 3)) mY = margin + (h - margin * 3);
						
						// 矩形領域を描画
						bufGraph.setColor(Color.black);
						bufGraph.drawLine(mX, mY, mX, sY0);
						bufGraph.drawLine(mX, mY, sX0, mY);
						bufGraph.drawLine(sX0, sY0, sX0, mY);
						bufGraph.drawLine(sX0, sY0, mX, sY0);
						
						// 座標の表示
						initInfo();
						infoGraph.setColor(Color.black);
						infoGraph.drawString("C = " + java.lang.Float.toString(getWdX(mX)).substring(0,5) + " + " + java.lang.Float.toString(getWdY(mY)).substring(0,5) + "i", 0, 10);
						
						repaint();
					}

				}
			 );
		
		addMouseListener
			(
				new MouseAdapter()
				{
					// マウスプレス
					public void mousePressed(MouseEvent e)
					{
						// マウスポインタの位置の取得
						mX = e.getX(); mY = e.getY();
						
						// グラフに入っている場合
						if(mX > margin && mX < margin + (w - margin * 2) && mY > margin && mY < margin + (h - margin * 3))
						{
							// マウスポインタの位置を選択領域の始点に代入
							sX0 = mX; sY0 = mY;
							
							// mainImg に bufImg を格納
							mainGraph.drawImage(bufImg, 0, 0, applet);
						}
					}
					
					// マウスリリース
					public void mouseReleased(MouseEvent e)
					{
						// マウスポインタの位置の取得
						mX = e.getX(); mY = e.getY();
						
						if(dragFlag)
						{
							// フラグを折る
							dragFlag = false;

							// 選択領域を強制的にグラフ内におさめる
							if(mX < margin) mX = margin;
							else if(mX > margin + (w - margin * 2)) mX = margin + (w - margin * 2);
							if(mY < margin) mY = margin;
							else if(mY > margin + (h - margin * 3)) mY = margin + (h - margin * 3);

							// マウスポインタの位置を選択領域の終点に代入
							sX1 = mX; sY1 = mY;
							
							// 選択領域の調整
							if(sX0>sX1){sBuf = sX0; sX0 = sX1; sX1 = sBuf;}
							if(sY0<sY1){sBuf = sY1; sY1 = sY0; sY0 = sBuf;}
							
							// 計算範囲のバックアップ
							bcackupRange();
							
							// フラグを立てる
							undoFlag = true;
							
							// undo ボタンの有効化
							setEnableDisableUndoButton();
							
							// 計算領域の取得
							range.min.x = getWdX(sX0); range.max.x = getWdX(sX1);
							range.min.y = getWdY(sY0); range.max.y = getWdY(sY1);
							
							// フラグを立てる
							goFlag = true;
							
							// 再計算
							repaint();
						}
					}
				}
			 );
	}
	
    // スタート
	public void start()
	{
		th = new Thread(this);
		th.start();
    }
    
	// ストップ
    public void stop(){goFlag = false;}

	// ラン
	public void run()
	{
		while(goFlag)
		{
			repaint();
			try{Thread.sleep(10);}
			catch(InterruptedException e){}
		}
	}
	
	// アップデート	
    public void update(Graphics g)
	{
		if(goFlag)
		{
			// ボタンの有効化・無効化
			setEnableDisableButton();

			// バッファフレームの初期化
			initBuf();
			
			// インフォメーションフレームの初期化
			initInfo();
			infoGraph.setColor(Color.black);
			infoGraph.drawString("C = 0.000 + 0.000i", 0, 10);
					
			// マンデルブロ集合の描画
			drawMandlbrotSet(g);
		
			// スレッド停止
			goFlag = false;
			
			// ボタンの有効化・無効化
			setEnableDisableButton();
		}
		
		else if(dragFlag){paint(g);}
		
		// 座標の表示
		g.drawImage(infoImg, margin + (w - margin *2) - 100, 30, this);

	}
	
	// 描画
	public void paint(Graphics g)
	{		
		// 表示
		if(bufImg != null) g.drawImage(bufImg, 0, 0, this);
	}

	// バッファフレームの初期化
	public void initBuf()
	{
		bufGraph.setColor(Color.white);
		bufGraph.fillRect(0, 0, w, h);
		bufGraph.setColor(Color.black);
		bufGraph.drawRect(0, 0, w - 1, h - 1);
		bufGraph.setColor(Color.white);
		bufGraph.fillRect(margin, margin, w - margin * 2, h - margin * 3);
	}
	
	// インフォメーションフレームの初期化
	public void initInfo()
	{
		infoGraph.setColor(Color.white);
		infoGraph.fillRect(0, 0, 100, 10);
	}

	// グリッドの描画
	public void drawGrid()
	{
		bufGraph.setColor(Color.black);
		bufGraph.drawRect(margin, margin, w - margin * 2, h - margin * 3);
		
		// タイトルの描画
		bufGraph.drawString("Mandelbrot Set", 25, 20);
		
		// 軸の説明の描画
		bufGraph.drawString("[Re]", margin + (w - margin * 2) + 5, margin + (h - margin * 3) + 20);
		bufGraph.drawString("[Im]", margin - 35, margin - 5);
			
		// 最小値・最大値の描画
		String strRangeMinX = java.lang.Float.toString(range.min.x) + "00000";
		String strRangeMaxX = java.lang.Float.toString(range.max.x) + "00000";
		String strRangeMinY = java.lang.Float.toString(range.min.y) + "00000";
		String strRangeMaxY = java.lang.Float.toString(range.max.y) + "00000";
		bufGraph.drawString(strRangeMinX.substring(0, 5), margin, margin + (h - margin * 3) + 15);
		bufGraph.drawString(strRangeMaxX.substring(0, 5), margin + (w - margin *2) - 25, margin + (h - margin * 3) + 15);
		bufGraph.drawString(strRangeMinY.substring(0, 5), 15, margin + (h - margin * 3));
		bufGraph.drawString(strRangeMaxY.substring(0, 5), 15, margin + 10);
	}
	
	// 軸の描画
	public void drawAxis()
	{
		int scOriginX, scOriginY;
		
		// 原点のスクリーン座標を求める
		scOriginX = getScX(0.0f);
		scOriginY = getScY(0.0f);
		
		//bufGraph.drawString(scOriginX + ", " + scOriginY, 10, 30);
		
		// 原点のy座標が描画領域に入っていればx軸を描画
		if(scOriginY > margin && scOriginY < (h - margin * 2) )
		{
			bufGraph.setColor(Color.black);
			bufGraph.drawLine(margin, scOriginY, w - margin, scOriginY);
		}
		
		// 原点のx標が描画領域に入っていればy軸を描画
		if(scOriginX > margin && scOriginX < (h - margin) )
		{
			bufGraph.setColor(Color.black);
			bufGraph.drawLine(scOriginX, margin, scOriginX, h - margin * 2);
		}
	}
	
	Color color[] = {Color.blue.brighter(), Color.blue,
					Color.cyan,
					Color.green.brighter(), Color.green,
					Color.yellow.brighter(), Color.yellow,
					Color.orange.brighter(), Color.orange,
					Color.pink, Color.pink.darker(),
					Color.magenta,
					Color.red.brighter(), Color.red};
	int colorLength = color.length;
	
	// マンデルブロ集合の描画
	public void drawMandlbrotSet(Graphics g)
	{
		int idxX, idxY, t;
		
		float a1, b1, a2, b2, p, q;

		Color cHSB, cRGBA;
		
		for (idxX = margin; idxX < w - margin; idxX++)
		{
			// グリッドの描画
			drawGrid();
			
			// pの取得
			p = getWdX(idxX);
			
			for (idxY = margin; idxY < h - margin * 2; idxY++)
			{
				// qの取得
				q = getWdY(idxY);
				
				a1 = a; b1 = b;
				for (t = 0; t < iteration; t++)
				{
					a2 = a1 * a1 - b1 * b1 + p;
					b2 = 2.0f * a1 * b1 + q;
					if (a2 * a2 + b2 * b2 > threshold)
					{
						if(colorTblFlag)
						{
							// カラーテーブルを使った描画
							bufGraph.setColor(color[t % colorLength]);
							bufGraph.fillOval(idxX, idxY, 1, 1);
						}
						else
						{
							// 色相を使った描画
							cHSB = Color.getHSBColor((float)t / (float) iteration, 1.0f, 1.0f);
							bufGraph.setColor(cHSB);
							bufGraph.fillOval(idxX, idxY, 1, 1);
						}

						break;
					}
					a1 = a2; b1 = b2;
				}
			}
			// 軸の描画
			drawAxis();

			// 描画
			paint(g);
		}
	}
	
	// ワールド座標をスクリーン座標に変換(x)
	public int getScX(float inWdX)
	{
		float outScX;
		
		outScX = (inWdX - range.min.x) / (range.max.x - range.min.x) * (float)(w - margin * 2);
		outScX += margin;
		
		return (int)outScX;
	}
	
	// ワールド座標をスクリーン座標に変換(y)
	public int getScY(float inWdY)
	{
		float outScY;
		
		outScY = -(inWdY - range.min.y) / (range.max.y - range.min.y) * (float)(h - margin * 3);
		outScY += (float)(h - margin * 2);
		
		return (int)outScY;
	}
	
	// スクリーン座標をワールド座標に変換(x)
	public float getWdX(int inScX)
	{
		float outWdX;
		
		outWdX = (float)(inScX - margin) * (range.max.x - range.min.x) / (float)(w - margin * 2);
		outWdX += range.min.x;
		
		return outWdX;
	}
	
	// スクリーン座標をワールド座標に変換(y)
	public float getWdY(int inScY)
	{
		float outWdY;
		
		outWdY = -(float)(inScY - (h - margin * 2)) * (range.max.y - range.min.y) / (float)(h - margin * 3);
		outWdY += range.min.y;
		
		return outWdY;
	}
}

class coordinate
{
	public float x;
	public float y;
}

class limit
{
	coordinate min = new coordinate();
	coordinate max = new coordinate();
}

Ryoichi Mizuno - Scientific Computer Graphics
Supplementary Information: Ryoichi Mizuno - Google+ g+External link