// 地図を、ポリゴンを使って、スムーズシェイディングを施して表示する プログラム
// 「ｍａｐｏｌｙｎ」（マポリン (^_^)）

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Mapolyn 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),
	};
	
	/* ボタン等 */
	Label iterLabel;
	TextField iterN;
	Button draw2DButton;
	Button init2DButton;
	Label multLabel;
	TextField multN;
	List colorList;
	Button draw3DPolygonButton;
	Button draw3DShadeButton;
	
	/* 画面・スレッド */
	Image ScreenImage;
	Graphics ScreenGraph;
	Thread t;
	boolean running;
	
	/* 地図サイズ */
	int N = 100;
	double height = 70.0;
	
	/* 立体ウィンドウサイズ */
	int size3D;
	
	/* 計算用変数 */
	double xmin;
	double xmax;
	double ymin;
	double ymax;
	double ox;
	double oy;
	double ow;
	int n;
	int scrnx;
	int scrny;
	int wx;
	int wy;
	int wz;
	int multiple;
	boolean grayScale;
	
	/* 選択範囲 */
	boolean dragging = false;
	int left, top, right, bottom;
	
	/* 描画モード*/
	int dimmode;
	
	/* その他もろもろ */
	int GXmin, GXmax, GYmin, GYmax;
	int OX, OY, Win;
	double Ib = .05, Ia = .1, IpKd = .7, Ks = .3, KsN = 4.0;
	double IntensMax = Ib + Ia + IpKd + Ks;
	int Imin = 32, Imax = 255, Idelta = Imax - Imin;
	int Col;
	int rPol, gPol, bPol;
	
	double[][] pa = new double[N][N];
	Color[][] lcol = new Color[N][N];
	double[][] y = new double[N][N];
	double[][] px0 = new double[N][N];
	double[][] py0 = new double[N][N];
	double[][] pz0 = new double[N][N];
	double[][] px1 = new double[N][N];
	double[][] py1 = new double[N][N];
	double[][] pz1 = new double[N][N];
	double[][] tx = new double[N][N];
	double[][] ty = new double[N][N];
	double[][] tz = new double[N][N];
	double[][] gx = new double[N][N];
	double[][] gy = new double[N][N];
	double a11, a12, a13, a14;
	double a21, a22, a23, a24;
	double a31, a32, a33, a34;
	double a41, a42, a43, a44;
	double LtX, LtY, LtZ;
	int dx, dz;
	double Xfrom, Yfrom, Zfrom;
	double Xat, Yat, Zat;
	double Kp, Hp;
	int[] Xmin;
	int[] Xmax;
	int Ymin, Ymax;
	double Pa, Pb, Pc, Pd;
	double Sx, Sy, Sz;
	boolean Simple;
	int[] polx = new int[3];
	int[] poly = new int[3];
	
	/* 初期化 */
	public void init() {
		/* 変数初期化 */
		initVar();
		n = 50;
		multiple = 4;
		grayScale = true;
		
		/* 画面サイズ取得 */
		Dimension d = getSize();
		scrnx = d.width;
		scrny = d.height;
		wx = N;
		wy = N;
		wz = wy;
		if (scrnx - N < scrny) {
			size3D = scrnx - N;
		}
		else {
			size3D = scrny;
		}
		GXmin = wx;
		GXmax = scrnx - 1;
		GYmin = 0;
		GYmax = scrny - 1;
		OX = (GXmin + GXmax) / 2;
		OY = (GYmin + GYmax) / 2;
		Win = size3D / 2;
		Xmin = new int[size3D];
		Xmax = new int[size3D];
		
		/* ボタン等定義 */
		iterLabel = new Label("iter:");
		add(iterLabel);
		iterN = new TextField(Integer.toString(n));
		add(iterN);
		draw2DButton = new Button("Draw2D");
		add(draw2DButton);
		draw2DButton.addActionListener(this);
		init2DButton = new Button("Init2D");
		add(init2DButton);
		init2DButton.addActionListener(this);
		multLabel = new Label("mult:");
		add(multLabel);
		multN = new TextField(Integer.toString(multiple));
		add(multN);
		colorList = new List(2);
		colorList.add("Gray");
		colorList.add("Color");
		colorList.select(grayScale ? 0 : 1);
		add(colorList);
		draw3DPolygonButton = new Button("Draw3D(Polygon)");
		add(draw3DPolygonButton);
		draw3DPolygonButton.addActionListener(this);
		draw3DShadeButton = new Button("Draw3D(Shade)");
		add(draw3DShadeButton);
		draw3DShadeButton.addActionListener(this);
		addMouseListener(this);
		addMouseMotionListener(this);
		
		/* 画面定義 */
		ScreenImage = createImage(scrnx, scrny);
		ScreenGraph = ScreenImage.getGraphics();
		
		/* 表示領域クリア */
		ScreenGraph.setColor(Color.lightGray);
		ScreenGraph.fillRect(0, 0, scrnx, scrny);
		
		/* 実行 */
		dimmode = 2;
		t = new Thread(this);
		running = true;
		t.start();
		while (t != null) {
			Thread.yield();
		}
		dimmode = 3;
		t = new Thread(this);
		running = true;
		t.start();
	}
	
	/* ボタン等チェック */
	public void actionPerformed(ActionEvent e) {
		/* "Draw2D"ボタン */
		if (e.getSource() == draw2DButton) {
			/* 計算中止 */
			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;
			}
			
			/* 変数計算 */
			double newxmin, newxmax, newymin, newymax;
			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;
			ox = (newxmin + newxmax) / 2.0;
			oy = (newymin + newymax) / 2.0;
			if (newxmax - newxmin > newymax - newymin) {
				ow = (newxmax - newxmin) / 2.0;
			}
			else {
				ow = (newymax - newymin) / 2.0;
			}
			
			/* 再計算 */
			dimmode = 2;
			t = new Thread(this);
			running = true;
			t.start();
		}
		/* "Init2D"ボタン */
		else if (e.getSource() == init2DButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			/* 変数初期化 */
			initVar();
			
			/* 再計算 */
			dimmode = 2;
			t = new Thread(this);
			running = true;
			t.start();
		}
		/* "Draw3DPolygon"ボタン */
		else if (e.getSource() == draw3DPolygonButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			Simple = true;
			
			/* 再計算 */
			dimmode = 3;
			t = new Thread(this);
			running = true;
			t.start();
		}
		/* "Draw3DShade"ボタン */
		else if (e.getSource() == draw3DShadeButton) {
			/* 計算中止 */
			running = false;
			while (t != null) {
				Thread.yield();
			}
			
			Simple = false;
			
			/* 再計算 */
			dimmode = 3;
			t = new Thread(this);
			running = true;
			t.start();
		}
	}
	
	/* 範囲選択開始 */
	public void mousePressed(MouseEvent e) {
		if (e.getX() < wx && e.getY() < wy) {
			dragging = true;
			left = e.getX();
			top = e.getY();
			right = left + 1;
			bottom = top + 1;
			repaint();
		}
	}
	
	/* 範囲選択中*/
	public void mouseDragged(MouseEvent e) {
		if (dragging == true && e.getX() < wx && e.getY() < wy) {
			right = e.getX();
			bottom = e.getY();
			repaint();
		}
	}
	
	/* 範囲選択終了*/
	public void mouseReleased(MouseEvent e) {
		if (dragging == true) {
			dragging = false;
		}
	}
	
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseMoved(MouseEvent e) {}
	
	/* 実行 */
	public void run() {
		if (dimmode == 2) {
			/* 選択範囲クリア */
			left = 0;
			top = 0;
			right = wx;
			bottom = wy;
			
			/* 変数表示・取得 */
			showStatus(xmin + "<Re c<" + xmax + ", " + ymin + "<Im c<" + ymax);
			n = Integer.valueOf(iterN.getText()).intValue();
			
			/* 計算開始 */
			makeFigure2D();
		}
		else if (dimmode == 3) {
			/* 変数表示・取得 */
			multiple = Integer.valueOf(multN.getText()).intValue();
			grayScale = (colorList.getSelectedIndex() == 0);
			
			/* 計算開始 */
			makeFigure3D();
		}
		
		/* 計算終了*/
		running = false;
		t = null;
	}
	
	/* 画面更新 */
	public void update(Graphics g) {
		paint(g);
	}
	
	/* 表示処理 */
	public void paint(Graphics g) {
		synchronized(this) {
			g.drawImage(ScreenImage, 0, 0, this);
			if (left != 0 && top != 0 && right != scrnx && bottom != scrny) {
				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 initVar() {
		xmin = -2.0;
		xmax = 0.5;
		ymin = -1.25;
		ymax = 1.25;
		ox = -0.75;
		oy = 0.0;
		ow = 1.25;
		
		//PRINT "パラメータの入力（座標系は右手系で垂直軸はＹ軸）"
		//PRINT "対象物体から見た光源の方向"
		LtX = -1.0; LtY = 1.5; LtZ = 1.5;
		//PRINT "どこから見るか"
		Xfrom = 150.0; Yfrom = 175.0; Zfrom = 250.0;
		//PRINT "どこを見るか"
		Xat = 70.0; Yat = 60.0; Zat = 100.0;
		//PRINT "画面内の大きさ"
		Hp = 4.0;
		//PRINT "表示の仕方"
		Simple = true;
		
		Kp = 1.0;
		double LL = Math.sqrt(LtX * LtX + LtY * LtY + LtZ * LtZ);
		LtX = LtX / LL; LtY = LtY / LL; LtZ = LtZ / LL;
		if (LtX > 0) {
			dx = 1;
		}
		else if (LtX < 0) {
			dx = -1;
		}
		else {
			dx = 0;
		}
		if (LtZ > 0) {
			dz = 1;
		}
		else if (LtZ < 0) {
			dz = -1;
		}
		else {
			dz = 0;
		}
		
		getMatrix();
	}
	
	void displayMap()
	{
		for (int k = 1; k <= wz - 3; k++) {
			int k1 = k + 1;
			for (int i = 1; i <= wx - 3; i++) {
				int i1 = i + 1;
				displaySub(i, k, i, k1, i1, k, i1, k1, 0);
				displaySub(i1, k1, i1, k, i, k1, i1, k1, 1);
				repaint();
				
				/* 中断か？ */
				if (!running) {
					return;
				}
			}
		}
	}
	
	void displaySub(int i0, int k0, int i1, int k1, int i2, int k2, int pi, int pk, int p01)
	{
		double x0, y0, z0;
		double x1, y1, z1;
		double x2, y2, z2;
		double a, b, c, d;
		double a2, b2, c2, d2;
		double Ca, Cb, Intens;
		
		double i10, i20, k10, k20;
		double x10, x20;
		double y10, y20;
		double gx0, gy0;
		double tx2, ty2, tz2;
		
		double px, py, pz;
		
		double ta, tb, tc, td;
		double xa, xb, xc, xd;
		double ya, yb, yc, yd;
		double za, zb, zc, zd;
		
		double ra, rb, rc, rd;
		double ga, gb, gc, gd;
		double ba, bb, bc, bd;
		int ir, ig, ib;
		
		int it = 0, kt = 0;
		
		x0 = gx[i0][k0]; x1 = gx[i1][k1]; x2 = gx[i2][k2];
		y0 = gy[i0][k0]; y1 = gy[i1][k1]; y2 = gy[i2][k2];
		triangle((int)(x0 + .5), (int)(y0 + .5), (int)(x1 + .5), (int)(y1 + .5), (int)(x2 + .5), (int)(y2 + .5));
		
		if (p01 == 0) {
			px = px0[pi][pk]; py = py0[pi][pk]; pz = pz0[pi][pk];
		}
		else {
			px = px1[pi][pk]; py = py1[pi][pk]; pz = pz1[pi][pk];
		}
		a = Xfrom - i0; b = Yfrom - y[i0][k0]; c = Zfrom - k0;
		d = Math.sqrt(a * a + b * b + c * c);
		a = a / d; b = b / d; c = c / d;
		Ca = px * LtX + py * LtY + pz * LtZ;
		
		if (a * px + b * py + c * pz <= 0) {
			rPol = Imax;
			gPol = Imax;
			bPol = Imax;
			Intens = Ib;
			drawTriangle(Intens);
		}
		else if (Ca <= 0
			&& tx[i0][k0] == tx[i1][k1] && tx[i1][k1] == tx[i2][k2]
			&& ty[i0][k0] == ty[i1][k1] && ty[i1][k1] == ty[i2][k2]
			&& tz[i0][k0] == tz[i1][k1] && tz[i1][k1] == tz[i2][k2]) {
			rPol = (lcol[i0][k0].getRed() + lcol[i1][k1].getRed() + lcol[i2][k2].getRed()) / 3;
			gPol = (lcol[i0][k0].getGreen() + lcol[i1][k1].getGreen() + lcol[i2][k2].getGreen()) / 3;
			bPol = (lcol[i0][k0].getBlue() + lcol[i1][k1].getBlue() + lcol[i2][k2].getBlue()) / 3;
			Intens = Ib + Ia;
			drawTriangle(Intens);
		}
		else if (Simple == true) {
			rPol = (lcol[i0][k0].getRed() + lcol[i1][k1].getRed() + lcol[i2][k2].getRed()) / 3;
			gPol = (lcol[i0][k0].getGreen() + lcol[i1][k1].getGreen() + lcol[i2][k2].getGreen()) / 3;
			bPol = (lcol[i0][k0].getBlue() + lcol[i1][k1].getBlue() + lcol[i2][k2].getBlue()) / 3;
			a2 = 2.0 * Ca * px - LtX;
			b2 = 2.0 * Ca * py - LtY;
			c2 = 2.0 * Ca * pz - LtZ;
			d2 = Math.sqrt(a2 * a2 + b2 * b2 + c2 * c2);
			a2 = a2 / d2; b2 = b2 / d2; c2 = c2 / d2;
			Cb = a * a2 + b * b2 + c * c2;
			Intens = Ib + Ia + IpKd * Ca + Ks * power(Cb, KsN);
			drawTriangle(Intens);
		}
		else {
			i10 = i1 - i0; i20 = i2 - i0;
			k10 = k1 - k0; k20 = k2 - k0;
			x10 = x1 - x0; x20 = x2 - x0;
			y10 = y1 - y0; y20 = y2 - y0;
			
			getPlaneVect(
				(double)i0, y[i0][k0], (double)k0,
				(double)i1, y[i1][k1], (double)k1,
				(double)i2, y[i2][k2], (double)k2
			);
			ta = Pa; tb = Pb; tc = Pc; td = Pd;
			
			z0 = tx[i0][k0]; z1 = tx[i1][k1]; z2 = tx[i2][k2];
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			xa = Pa; xb = Pb; xc = Pc; xd = Pd;
			
			z0 = ty[i0][k0]; z1 = ty[i1][k1]; z2 = ty[i2][k2];
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			ya = Pa; yb = Pb; yc = Pc; yd = Pd;
			
			z0 = tz[i0][k0]; z1 = tz[i1][k1]; z2 = tz[i2][k2];
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			za = Pa; zb = Pb; zc = Pc; zd = Pd;
			
			z0 = lcol[i0][k0].getRed(); z1 = lcol[i1][k1].getRed(); z2 = lcol[i2][k2].getRed();
//			z0 = (lcol[i0][k0].getRed() + lcol[i0 - 1][k0].getRed() + lcol[i0 + 1][k0].getRed() + lcol[i0][k0 - 1].getRed() + lcol[i0][k0 + 1].getRed()) / 5;
//			z1 = (lcol[i1][k1].getRed() + lcol[i1 - 1][k1].getRed() + lcol[i1 + 1][k1].getRed() + lcol[i1][k1 - 1].getRed() + lcol[i1][k1 + 1].getRed()) / 5;
//			z2 = (lcol[i2][k2].getRed() + lcol[i2 - 1][k2].getRed() + lcol[i2 + 1][k2].getRed() + lcol[i2][k2 - 1].getRed() + lcol[i2][k2 + 1].getRed()) / 5;
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			ra = Pa; rb = Pb; rc = Pc; rd = Pd;
			
			z0 = lcol[i0][k0].getGreen(); z1 = lcol[i1][k1].getGreen(); z2 = lcol[i2][k2].getGreen();
//			z0 = (lcol[i0][k0].getGreen() + lcol[i0 - 1][k0].getGreen() + lcol[i0 + 1][k0].getGreen() + lcol[i0][k0 - 1].getGreen() + lcol[i0][k0 + 1].getGreen()) / 5;
//			z1 = (lcol[i1][k1].getGreen() + lcol[i1 - 1][k1].getGreen() + lcol[i1 + 1][k1].getGreen() + lcol[i1][k1 - 1].getGreen() + lcol[i1][k1 + 1].getGreen()) / 5;
//			z2 = (lcol[i2][k2].getGreen() + lcol[i2 - 1][k2].getGreen() + lcol[i2 + 1][k2].getGreen() + lcol[i2][k2 - 1].getGreen() + lcol[i2][k2 + 1].getGreen()) / 5;
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			ga = Pa; gb = Pb; gc = Pc; gd = Pd;
			
			z0 = lcol[i0][k0].getBlue(); z1 = lcol[i1][k1].getBlue(); z2 = lcol[i2][k2].getBlue();
//			z0 = (lcol[i0][k0].getBlue() + lcol[i0 - 1][k0].getBlue() + lcol[i0 + 1][k0].getBlue() + lcol[i0][k0 - 1].getBlue() + lcol[i0][k0 + 1].getBlue()) / 5;
//			z1 = (lcol[i1][k1].getBlue() + lcol[i1 - 1][k1].getBlue() + lcol[i1 + 1][k1].getBlue() + lcol[i1][k1 - 1].getBlue() + lcol[i1][k1 + 1].getBlue()) / 5;
//			z2 = (lcol[i2][k2].getBlue() + lcol[i2 - 1][k2].getBlue() + lcol[i2 + 1][k2].getBlue() + lcol[i2][k2 - 1].getBlue() + lcol[i2][k2 + 1].getBlue()) / 5;
			getPlaneVect(x0, y0, z0, x1, y1, z1, x2, y2, z2);
			ba = Pa; bb = Pb; bc = Pc; bd = Pd;
			
			for (int gy = Ymin; gy <= Ymax; gy++) {
				if (gy >= GYmin && gy <= GYmax) {
					for (int gx = Xmin[gy]; gx <= Xmax[gy]; gx++) {
						if (gx >= GXmin && gx <= GXmax) {
							if (xc == 0) {
								a = 0;
							}
							else {
								a = -(xa * gx + xb * gy + xd) / xc;
							}
							if (yc == 0) {
								b = 0;
							}
							else {
								b = -(ya * gx + yb * gy + yd) / yc;
							}
							if (zc == 0) {
								c = 0;
							}
							else {
								c = -(za * gx + zb * gy + zd) / zc;
							}
							d = Math.sqrt(a * a + b * b + c * c);
							if (d != 0) {
								a = a / d; b = b / d; c = c / d;
							}
							Ca = a * LtX + b * LtY + c * LtZ;
							if (Ca <= 0) {
								rPol = Imax;
								gPol = Imax;
								bPol = Imax;
								Intens = Ib + Ia;
							}
							else {
								a2 = 2.0 * Ca * a - LtX;
								b2 = 2.0 * Ca * b - LtY;
								c2 = 2.0 * Ca * c - LtZ;
								d2 = Math.sqrt(a2 * a2 + b2 * b2 + c2 * c2);
								a2 = a2 / d2; b2 = b2 / d2; c2 = c2 / d2;
								
								gx0 = gx - x0; gy0 = gy - y0;
								a = (y20 * gx0 - x20 * gy0) / (y20 * x10 - x20 * y10);
								b = (x10 * gy0 - y10 * gx0) / (y20 * x10 - x20 * y10);
								tx2 = i0 + a * i10 + b * i20;
								tz2 = k0 + a * k10 + b * k20;
								if (tb == 0) {
									ty2 = 0;
								}
								else {
									ty2 = -(ta * tx2 + tc * tz2 + td) / tb;
								}
								
								if (dx > 0) {
									if (p01 == 0) {
										it = i0 + 2;
									}
									else {
										it = i0 + 1;
									}
								}
								else if (dx < 0) {
									if (p01 == 0) {
										it = i0 - 1;
									}
									else {
										it = i0 - 2;
									}
								}
								
								if (dz > 0) {
									if (p01 == 0) {
										kt = k0 + 2;
									}
									else {
										kt = k0 + 1;
									}
								}
								else if (dz < 0) {
									if (p01 == 0) {
										kt = k0 - 1;
									}
									else {
										kt = k0 - 2;
									}
								}
								
								if (hide(it, kt, tx2, ty2, tz2) == true) {
									if (rc == 0) {
										rPol = 0;
									}
									else {
										rPol = (int)(-(ra * gx + rb * gy + rd) / rc);
									}
									if (gc == 0) {
										gPol = 0;
									}
									else {
										gPol = (int)(-(ga * gx + gb * gy + gd) / gc);
									}
									if (bc == 0) {
										bPol = 0;
									}
									else {
										bPol = (int)(-(ba * gx + bb * gy + bd) / bc);
									}
									Intens = Ib + Ia;
								}
								else {
									if (rc == 0) {
										rPol = 0;
									}
									else {
										rPol = (int)(-(ra * gx + rb * gy + rd) / rc);
									}
									if (gc == 0) {
										gPol = 0;
									}
									else {
										gPol = (int)(-(ga * gx + gb * gy + gd) / gc);
									}
									if (bc == 0) {
										bPol = 0;
									}
									else {
										bPol = (int)(-(ba * gx + bb * gy + bd) / bc);
									}
									a = Xfrom - tx2;
									b = Yfrom - ty2;
									c = Zfrom - tz2;
									d = Math.sqrt(a * a + b * b + c * c);
									a = a / d; b = b / d; c = c / d;
									Cb = a * a2 + b * b2 + c * c2;
									Intens = Ib + Ia + IpKd * Ca + Ks * power(Cb, KsN);
								}
							}
							Col = (int)(Intens / IntensMax * Idelta) + Imin;
							ir = rPol * Col / Imax;
							if (ir > Imax) {
								ir = Imax;
							}
							else if (ir < Imin) {
								ir = Imin;
							}
							ig = gPol * Col / Imax;
							if (ig > Imax) {
								ig = Imax;
							}
							else if (ig < Imin) {
								ig = Imin;
							}
							ib = bPol * Col / Imax;
							if (ib > Imax) {
								ib = Imax;
							}
							else if (ib < Imin) {
								ib = Imin;
							}
							if (Col > Imax) {
								Col = Imax;
							}
							
							if (grayScale) {
								ScreenGraph.setColor(new Color(Col, Col, Col));
							}
							else {
								ScreenGraph.setColor(new Color(ir, ig, ib));
							}
							synchronized(this) {
								ScreenGraph.fillRect(gx, gy, 1, 1);
							}
						}
					}
				}
			}
		}
	}
	
	void drawBox(double x0, double x1, double y0, double y1, double z0, double z1)
	{
		double[] bx = new double[8];
		double[] by = new double[8];
		double[] bz = new double[8];
		bx[0] = x0; by[0] = y0; bz[0] = z0;
		bx[1] = x0; by[1] = y0; bz[1] = z1;
		bx[2] = x0; by[2] = y1; bz[2] = z1;
		bx[3] = x0; by[3] = y1; bz[3] = z0;
		bx[4] = x1; by[4] = y0; bz[4] = z0;
		bx[5] = x1; by[5] = y0; bz[5] = z1;
		bx[6] = x1; by[6] = y1; bz[6] = z1;
		bx[7] = x1; by[7] = y1; bz[7] = z0;
		for (int i = 0; i <= 7; i++) {
	    	getScreenCoord(bx[i], by[i], bz[i]);
			bx[i] = Sx; by[i] = Sy;
		}
		ScreenGraph.setColor(Color.cyan);
		synchronized(this) {
			ScreenGraph.drawLine((int)bx[0], (int)by[0], (int)bx[1], (int)by[1]);
			ScreenGraph.drawLine((int)bx[1], (int)by[1], (int)bx[2], (int)by[2]);
			ScreenGraph.drawLine((int)bx[2], (int)by[2], (int)bx[3], (int)by[3]);
			ScreenGraph.drawLine((int)bx[3], (int)by[3], (int)bx[0], (int)by[0]);
			ScreenGraph.drawLine((int)bx[4], (int)by[4], (int)bx[5], (int)by[5]);
			ScreenGraph.drawLine((int)bx[5], (int)by[5], (int)bx[6], (int)by[6]);
			ScreenGraph.drawLine((int)bx[6], (int)by[6], (int)bx[7], (int)by[7]);
			ScreenGraph.drawLine((int)bx[7], (int)by[7], (int)bx[4], (int)by[4]);
			ScreenGraph.drawLine((int)bx[0], (int)by[0], (int)bx[4], (int)by[4]);
			ScreenGraph.drawLine((int)bx[1], (int)by[1], (int)bx[5], (int)by[5]);
			ScreenGraph.drawLine((int)bx[2], (int)by[2], (int)bx[6], (int)by[6]);
			ScreenGraph.drawLine((int)bx[3], (int)by[3], (int)bx[7], (int)by[7]);
		}
		repaint();
	}
	
	void drawTriangle(double Intens)
	{
		int ir, ig, ib;
		Col = (int)(Intens / IntensMax * Idelta) + Imin;
		ir = rPol * Col / Imax;
		if (ir > Imax) {
			ir = Imax;
		}
		ig = gPol * Col / Imax;
		if (ig > Imax) {
			ig = Imax;
		}
		ib = bPol * Col / Imax;
		if (ib > Imax) {
			ib = Imax;
		}
		if (Col > Imax) {
			Col = Imax;
		}
		
		if (grayScale) {
			ScreenGraph.setColor(new Color(Col, Col, Col));
		}
		else {
			ScreenGraph.setColor(new Color(ir, ig, ib));
		}
		if (Simple) {
			synchronized(this) {
				ScreenGraph.fillPolygon(polx, poly, 3);
			}
		}
		else {
			for (int gy = Ymin; gy <= Ymax; gy++) {
				if (gy >= GYmin && gy <= GYmax) {
					synchronized(this) {
						ScreenGraph.drawLine(Xmin[gy], gy, Xmax[gy], gy);
					}
				}
			}
		}
	}
	
//	void drawTrianglePset(double Intens)
//	{
//		Col = (int)(Intens / IntensMax * Idelta) + Imin;
//		if (Col > Imax) {
//			Col = Imax;
//		}
//		for (int gy = Ymin; gy <= Ymax; gy++) {
//			if (gy >= GYmin && gy <= GYmax) {
//				for (int gx = Xmin[gy]; gx <= Xmax[gy]; gx++) {
//					if (gx >= GXmin && gx <= GXmax) {
//						ScreenGraph.setColor(new Color(Col, Col, Col));
//						synchronized(this) {
//							ScreenGraph.fillRect(gx, gy, 1, 1);
//						}
//					}
//				}
//			}
//		}
//	}
	
	void getCoord()
	{
		for (int k = 0; k <= wz - 1; k++) {
			for (int i = 0; i <= wx - 1; i++) {
				getScreenCoord((double)i, y[i][k], (double)k);
				gx[i][k] = Sx; gy[i][k] = Sy;
				ScreenGraph.setColor(Color.blue);
				synchronized(this) {
					ScreenGraph.fillRect((int)Sx, (int)Sy, 1, 1);
				}
			}
			repaint();
			
			/* 中断か？ */
			if (!running) {
				return;
			}
		}
	}
	
	void getLine(int x0, int y0, int x1, int y1)
	{
		int xs, ys, xe, ye, h, v, ystep, small, big, c;
		
		if (x0 < x1) {
			xs = x0; ys = y0;
			xe = x1; ye = y1;
		}
		else {
			xs = x1; ys = y1;
			xe = x0; ye = y0;
	    }
		h = xe - xs;
		if (ys < ye) {
			v = ye - ys; ystep = 1;
		}
		else {
			v = ys - ye; ystep = -1;
		}
		if (v < h) {
			small = v; big = h;
		}
		else {
			small = h; big = v;
		}
		int t = big / 2;
		for (c = big; c >= 0; c--) {
			if (ys >= GYmin && ys <= GYmax) {
				if (xs < Xmin[ys]) {
					Xmin[ys] = xs;
				}
				if (xs > Xmax[ys]) {
					Xmax[ys] = xs;
				}
			}
			t += small;
			if (big <= t) {
				t -= big;
				if (v < h) {
					ys += ystep;
				}
				else {
					xs++;
				}
			}
			if (v < h) {
				xs++;
			}
			else {
				ys += ystep;
			}
		}
	}
	
	void getMatrix()
	{
		double Xsub, Ysub, Zsub;
		double CosA, SinA, CosB, SinB;
		Xsub = Xfrom - Xat; Ysub = Yfrom - Yat; Zsub = Zfrom - Zat;
		double a = Math.sqrt(Xsub * Xsub + Zsub * Zsub);
		double b = Math.sqrt(Xsub * Xsub + Ysub * Ysub + Zsub * Zsub);
		CosA = Zsub / a; SinA = -Xsub / a;
		CosB = a / b; SinB = Ysub / b;
		a11 = CosA;
		a12 = SinA * SinB;
		a13 = Kp / Hp * SinA * CosB;
		a14 = a13;
		a21 = 0.0;
		a22 = CosB;
		a23 = -Kp / Hp * SinB;
		a24 = a23;
		a31 = SinA;
		a32 = -CosA * SinB;
		a33 = -Kp / Hp * CosA * CosB;
		a34 = a33;
		a41 = -Xfrom * CosA - Zfrom * SinA;
		a42 = -Xfrom * SinA * SinB - Yfrom * CosB + Zfrom * CosA * SinB;
		a43 = Kp / Hp * (-Xfrom * SinA * CosB + Yfrom * SinB + Zfrom * CosA * CosB) - Kp;
		a44 = a43 + Kp;
	}
	
	void getPlaneVect(double x0, double y0, double z0, double x1, double y1, double z1, double x2, double y2, double z2)
	{
		double tmp;
		Pa = y0 * (z1 - z2) + y1 * (z2 - z0) + y2 * (z0 - z1);
		Pb = z0 * (x1 - x2) + z1 * (x2 - x0) + z2 * (x0 - x1);
		Pc = x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1);
		Pd = x0 * (y2 * z1 - y1 * z2) + y0 * (z2 * x1 - z1 * x2) + z0 * (x2 * y1 - x1 * y2);
		tmp = Math.sqrt(Pa * Pa + Pb * Pb + Pc * Pc);
		Pa = Pa / tmp; Pb = Pb / tmp; Pc = Pc / tmp; Pd = Pd / tmp;
	}
	
	void getPolyVect()
	{
		for (int k = 1; k <= wz - 1; k++) {
			double sk = (double)k;
			int k1 = k - 1;
			double sk1 = (double)k1;
			for (int i = 1; i <= wx - 1; i++) {
				double si = (double)i;
				int i1 = i - 1;
				double si1 = (double)i1;
				getPlaneVect(si1, y[i1][k1], sk1, si1, y[i1][k], sk, si, y[i][k1], sk1);
				px0[i][k] = Pa; py0[i][k] = Pb; pz0[i][k] = Pc;
				getPlaneVect(si, y[i][k], sk, si, y[i][k1], sk1, si1, y[i1][k], sk);
				px1[i][k] = Pa; py1[i][k] = Pb; pz1[i][k] = Pc;
				ScreenGraph.setColor(Color.red);
				synchronized(this) {
					ScreenGraph.fillRect((int)gx[i][k], (int)gy[i][k], 1, 1);
				}
			}
			repaint();
			
			/* 中断か？ */
			if (!running) {
				return;
			}
		}
	}
	
	void getScreenCoord(double x, double y, double z)
	{
		double wr = a13 * x + a23 * y + a33 * z + a43 + Kp;
		double xx = (a11 * x + a21 * y + a31 * z + a41) / wr;
		double yy = (a12 * x + a22 * y + a32 * z + a42) / wr;
		double zz = (a13 * x + a23 * y + a33 * z + a43) / wr;
		Sx = OX + xx * Win; Sy = OY - yy * Win;
		Sz = zz;
	}
	
	void getTopVect()
	{
		double a, b, c, tmp;
		
		for (int k = 1; k <= wz - 2; k++) {
			int k1 = k + 1;
			for (int i = 1; i <= wx - 2; i++) {
				int i1 = i + 1;
				a = px1[i][k] + px0[i1][k] + px1[i1][k] + px0[i][k1] + px1[i][k1] + px0[i1][k1];
				b = py1[i][k] + py0[i1][k] + py1[i1][k] + py0[i][k1] + py1[i][k1] + py0[i1][k1];
				c = pz1[i][k] + pz0[i1][k] + pz1[i1][k] + pz0[i][k1] + pz1[i][k1] + pz0[i1][k1];
				tmp = Math.sqrt(a * a + b * b + c * c);
				tx[i][k] = a / tmp; ty[i][k] = b / tmp; tz[i][k] = c / tmp;
				ScreenGraph.setColor(Color.magenta);
				synchronized(this) {
					ScreenGraph.fillRect((int)gx[i][k], (int)gy[i][k], 1, 1);
				}
			}
			repaint();
			
			/* 中断か？ */
			if (!running) {
				return;
			}
		}
	}
	
	boolean hide(int i0, int k0, double tx, double ty, double tz)
	{
		double lx, ly, lz, t;
		double yt;
		int iend, kend;
		
		if (dx > 0) {
			iend = (int)(wx - 3);
		}
		else {
			iend = 1;
		}
		if (dx > 0) {
			for (int i = i0; i <= iend; i ++) {
				t = (i - tx) / LtX;
				ly = ty + t * LtY;
				lz = tz + t * LtZ;
				int k = (int)lz; int k1 = k + 1;
				if (k > wz - 3 || k < 1 || ly > height) {
					break;
				}
				t = lz - k;
				yt = (1 - t) * y[i][k] + t * y[i][k1];
				if (yt > ly) {
					return (true);
				}
			}
		}
		else if (dx < 0) {
			for (int i = i0; i >= iend; i--) {
				t = (i - tx) / LtX;
				ly = ty + t * LtY;
				lz = tz + t * LtZ;
				int k = (int)lz; int k1 = k + 1;
				if (k > wz - 3 || k < 1 || ly > height) {
					break;
				}
				t = lz - k;
				yt = (1 - t) * y[i][k] + t * y[i][k1];
				if (yt > ly) {
					return (true);
				}
			}
		}
		
		if (dz > 0) {
			kend = (int)(wz - 3);
		}
		else {
			kend = 1;
		}
		if (dz > 0) {
			for (int k = k0; k <= kend; k++) {
				t = (k - tz) / LtZ;
				ly = ty + t * LtY;
				lx = tx + t * LtX;
				int i = (int)lx; int i1 = i + 1;
				if (i > wx - 3 || i < 1 || ly > height) {
					break;
				}
				t = lx - i;
				yt = (1 - t) * y[i][k] + t * y[i1][k];
				if (yt > ly) {
					return (true);
				}
			}
		}
		else if (dz < 0) {
			for (int k = k0; k >= kend; k--) {
				t = (k - tz) / LtZ;
				ly = ty + t * LtY;
				lx = tx + t * LtX;
				int i = (int)lx; int i1 = i + 1;
				if (i > wx - 3 || i < 1 || ly > height) {
					break;
				}
				t = lx - i;
				yt = (1 - t) * y[i][k] + t * y[i1][k];
				if (yt > ly) {
					return (true);
				}
			}
		}
		
		return (false);
	}
	
	double power(double Pbase, double Expornent)
	{
		int Iexpo = (int)Expornent;
		if (Expornent == (double)Iexpo && Iexpo >= 0) {
			double Pwork = 1.0;
			for (int i = 1; i <= Iexpo; i++) {
				Pwork = Pwork * Pbase;
			}
			return (Pwork);
		}
		else {
			return (Math.pow(Pbase, Expornent));
		}
	}
	
	void readData()
	{
		for (int j = 0; j < wy; j++) {
			for (int i = 0; i < wx; i++) {
				y[i][j] = pa[i][j];
				for (int k = 1; k < multiple; k++) {
					y[i][j] = y[i][j] * y[i][j] / height;
				}
			}
		}
	}
	
	void triangle(int x0, int y0, int x1, int y1, int x2, int y2)
	{
		polx[0] = x0; poly[0] = y0;
		polx[1] = x1; poly[1] = y1;
		polx[2] = x2; poly[2] = y2;
		if (Simple == true) {
			return;
		}
		
		if (y0 > y1) {
			if (y0 > y2) {
				Ymax = y0;
			}
			else {
				Ymax = y2;
			}
			if (y1 > y2) {
				Ymin = y2;
			}
			else {
				Ymin = y1;
			}
		}
		else {
			if (y1 > y2) {
				Ymax = y1;
			}
			else {
				Ymax = y2;
			}
			if (y0 > y2) {
				Ymin = y2;
			}
			else {
				Ymin = y0;
			}
		}
		
		for (int y = Ymin; y <= Ymax; y++) {
			if (y >= GYmin && y <= GYmax) {
				Xmin[y] = GXmax;
				Xmax[y] = GXmin;
			}
		}
		
		getLine(x0, y0, x1, y1);
		getLine(x1, y1, x2, y2);
		getLine(x2, y2, x0, y0);
	}
	
	/* 計算 */
	public void makeFigure3D() {
		int pass;
		
		/* 表示領域クリア */
		ScreenGraph.setColor(Color.lightGray);
		synchronized(this) {
			ScreenGraph.fillRect(GXmin, GYmin, GXmax - GXmin, GYmax - GYmin);
		}
		drawBox(0.0, (double)wx, 0.0, height, 0.0, (double)wz);
		repaint();
		
		if (Simple == true) {
			pass = 4;
		}
		else {
			pass = 5;
		}
		
		showStatus("1/" + pass + " ReadData");
		readData();
		
		/* 中断か？ */
		if (!running) {
			return;
		}
		
		showStatus("2/" + pass + " GetCoord");
		getCoord();
		
		/* 中断か？ */
		if (!running) {
			return;
		}
		
		showStatus("3/" + pass +" GetPolyVect");
		getPolyVect();
		
		/* 中断か？ */
		if (!running) {
			return;
		}
		
		if (!Simple) {
			showStatus("4/" + pass +" GetTopVect");
			getTopVect();
		}
		
		/* 中断か？ */
		if (!running) {
			return;
		}
		
		/* 表示領域クリア */
		ScreenGraph.setColor(Color.black);
		synchronized(this) {
			ScreenGraph.fillRect(GXmin, GYmin, GXmax - GXmin, GYmax - GYmin);
		}
		
		showStatus(pass + "/" + pass +" DisplayMap");
		displayMap();
		
		/* 中断か？ */
		if (!running) {
			return;
		}
		
		showStatus("done.");
	}
	
	double power2(int expo)
	{
		double Pwork = 1.0;
		for (int i = 1; i <= expo; i++) {
			Pwork = Pwork * 2.0;
		}
		return (Pwork);
	}
	
	void makeFigure2D()
	{
		double dx, dy, u, v, x, y, x2, y2;
		int i, j, k, l;
		double p, pmax, delta;
		
		/* 表示領域クリア */
		ScreenGraph.setColor(Color.orange);
		synchronized(this) {
			ScreenGraph.fillRect(0, 0, wx, wy);
		}
		repaint();
		
		/* パラメータ範囲取得 */
		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);
		
		/* 計算ループ */
		pmax = 0.0;
		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;
				p = 0.0;
				for (k = 1; k <= n; k++) {
					x2 = x * x;
					y2 = y * y;
					if (x2 + y2 > 1000.0) {
						l = k;
						p = Math.log(x2 + y2) / power2(k);
						break;
					}
					y = 2.0 * x * y + v;
					x = x2 - y2 + u;
				}
				pa[i][j] = p;
				if (p > pmax) {
					pmax = p;
				}
				
				/* 中断か？ */
				if (!running) {
					return;
				}
				
				/* 色設定・点表示 */
				if (l > 0) {
					ScreenGraph.setColor(col[l % col.length]);
					lcol[i][j] = col[l % col.length];
				}
				else {
					ScreenGraph.setColor(Color.black);
					lcol[i][j] = Color.black;
				}
				synchronized(this) {
					ScreenGraph.fillRect(i, j, 1, 1);
				}
			}
			repaint();
		}
		delta = height / pmax;
		for (j = 0; j < wy; j++) {
			for (i = 0; i < wx; i++) {
				pa[i][j] = height - pa[i][j] * delta;
			}
		}
	}
}

