/*
 * $B%S%8%e%"%k>pJs=hM}O@(B
 *    2(1) $B#3<!(BBezier$B6JLL$r:n@.$7(B,$B#43Q7A$KJ,3d$9$k!#(B
 *  Programmed by Yoshizumi TANAKA. 1998/7
 *
 * *Button*
 *       Division $B!D(B $BJ,3d?t$r#2G\$K$7$^$9!#(B
 *     Initialize $B!D(B $B=i4|>uBV!J(B2$B!v(B2$BJ,3d!K$KLa$7$^$9!#(B
 *
 * *Mouse*
 *  $B6uGr$r$D$+$s$G%I%i%C%0$9$k$H2sE>$7$^$9!#(B
 */


import java.applet.*;
import java.awt.*;

public class Bezier extends Applet {
    final int N = 4;
    final int M = 8;

    Dimension appsize;
    int mouseX, mouseY;
    Button b1, b2;
    Obj obj[] = new Obj[M];
    double Data[][][][] =
        {{{{0.000, 0.000, 1.000}, {0.552, 0.000, 1.000}, {1.000, 0.000, 0.552}, {1.000, 0.000, 0.000},},
	  {{0.000, 0.000, 1.000}, {0.552, 0.305, 1.000}, {1.000, 0.552, 0.552}, {1.000, 0.552, 0.000},},
	  {{0.000, 0.000, 1.000}, {0.305, 0.552, 1.000}, {0.552, 1.000, 0.552}, {0.552, 1.000, 0.000},},
	  {{0.000, 0.000, 1.000}, {0.000, 0.552, 1.000}, {0.000, 1.000, 0.552}, {0.000, 1.000, 0.000},},},
	 {{{-0.000, 0.000, 1.000}, {-0.000, 0.552, 1.000}, {-0.000, 1.000, 0.552}, {-0.000, 1.000, 0.000},},
	  {{-0.000, 0.000, 1.000}, {-0.305, 0.552, 1.000}, {-0.552, 1.000, 0.552}, {-0.552, 1.000, 0.000},},
	  {{-0.000, 0.000, 1.000}, {-0.552, 0.305, 1.000}, {-1.000, 0.552, 0.552}, {-1.000, 0.552, 0.000},},
	  {{-0.000, 0.000, 1.000}, {-0.552, 0.000, 1.000}, {-1.000, 0.000, 0.552}, {-1.000, 0.000, 0.000},},},
	 {{{-0.000, -0.000, 1.000}, {-0.552, -0.000, 1.000}, {-1.000, -0.000, 0.552}, {-1.000, -0.000, 0.000},},
	  {{-0.000, -0.000, 1.000}, {-0.552, -0.305, 1.000}, {-1.000, -0.552, 0.552}, {-1.000, -0.552, 0.000},},
	  {{-0.000, -0.000, 1.000}, {-0.305, -0.552, 1.000}, {-0.552, -1.000, 0.552}, {-0.552, -1.000, 0.000},},
	  {{-0.000, -0.000, 1.000}, {-0.000, -0.552, 1.000}, {-0.000, -1.000, 0.552}, {-0.000, -1.000, 0.000},},},
	 {{{0.000, -0.000, 1.000}, {0.000, -0.552, 1.000}, {0.000, -1.000, 0.552}, {0.000, -1.000, 0.000},},
	  {{0.000, -0.000, 1.000}, {0.305, -0.552, 1.000}, {0.552, -1.000, 0.552}, {0.552, -1.000, 0.000},},
	  {{0.000, -0.000, 1.000}, {0.552, -0.305, 1.000}, {1.000, -0.552, 0.552}, {1.000, -0.552, 0.000},},
	  {{0.000, -0.000, 1.000}, {0.552, -0.000, 1.000}, {1.000, -0.000, 0.552}, {1.000, -0.000, 0.000},},},
	 {{{0.000, 0.000, -1.000}, {0.000, 0.552, -1.000}, {0.000, 1.000, -0.552}, {0.000, 1.000, -0.000},},
	  {{0.000, 0.000, -1.000}, {0.305, 0.552, -1.000}, {0.552, 1.000, -0.552}, {0.552, 1.000, -0.000},},
	  {{0.000, 0.000, -1.000}, {0.552, 0.305, -1.000}, {1.000, 0.552, -0.552}, {1.000, 0.552, -0.000},},
	  {{0.000, 0.000, -1.000}, {0.552, 0.000, -1.000}, {1.000, 0.000, -0.552}, {1.000, 0.000, -0.000},},},
	 {{{-0.000, 0.000, -1.000}, {-0.552, 0.000, -1.000}, {-1.000, 0.000, -0.552}, {-1.000, 0.000, -0.000},},
	  {{-0.000, 0.000, -1.000}, {-0.552, 0.305, -1.000}, {-1.000, 0.552, -0.552}, {-1.000, 0.552, -0.000},},
	  {{-0.000, 0.000, -1.000}, {-0.305, 0.552, -1.000}, {-0.552, 1.000, -0.552}, {-0.552, 1.000, -0.000},},
	  {{-0.000, 0.000, -1.000}, {-0.000, 0.552, -1.000}, {-0.000, 1.000, -0.552}, {-0.000, 1.000, -0.000},},},
	 {{{-0.000, -0.000, -1.000}, {-0.000, -0.552, -1.000}, {-0.000, -1.000, -0.552}, {-0.000, -1.000, -0.000},},
	  {{-0.000, -0.000, -1.000}, {-0.305, -0.552, -1.000}, {-0.552, -1.000, -0.552}, {-0.552, -1.000, -0.000},},
	  {{-0.000, -0.000, -1.000}, {-0.552, -0.305, -1.000}, {-1.000, -0.552, -0.552}, {-1.000, -0.552, -0.000},},
	  {{-0.000, -0.000, -1.000}, {-0.552, -0.000, -1.000}, {-1.000, -0.000, -0.552}, {-1.000, -0.000, -0.000},},},
	 {{{0.000, -0.000, -1.000}, {0.552, -0.000, -1.000}, {1.000, -0.000, -0.552}, {1.000, -0.000, -0.000},},
	  {{0.000, -0.000, -1.000}, {0.552, -0.305, -1.000}, {1.000, -0.552, -0.552}, {1.000, -0.552, -0.000},},
	  {{0.000, -0.000, -1.000}, {0.305, -0.552, -1.000}, {0.552, -1.000, -0.552}, {0.552, -1.000, -0.000},},
	  {{0.000, -0.000, -1.000}, {0.000, -0.552, -1.000}, {0.000, -1.000, -0.552}, {0.000, -1.000, -0.000},},},};

public void init() {
	setBackground(Color.black);
	setForeground(Color.white);
	appsize = size();
	
	add(b1 = new Button("Division"));
	add(b2 = new Button("Initialize"));

	for (int i = 0; i < M; i++) {
	    obj[i] = new Obj(Data[i]);
	    obj[i].Scale(100);
	    obj[i].setScreen(appsize.width/2, appsize.height/2);
	    obj[i].setViewPoint(500, 20, 30);
	    obj[i].setViewRefPoint(0, 0, 50);
	}
	resize(appsize);
    }
    public void paint(Graphics g) {
	for (int i = 0; i < M; i++) {
	    obj[i].calcRadian();
	    obj[i].Display(g);
	    showStatus("theta="+obj[i].theta+", phi="+obj[i].phi);
	}
    }
    public boolean action(Event e, Object o) {
	if (e.target == b1) { // division
	    for (int i = 0; i < M; i++) {
		obj[i].division();
	    }
	    repaint();
	return true;
	} else if (e.target == b2) { // initialize
	    for (int i = 0; i < M; i++) {
		obj[i].Init(1);
		obj[i].division();
	    }
	    repaint();
	    return true;
	} else {
	    return false;
	}
    }
    public boolean mouseDown(Event e, int x, int y) {
	mouseX = x;
	mouseY = y;
	return true;
    }
    public boolean mouseDrag(Event e, int x, int y) {
	for (int i = 0; i < M; i++) {
	    obj[i].theta -= x - mouseX;
	    obj[i].phi   += y - mouseY;
	}
	mouseX = x;
	mouseY = y;
	repaint();
	return true;
    }
    public boolean mouseUp(Event e, int x, int y) {
	return true;
    }
} // end of class Bezier

