import java.applet.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.text.*; import java.util.*; import java.util.zip.*; import netscape.javascript.*; import javax.comm.*; import javax.sound.midi.*; import javax.sound.midi.spi.*; import javax.sound.sampled.*; import javax.sound.sampled.spi.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.sax.*; import javax.xml.transform.stream.*; import org.xml.sax.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.*; public class MapForce2 extends BApplet {PSystem ps;
Arcball arcball;

int PARTICLES_Z = 5;
int PARTICLES_Y = 4;
int PARTICLES_X = 10;

float unitLength = 30;

int X_OFFSET = 200;
int Y_OFFSET = 200;
int Z_OFFSET = -70;

Particle[][][] particles;

SinCosField3D myMap;
MapForce mapForce;


void setup() {
  size(500, 500);
  ps = (PSystem)loadPlugin("PSystem");
  arcball = (Arcball)loadPlugin("Arcball");
  ps.setGravity(0);
  ps.defaultSpringRestLength = unitLength;
  ps.defaultSpringStrength = 0.5f;
  
  myMap = new SinCosField3D();
  
  mapForce = new MapForce(myMap);
  mapForce.outputMultiplier = 0.5f;
  ps.addForce(mapForce);
  
  makeGrid();
}

void loop() {
  translate(0, 0, -200);

  background(0xff99CCCC);
  translate(width/2, height/2);
  arcball.run();
  translate(-width/4, -height/2);
  
  stroke(255, 0, 0, 100);
  myMap.draw3DVectors(width, height, (width+height)/2, 10, 10, 10, 20);

  stroke(100, 100, 100, 100);
  ps.draw();
  
}


class SinCosField2D extends Map {

  SinCosField2D() {
    super(2, 2);
  }

  void map(float[] in, float[] out) {
    out[0] = 0.5f - ((sin(TWO_PI*(in[0] + (float)mouseX / width)) + 1) / 2) * ((cos(frame / 100.0f + TWO_PI*in[1]) + 1) / 2);
    out[1] = 0.5f - ((cos(frame / 77.0f + TWO_PI*in[0]) + 1) / 2) * ((sin(TWO_PI*(in[1] + (float)mouseY / height)) + 1) / 2);
    //out[0] = (0.5 - (float)mouseX / width) * in[0];
    //out[1] = (0.5 - (float)mouseY / height) * in[1];
  }
  
}

class SqField2D extends Map {

  SqField2D() {
    super(2, 2);
  }

  void map(float[] in, float[] out) {
    out[0] = sqrt(in[0]);
    out[1] = in[1] * in[1];
  }
  
}

class SinCosField3D extends Map {

  SinCosField3D() {
    super(3, 3);
  }

  void map(float[] in, float[] out) {
    out[0] = ((sin(TWO_PI*(in[0] + (float)mouseX / width)) + 1) / 2) * ((cos(frame / 100.0f + TWO_PI*in[1]) + 1) / 2);
    out[1] = ((cos(frame / 77.0f + TWO_PI*in[0]) + 1) / 2) * ((sin(TWO_PI*(in[1] + (float)mouseY / height)) + 1) / 2);
    out[2] = 0.5f - in[2] * ((float)mouseX / width) * ((float)mouseY / height);
  }

}

void makeGrid() {
  particles = new Particle[PARTICLES_X][PARTICLES_Y][PARTICLES_Z];

  for (int x = 0; x < PARTICLES_X; x++) {
    for (int z = 0; z < PARTICLES_Z; z++) {
      for (int y = 0; y < PARTICLES_Y; y++) {
        particles[x][y][z] = new Particle();
        ps.addParticle(particles[x][y][z]);
        particles[x][y][z].setPos(X_OFFSET + unitLength*x, Y_OFFSET + unitLength*y, Z_OFFSET + unitLength*z);
      }
    }
  }
}


public class MapForce extends Force {
    private PSystem pSystem;
    
    public Map mapObj;
    public float inputDivisor;
    public float outputMultiplier = 1.0f;

    MapForce() {
      super();
      inputDivisor = (width + height)/2.0f;
    }

    MapForce(Map map) {
      this();
      this.mapObj = map;
    }

  void init(PSystem pSystem) {
    this.pSystem = pSystem;
  }


    void applyForce() {
      Particle p;
      Particle particles[] = pSystem.particles;
      for (int i = 0; i < pSystem.nParticles; i++) {
        p = particles[i];
        applyForce(p);
      }
    }

    void applyForce(Particle p) {
       float[] mapOut = new float[3];
       float[] mapIn = new float[3];
       mapIn[0] = p.pos[0] / inputDivisor;
       mapIn[1] = p.pos[1] / inputDivisor;
       mapIn[2] = p.pos[2] / inputDivisor;
       
       mapObj.map(mapIn, mapOut);
       
       mapOut[0] = mapOut[0] * outputMultiplier;
       mapOut[1] = mapOut[1] * outputMultiplier;
       mapOut[2] = mapOut[2] * outputMultiplier;
       
       p.addForce(mapOut[0], mapOut[1], mapOut[2]);
    }
}



class Map {
  int inDim;
  int outDim;
  boolean isPolar = false;

  Map(int inDim, int outDim) {
    this.inDim = inDim;
    this.outDim = outDim;
  }

  void map(float[] in, float[] out) {
    for (int i = 0; i < outDim; i++) {
      out[i] = 0;
    }
  }

  void draw2DVectors(float fieldWidth, float fieldHeight, int xCells, int yCells, float baseLength) {
    if (isPolar) {
      draw2DVectorsPolar(fieldWidth, fieldHeight, xCells, yCells, baseLength);
    }
    else {
      draw2DVectorsCartesian(fieldWidth, fieldHeight, xCells, yCells, baseLength);
    }
  }

  void draw3DVectors(float fieldWidth, float fieldHeight, float fieldDepth, int xCells, int yCells, int zCells, float baseLength) {
    if (outDim != 3 || inDim != 3) {
      return;
    }

    float[] output = new float[3];
    float[] input = new float[3];

    float xInc = fieldWidth / xCells;
    float yInc = fieldHeight / yCells;
    float zInc = fieldDepth / zCells;

    float xIn, yIn, zIn;

    for (int x = 0; x < xCells; x++) {

      for (int y = 0; y < yCells; y++) {

        for (int z = 0; z < zCells; z++) {

          xIn = (x + 0.5f) / (float)xCells;
          yIn = (y + 0.5f) / (float)yCells;
          zIn = (z + 0.5f) / (float)zCells;
          
          input[0] = xIn;
          input[1] = yIn;
          input[2] = zIn;

          map(input, output);

          push();
          translate((x + 0.5f) * xInc, (y + 0.5f) * yInc, (z + 0.5f) * zInc);
          line(0, 0, 0, baseLength * output[0], baseLength * output[1], baseLength * output[2]);
          pop();
        }
      }
    }
  }

  void draw2DVectorsCartesian(float fieldWidth, float fieldHeight, int xCells, int yCells, float baseLength) {
    if (outDim != 2 || inDim != 2) {
      return;
    }

    float[] output = new float[2];
    float[] input = new float[2];

    float xInc = fieldWidth / xCells;
    float yInc = fieldHeight / yCells;

    float xIn, yIn;

    for (int x = 0; x < xCells; x++) {

      for (int y = 0; y < yCells; y++) {

        xIn = (x + 0.5f) / (float)xCells;
        yIn = (y + 0.5f) / (float)yCells;
        input[0] = xIn;
        input[1] = yIn;

        map(input, output);

        push();
        translate((x + 0.5f) * xInc, (y + 0.5f) * yInc);
        line(0, 0, baseLength * output[0], baseLength * output[1]);
        pop();
      }
    }
  }

  void draw2DVectorsPolar(float fieldWidth, float fieldHeight, int xCells, int yCells, float baseLength) {
    if (outDim != 2 || inDim != 2) {
      return;
    }

    float[] output = new float[2];
    float[] input = new float[2];

    float xInc = fieldWidth / xCells;
    float yInc = fieldHeight / yCells;

    float xIn, yIn;

    for (int x = 0; x < xCells; x++) {

      for (int y = 0; y < yCells; y++) {

        xIn = (x + 0.5f) / (float)xCells;
        yIn = (y + 0.5f) / (float)yCells;
        input[0] = xIn;
        input[1] = yIn;

        map(input, output);

        push();
        translate((x + 0.5f) * xInc, (y + 0.5f) * yInc);
        line(0, 0, output[1] * baseLength * cos(TWO_PI * output[0]), output[1] * baseLength * sin(TWO_PI * output[0]));
        pop();
      }
    }
  }

}


}