// 3D random landscape,  Evgeny Demidov  19 Feb 2002
import java.awt.*;
import java.awt.event.*;
import java.util.StringTokenizer;
public class Random3D extends java.applet.Applet
 implements ItemListener, ActionListener, MouseListener, MouseMotionListener{
  int n = 32, n1, Rc = 4, h,w,h2,w2, mx0,my0,  xPol[],yPol[], iCol[][][];
  double Vp = 0, Vs, Vmin, Vmax,
   fiX = .8, fiY = .3, dfi = .01, scale = .8, m20,m21,m22;
  double vert[][][], vert1[][][], Norm[][][][], Norm1z[][][], M[];
  Image buffImage;     Graphics buffGraphics;
  Choice chRc, chL;    Label lbRc, lbL;    Button btNew;
  Color[][] col;
  boolean painted;

public void init(){
  w = getSize().width - 30;
  h = getSize().height;   w2 = w/2;  h2 = h/2;
  String s=getParameter("N");  if (s != null) n = Integer.parseInt(s);
  s=getParameter("Rc");  if (s != null) Rc = Integer.parseInt(s);
  xPol = new int[3];  yPol = new int[3];
  buffImage = createImage(w, h);   buffGraphics = buffImage.getGraphics();
  col = new Color[2][256];
  for (int i = 0; i < 256; i++){
   col[0][i] = new Color(0, 0, i);
   col[1][i] = new Color(0, i, 0);}
  s = getParameter("bgColor"); if (s != null){
  StringTokenizer st = new StringTokenizer(s);
   int red = Integer.parseInt(st.nextToken());
   int green = Integer.parseInt(st.nextToken());
   int blue = Integer.parseInt(st.nextToken());
   setBackground( new Color(red, green, blue));}
  else setBackground(new Color(255,255,255));
  lbL = new Label("L", Label.RIGHT);  add(lbL);
  chL = new Choice();
  for (int i = 0, r = 32; i < 4; i++){
    chL.addItem(Integer.toString(r));  r *= 2;}
  chL.select("" + n);  chL.addItemListener(this);  add(chL);
  lbRc = new Label("Rc", Label.RIGHT);  add(lbRc);
  chRc = new Choice();
  for (int i = 0, r = 1; i < 5; i++){
    chRc.addItem(Integer.toString(r));  r *= 2;}
  chRc.select("" + Rc);  chRc.addItemListener(this);  add(chRc);
  btNew = new Button("New");   btNew.addActionListener(this);
  add(btNew);
  addMouseListener(this);  addMouseMotionListener(this);
  landscape();
  make3D();
}

public void make3D(){
  vert = new double[n1][n1][3];  vert1 = new double[n1][n1][2];
  double dx = w/(double)n;
  for (int i = 0; i < n1; i++) 
   for (int j = 0; j < n1; j++){
    vert[i][j][0] = dx*i - w2;  vert[i][j][2] = dx*j - w2;
    double m = M[i+n1*j];
    if (m < Vp) m = Vp;
    vert[i][j][1] = w*m - w2/2;}
  Norm = new double[n1][n1][2][3];  Norm1z = new double[n1][n1][2];
  iCol = new int[n][n][2];
  for (int i = 0; i < n; i++)
   for (int j = 0; j < n; j++){
    double s =
      ((vert[i][j][1] + vert[i+1][j][1] + vert[i+1][j+1][1])/3 + w2/2)/w;
    if (s < Vp+.0001) iCol[i][j][0] = 0;
    else  iCol[i][j][0] = 1;
    s = ((vert[i][j][1] + vert[i][j+1][1] + vert[i+1][j+1][1])/3 + w2/2)/w;
    if (s < Vp+.0001) iCol[i][j][1] = 0;
    else  iCol[i][j][1] = 1;
    Norm[i][j][0][0] = vert[i][j][1] - vert[i+1][j][1];
    Norm[i][j][0][1] = dx;
    Norm[i][j][0][2] = vert[i+1][j][1] - vert[i+1][j+1][1];
    double mod = Math.sqrt(Norm[i][j][0][0]*Norm[i][j][0][0] + Norm[i][j][0][1]*
     Norm[i][j][0][1] + Norm[i][j][0][2]*Norm[i][j][0][2]) / 255.5;
    Norm[i][j][0][0] /= mod; Norm[i][j][0][1] /= mod; Norm[i][j][0][2] /= mod;
    Norm[i][j][1][0] = vert[i][j+1][1] - vert[i+1][j+1][1];
    Norm[i][j][1][1] = dx;
    Norm[i][j][1][2] = vert[i][j][1] - vert[i][j+1][1];
    mod = Math.sqrt(Norm[i][j][1][0]*Norm[i][j][1][0] + Norm[i][j][1][1]*
     Norm[i][j][1][1] + Norm[i][j][1][2]*Norm[i][j][1][2]) / 255.5;
    Norm[i][j][1][0] /= mod; Norm[i][j][1][1] /= mod; Norm[i][j][1][2] /= mod;}
  rotate();
}

public void landscape(){
  n1 = n+1;
  int nc=n, Max=n1*n1, ncn1;
  M = new double[Max];
  double f[][] = new double[n1+64][n1+64];
  for (int i = 0; i < n1+64; i++)
   for (int j = 0; j < n1+64; j++) f[i][j] = 2*Math.random() - 1;
//  f[100][100] = 1;
  int Lc = 2*Rc;
  double Rc2 = Rc*Rc;
  double G[][] = new double[32][32];
  G[0][0] = 1;
  for (int i = 1; i < Lc; i++){
   G[0][i] = Math.exp(-(i*i/Rc2));
   for (int j = 1; j <= i; j++) G[j][i] = G[0][i]*G[0][j];}
  Vs = 0;
  Vmin = 100; Vmax = -100;
  int t = 0, n33 = n1+32;
  for (int i = 32; i < n33; i++){
   for (int j = 32; j < n33; j++){
    double s = G[0][0]*f[i][j];
    for (int m = 1; m < Lc; m++){
     int im = i+m, i_m = i-m, jm = j+m, j_m = j-m;
     s += G[0][m]*(f[im][j]+f[i_m][j]+f[i][jm]+f[i][j_m]) +
          G[m][m]*(f[im][jm]+f[i_m][jm]+f[im][j_m]+f[i_m][j_m]);
     for (int n = 1; n < m; n++){
      int in = i+n, i_n = i-n, jn = j+n, j_n = j-n;
      s += G[n][m]*(f[im][jn]+f[i_m][jn]+f[im][j_n]+f[i_m][j_n] +
                    f[in][jm]+f[i_n][jm]+f[in][j_m]+f[i_n][j_m]);}}
    s *= 0.02;
    M[t++] = s;
    Vs += s;
    if (s > Vmax) Vmax = s;
    if (s < Vmin) Vmin = s;}}
    Vs /= Max;
}

public void destroy() {
  removeMouseListener(this);  removeMouseMotionListener(this);
}
public void actionPerformed(ActionEvent e){
  landscape();
  make3D();
  repaint();
}
public void itemStateChanged(ItemEvent e){
  Object src = e.getSource();
  if (src == chRc)
   Rc = Integer.parseInt(chRc.getSelectedItem());
  if (src == chL)
   n = Integer.parseInt(chL.getSelectedItem());
  landscape();
  make3D();
  repaint();
}
public void mouseClicked(MouseEvent e){}       // event handling
public void mousePressed(MouseEvent e) {
  mx0 = e.getX();  my0 = e.getY();
  e.consume();
}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e)  {}
public void mouseMoved(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {
  int x1 = e.getX();  int y1 = e.getY();
  if( e.getX() < w){
    if ( e.isShiftDown() )  scale *= Math.exp(-(y1 - my0)/(double)w);
    else   fiX += dfi*(y1 - my0);
    fiY += dfi*(x1 - mx0);   mx0 = x1;  my0 = y1;
    rotate();}
  else{
    Vp = Vmax - y1*(Vmax-Vmin)/h;
  make3D();}
  repaint();
  e.consume();
}
public void rotate(){
  double ct = Math.cos(fiX), cf = Math.cos(fiY),
         st = Math.sin(fiX), sf = Math.sin(fiY),
         m00 =  scale*cf,    m02 =  scale*sf,
         m10 = scale*st*sf, m11 =  scale*ct, m12 = -scale*st*cf;
         m20 = -ct*sf; m21 = st; m22 = ct*cf;
  for (int i = 0; i < n1; i++)
   for (int j = 0; j < n1; j++){
    vert1[i][j][0] = m00*vert[i][j][0] + m02*vert[i][j][2];
    vert1[i][j][1] = m10*vert[i][j][0] + m11*vert[i][j][1] + m12*vert[i][j][2];}
  for (int i = 0; i < n; i++)
   for (int j = 0; j < n; j++)
    for (int k = 0; k < 2; k++)
     Norm1z[i][j][k] = m20*Norm[i][j][k][0] + m21*Norm[i][j][k][1] +
      m22*Norm[i][j][k][2];
  painted = false;
}
public void paint(Graphics g) {
 if ( !painted ){
  buffGraphics.clearRect(0, 0, w, h);
  int ib=0, ie=n, sti=1,  jb=0, je=n, stj=1;
  if (m20 < 0){ ib = n; ie = -1; sti = -1;}
  if (m22 < 0){ jb = n; je = -1; stj = -1;}
  for (int i = ib; i != ie; i += sti)
   for (int j = jb; j != je; j += stj){
    if (Norm1z[i][j][0] > 0){
     xPol[0] = w2 + (int)vert1[i][j][0];
     xPol[1] = w2 + (int)vert1[i+1][j][0];
     xPol[2] = w2 + (int)vert1[i+1][j+1][0];
     yPol[0] = h2 - (int)vert1[i][j][1];
     yPol[1] = h2 - (int)vert1[i+1][j][1];
     yPol[2] = h2 - (int)vert1[i+1][j+1][1];
     buffGraphics.setColor(col[iCol[i][j][0]][(int)(Norm1z[i][j][0])]);
     buffGraphics.fillPolygon(xPol,yPol, 3);}
    if (Norm1z[i][j][1] > 0){
     xPol[0] = w2 + (int)vert1[i][j][0];
     xPol[1] = w2 + (int)vert1[i][j+1][0];
     xPol[2] = w2 + (int)vert1[i+1][j+1][0];
     yPol[0] = h2 - (int)vert1[i][j][1];
     yPol[1] = h2 - (int)vert1[i][j+1][1];
     yPol[2] = h2 - (int)vert1[i+1][j+1][1];
     buffGraphics.setColor(col[iCol[i][j][1]][(int)(Norm1z[i][j][1])]);
     buffGraphics.fillPolygon(xPol,yPol, 3);} }
  painted = true;}
 g.drawImage(buffImage, 0, 0, this);
 g.setColor(Color.blue);
 g.fillRect(w+10,0, 20,h);
 g.setColor(Color.white);
 int y = h - (int)(h*(Vs-Vmin)/(Vmax-Vmin));
 g.drawLine(w+10,y, w+30,y);
 g.setColor(Color.yellow);
 y = h - (int)(h*(Vp-Vmin)/(Vmax-Vmin));
 g.drawLine(w+10,y, w+30,y);
// showStatus( "n=" + n);
}
public void update(Graphics g){ paint(g); }

}