class Obj extends Canvas {
    final int n = 3;
    int N = 16;

    Point pos[][] = new Point[n+1][n+1];
    Point cel[][] = new Point[N+1][N+1];
    Point prj[][] = new Point[N+1][N+1];

    int x_sc_mid, y_sc_mid;
    int scale;
    double R, theta, phi;
    double ct, cp, st, sp;
    Point ViewRef = new Point();
    
    Obj(double in[][][]) {
	for (int i = 0; i <= n; i++)
	    for (int j = 0; j <= n; j++)
		pos[i][j] = new Point(in[i][j][0], in[i][j][1], in[i][j][2]);
	for (int i = 0; i <= N; i++)
	    for (int j = 0; j <= N; j++)
		cel[i][j] = new Point();
       	Init(1);
       	division();
    }
    public void Init(int i) {
	Init();
	N = i;
    }
    public void Init() {
	for (int i = 0; i <= N; i++) {
	    for (int j = 0; j <= N; j++) {
		cel[i][j].x = 0;
		cel[i][j].y = 0;
		cel[i][j].z = 0;
	    }
	}
    }
    public void Scale(int s) {
	scale = s;
	Scale();
    }
    public void Scale() {
	for (int i = 0; i <= n; i++) {
	    for (int j = 0; j <= n; j++) {
		pos[i][j].x *= scale;
		pos[i][j].y *= scale;
		pos[i][j].z *= scale;
	    }
	}
    }
    public void setViewRefPoint(double vrx, double vry, double vrz){
	ViewRef.x = vrx;
	ViewRef.y = vry;
	ViewRef.z = vrz;
    }
    public void setViewPoint(double r, double th, double ph) {
	final double RAD = Math.PI / 180.0;
	R = r;
	theta = th;
	phi = ph;
    }
    public void calcRadian() {
	final double RAD = Math.PI / 180.0;
	double th = theta * RAD;
	double ph =   phi * RAD;
	st = Math.sin(th);
	ct = Math.cos(th);
	sp = Math.sin(ph);
	cp = Math.cos(ph);
    }
    public void setScreen(int x, int y) {
	x_sc_mid = x;
	y_sc_mid = y;
    }
    public void division() {
	Init();
	if (N < 16) {
	    N *= 2;
	}
	for (int u = 0; u <= N; u++)
	    for (int v = 0; v <= N; v++)
		Burnstain(u, v);
    }
    void Burnstain(int u, int v) {
	Point w[][] = new Point[n+1][n+1];

	for (int i = 0; i <= n; i++) {
	    for (int j = 0; j <= n; j++) {
		w[i][j] = new Point(pos[i][j].x, pos[i][j].y, pos[i][j].z);
	    }
	}
	for (int i = 0; i <= n; i++) {
	    for (int k = 1; k <= n; k++) {
		for (int j = 0; j <= n-k; j++) {
		    w[i][j].x += (w[i][j+1].x -w[i][j].x) * v / N;
		    w[i][j].y += (w[i][j+1].y -w[i][j].y) * v / N;
		    w[i][j].z += (w[i][j+1].z -w[i][j].z) * v / N;
		}
	    }
	}
	for (int k = 1; k <=n; k++) {
	    for (int i = 0; i <= n-k; i++) {
		w[i][0].x += (w[i+1][0].x - w[i][0].x) * u / N;
		w[i][0].y += (w[i+1][0].y - w[i][0].y) * u / N;
		w[i][0].z += (w[i+1][0].z - w[i][0].z) * u / N;
	    }
	}
	cel[u][v].x = w[0][0].x;
	cel[u][v].y = w[0][0].y;
	cel[u][v].z = w[0][0].z;
    }
    Point trans(Point p) {
	Point q;
	double tmp, d_rate;
	
	q = new Point(p.x-ViewRef.x, p.y-ViewRef.y, p.z-ViewRef.z);
	
	tmp =   q.x * ct + q.y * st;
	q.x = - q.x * st + q.y * ct;
	q.y = - tmp * sp + q.z * cp;
	q.z =   tmp * cp + q.z * sp;
	
	d_rate = R / (R - q.z);
	q.x =   q.x * d_rate + x_sc_mid;
	q.y = - q.y * d_rate + y_sc_mid;
	
	return q;
    }
    public void Display(Graphics g) {
	for(int i = 0; i <= N; i++)
	    for(int j = 0; j <= N; j++)
		prj[i][j] = trans(cel[i][j]);
	
	for(int j = 0; j <= N; j++) {
	    for(int i = 0; i < N; i++)
		Line(g, i, j, i+1, j);
	    if (j < N)
		for(int i = 0; i <= N; i++)
		    Line(g, i, j, i, j+1);
	}
    }
    void Line(Graphics g, int i, int j, int k, int h) {
	g.drawLine((int) prj[i][j].x, (int) prj[i][j].y,
		   (int) prj[k][h].x, (int) prj[k][h].y);
    }
} // end of class Obj

class Point {
    double x, y, z;
    Point() {}
    Point(double init_x, double init_y, double init_z) {
	x = init_x;
	y = init_y;
	z = init_z;
    }
} // end of class Point
