
import render.*;

public class tumbler2 extends RenderApplet
{
   Material wire, steel, plastic, clear, shade, darkTile, lightTile;
   Geometry g, body, floor, vehicle, rotor, balls, shadow;
   double r = .5, R = Math.sqrt(3) + .7*r;

   public void initialize() {
      setBgColor(.3,.3,.6);
      setFL(15);
      addLight(3,1,1, 1,1,1);
      addLight(1,3,1, 1,1,1);
      addLight(-1,.2,-.5, 1,1,1);

      wire    = new Material().setColor(1,0,0, 1,1,1,15);
      steel   = new Material().setColor(1,1,1, 1,1,1,20);
      plastic = new Material().setColor(0,.6,.6, 1,1,1,20);
      clear   = new Material().setColor(.15,.15,.2, 1,1,1,40).setTransparency(.55);
      shade   = new Material().setColor(0,0,0).setTransparency(.7);
      darkTile  = new Material().setColor(.22,.2,.15);
      lightTile = new Material().setColor(.44,.4,.3);

      push();
         rotateX(Math.PI/12);
         transform(world);
      pop();

      for (int i = 0 ; i < 8 ; i++)
      for (int j = 0 ; j < 8 ; j++) {
	  push();
	     translate(2*i - 8, -R, 2*j-8);
	     scale(1,.001,1);
	     transform(world.add().cube().setMaterial((i+j) %2 == 0 ? darkTile : lightTile));
	  pop();
      }

      vehicle = world.add();

      shadow = vehicle.add();
      body = vehicle.add();

      rotor = body.add();
      Geometry block = rotor.add().cube().setMaterial(plastic);
      coil(0,0,-1, Math.PI/2, 0);
      coil(0,0, 1, Math.PI/2, 0);
      coil(0,-1,0, 0, 0);
      coil(0, 1,0, 0, 0);
      coil(-1,0,0, 0, Math.PI/2);
      coil( 1,0,0, 0, Math.PI/2);

      push();
         Geometry casing = body.add();
	 casing.ball(30).setMaterial(clear);
	 scale(R,R,R);
	 transform(casing);
      pop();

      push();
	 shadow.ball(10).setMaterial(shade);
	 translate(-.4*R,.02-R,-.4*R);
	 scale(1.2*R,.005,1.2*R);
	 transform(shadow);
      pop();
   }

   double oldTime = 0;
   double gravity = .3;

   public void animate(double time) {

      double vx = 2 * Math.cos(.5 * time);
      double vz = 2 * Math.cos(     time);

      double dx = (time-oldTime) * vx;
      double dz = (time-oldTime) * vz;

      // MOVE THE VEHICLE

      Matrix MV = vehicle.getMatrix();
      MV.set(0,3, MV.get(0,3) + dx);
      MV.set(2,3, MV.get(2,3) + dz);

      // TUMBLE

      Matrix MB = body.getMatrix();

      double X = vz * MB.get(0,0) - vx * MB.get(2,0);
      double Y = vz * MB.get(0,1) - vx * MB.get(2,1);
      double Z = vz * MB.get(0,2) - vx * MB.get(2,2);
      MB.rotate(Math.sqrt(dx*dx+dz*dz), X, Y, Z);

      // SHIFT THE ROTOR INSIDE THE VEHICLE

      push();
         double x = .4 * vx * MB.get(0,0) + .4 * vz * MB.get(2,0) - gravity * MB.get(1,0);
         double y = .4 * vx * MB.get(0,1) + .4 * vz * MB.get(2,1) - gravity * MB.get(1,1);
         double z = .4 * vx * MB.get(0,2) + .4 * vz * MB.get(2,2) - gravity * MB.get(1,2);
         double r = Math.sqrt(x*x + y*y + z*z);
         if (r > .4) {
            x *= .4 / r;
            y *= .4 / r;
            z *= .4 / r;
         }
         translate(x, y, z);
	 transform(rotor);
      pop();

      oldTime = time;
   }

   void coil(double x,double y,double z, double theta, double phi) {
      push();
         translate(x,y,z);
	 rotateY(theta);
	 rotateX(phi);
	 push();
            g = rotor.add();
            g.cylinder(20).setMaterial(steel);
            scale(.23,.23,1.1);
            transform(g);
	 pop();
	 push();
            g = rotor.add();
            g.pill(20,.92,.08).setMaterial(wire);
            scale(.45,.45,.7);
            transform(g);
	 pop();
      pop();
   }


   void ball(double x,double y,double z) {
      push();
         translate(x,y,z);
         g = balls.add();
	 scale(r,r,r);
	 transform(g);
      pop();
   }

   final static double LOG_HALF = Math.log(.5);

   static double gain(double a, double b) {
      if (a<.001)
         return 0.;
      if (a>.999)
         return 1;

      b = b<.001 ? .001 : b>.999 ? .999 : b;
      double p = Math.log(1. - b) / LOG_HALF;
      return (a < .5) ? Math.pow(2 * a, p) / 2
                      : 1 - Math.pow(2 * (1. - a), p) / 2;
   }

   void rotate(Matrix MB, double theta, double X, double Y, double Z) {
      double XY = Math.sqrt(X*X + Y*Y);
      double Y_X = Math.atan2(Y,X);
      double XY_Z = Math.atan2(XY,Z);
      MB.rotateZ(Y_X);
      MB.rotateY(XY_Z);
      MB.rotateZ(theta);
      MB.rotateY(-XY_Z);
      MB.rotateZ(-Y_X);
   }
/*
   void rotate(double theta, double X, double Y, double Z) {
      double Y_X = Math.atan2(Y,X);
      double XY_Z = Math.atan2(Math.sqrt(X*X + Y*Y),Z);
      rotateZ(Y_X);
      rotateY(XY_Z);
      rotateZ(theta);
      rotateY(-XY_Z);
      rotateZ(-Y_X);
   }
*/
}


