//

import java.awt.*;
import java.util.*;

public class butterflies extends BufferedApplet
{
   int N = 8; // NUMBER OF BUTTERFLIES PER ROW

   Graphics g;
   int width = 0, height = 0;
   Color bgColor = new Color(0xff, 0x90, 0x00);
   Color shadowColor = bgColor.darker();
   Color fgColor[] = new Color[N*N];
   double oldTime, rate = .5, var = .5;
   double randomSeed[] = new double[N*N];
   Random R = new Random();

   double rnd() { return Math.abs(R.nextDouble()) % 1.0; } // RANDOM NUMBER BETWEEN ZERO AND ONE

   public void render(Graphics g) {
      this.g = g;
      if (width == 0) {
	 width = bounds().width;
	 height = bounds().height;
         oldTime = System.currentTimeMillis() / 1000.;

	 // PICK A RANDOM COLOR FOR EACH BUTTERFLY

         for (int i = 0 ; i < N ; i++)
         for (int j = 0 ; j < N ; j++) {
	    int n = N * i + j;
	    fgColor[n] = new Color((float)(.3+.6*rnd()),(float)(.2+.6*rnd()),(float)(.1+.6*rnd()));
            randomSeed[n] = rnd() - .5;
         }
      }

      // HOW MUCH TIME HAS GONE BY SINCE LAST FRAME?

      double newTime = System.currentTimeMillis() / 1000.;
      double deltaTime = newTime - oldTime;
      oldTime = newTime;

      // BLANK OUT THE BACKGROUND

      g.setColor(bgColor);
      g.fillRect(0,0, width, height);

      // DRAW ALL THE BUTTERFLIES

      for (int i = 0 ; i < N ; i++)
      for (int j = 0 ; j < N ; j++) {
	 int x = i * width  / N + width /N/2;
	 int y = j * height / N + height/N/2;

	 drawButterfly(deltaTime, i + N * j, x, y, width/(2*N-N/2), width/(2*N-N/2));
      }

      animating = true; // FORCE APPLET TO REDISPLAY EVERY FRAME
   }

   // DATA CONTAINING SHAPE OF ONE BUTTERFLY WING

   double X[] = {.0, .3,  .7 , 1., .9, .85, .6, .7, .4, .2, .0};
   double Y[] = {.8, .95, .98, .9, .8, .65, .4, .3, .0, .05, .2};

   int iX[] = new int[X.length];
   int iY[] = new int[Y.length];

   double theta[] = new double[N*N]; // TIME-VARYING FLAP POSITION FOR EACH BUTTERFLY

   public void drawButterfly(double deltaTime, int n, int x, int y, int w, int h) {

      // COMPUTE HOW MUCH TO PROGRESS THE FLAPPING MOTION FOR THIS BUTTERFLY

      theta[n] += rate * deltaTime * (20 + 30 * var * randomSeed[n]);

      // COMPUTE HORIZONTAL SCALE FACTOR DUE TO FLAPPING

      double s = .4 + .15 * Math.sin(theta[n]);

      // COMPUTE THE VERTICAL DISPLAY POSITIONS OF WING VERTICES

      for (int i = 0 ; i < X.length ; i++)
	 iY[i] = y + (int)(h * (.5 - Y[i]));

      // DRAW SHADOWS OF WINGS

      g.setColor(shadowColor);

      for (int i = 0 ; i < X.length ; i++)
	 iY[i] += w/5;

      for (int i = 0 ; i < X.length ; i++)
	 iX[i] = x + (int)(w * (.03 + s * X[i]) + (.1 + 2*X[i]*(1-s))*w/5);
      g.fillPolygon(iX, iY, X.length);

      for (int i = 0 ; i < X.length ; i++)
	 iX[i] = x + (int)(w * (-.03 - s * X[i]) + (.1 + 2*X[i]*(1-s))*w/5);
      g.fillPolygon(iX, iY, X.length);

      for (int i = 0 ; i < X.length ; i++)
	 iY[i] -= w/5;

      // DRAW BUTTERFLY'S BODY

      g.setColor(Color.black);
      g.fillOval(x-w/20, y-h/2+h/8, w/10, h);

      // DRAW ANTENNAE

      int a = (int)(.05 * w * Math.sin(.4 * theta[n]));
      g.drawLine(x, y-h/3, x-w/4 + a, y-h/2-h/4);
      g.drawLine(x, y-h/3, x+w/4 - a, y-h/2-h/4);

      // DRAW BUTTERFLY'S RIGHT WING

      for (int i = 0 ; i < X.length ; i++)
	 iX[i] = x + (int)(w * (.03 + s * X[i]));
      g.setColor(fgColor[n]);
      g.fillPolygon(iX, iY, X.length);
      g.setColor(Color.black);
      g.drawPolygon(iX, iY, X.length);

      // DRAW BUTTERFLY'S LEFT WING

      for (int i = 0 ; i < X.length ; i++)
	 iX[i] = x + (int)(w * (-.03 - s * X[i]));
      g.setColor(fgColor[n]);
      g.fillPolygon(iX, iY, X.length);
      g.setColor(Color.black);
      g.drawPolygon(iX, iY, X.length);

      // DRAW SPOTS ON WINGS

      g.setColor(bgColor);
      int sw = (int)(s * w);
      g.fillOval(x+w/33 +   sw/4  , y - h/3, sw/2,h/4);
      g.fillOval(x-w/33 - 3*sw/4+1, y - h/3, sw/2,h/4);
      g.fillOval(x+w/33 +   sw/5  , y      , sw/3,h/3);
      g.fillOval(x-w/33 - 3*sw/5+1, y      , sw/3,h/3);
   }

   // USER CALLBACK TO MONITOR MOUSE POSITION

   public boolean mouseMove(Event e, int x, int y) {
      rate = .1 + Math.max(0, Math.min(.9, .9 * x / width)); // MOUSE X CONTROLS FLAP RATE
      var  = Math.max(0, Math.min(1, 1. * y / height));      // MOUSE Y CONTROLS RANDOMNESS
      return true;
   }
}