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

public class FractalTree2 extends Applet
{
  // The change in angle is based on this value.
  public static final double change = Math.PI / 8; 
  Image buf;
  Graphics gr;
  Graphics gs;

  public int getWidth() // getWidth is defined for java 1.2, but not 1.1
  {
    return getSize().width;
  }

  public int getHeight() // same as getWidth.
  {
    return getSize().height;
  }

  public void init()
  {
    buf = createImage(getWidth(), getHeight());
    gr = buf.getGraphics();
  }

  void setColor(Color c)
  {
    gr.setColor(c);
    gs.setColor(c);
  }

  void fillRect(int x, int y, int width, int height)
  {
    gr.fillRect(x, y, width, height);
    gs.fillRect(x, y, width, height);
  }

  void drawLine(int x, int y, int x2, int y2)
  {
    gr.drawLine(x, y, x2, y2);
  gs.drawLine(x, y, x2, y2);
  }

  public void start()  // Initialize the new tree's random values.
  {
    gs = getGraphics();
    setColor(Color.blue);
    fillRect(0, 0, buf.getWidth(null), buf.getHeight(null));
    Thread t = new Thread() {
      public void run()
      {
        drawBranch(0, buf.getWidth(null)/2, 0, 0, 
             0.0, 0.0, buf.getHeight(null)/5);
        gs.drawImage(buf, 0, 0, null);
      }
    };
    t.start();
  }

  public void paint(Graphics g) // Paint the random tree.
  {  
    g.drawImage(buf, 0, 0, null);
  }

  double absRandom()
  {
    return change * (Math.random() + 1.5) / 2.0;
  }
  double random()
  {
    if (Math.random() < .5)
      return - absRandom();
    return absRandom();
  }

  void drawBranch(
    int x, int y, int z, int depth,
    double theta, double phi, double length)
  {
    if (depth >= 16)
      return;
    int x2 = x + (int)(Math.sin(theta) * Math.sin(phi) * length);
    int y2 = y + (int)(Math.cos(theta) * Math.sin(phi) * length);
    int z2 = z + (int)(Math.cos(phi) * length);
    double c1 = random();
    double c2 = absRandom();
    double c3 = random();
    double c4 = absRandom();
    double c5 = Math.PI / 2.0 + random();
    double c6 = absRandom();
    double c7 = Math.PI / 2.0 + random();
    double c8 = absRandom();
    setColor(new Color(255 - depth * 16, depth * 16, 0));
    int sx1 = (y * 1000) / (x + 1000);
    int sy1 = (z * 1000) / (x + 1000);
    int sx2 = (y2 * 1000) / (x2 + 1000);
    int sy2 = (z2 * 1000) / (x2 + 1000);
    drawLine(sx1, buf.getHeight(null) - sy1, sx2, buf.getHeight(null) - sy2);
    drawBranch(x2, y2, z2, depth + 1, theta + c1, phi + c2, length * .8);
    drawBranch(x2, y2, z2, depth + 1, theta + c3, phi - c4, length * .8);
    if (depth <= 1)
    {
      drawBranch(x2, y2, z2, depth + 1, theta + c7, phi - c8, length * .8);
      drawBranch(x2, y2, z2, depth + 1, theta - c5, phi + c6, length * .8);
    }
    
  }
}
