//Magnus Pfleghar
FlowField flowField;
boolean dispToggle=false;
boolean moveToggle=false;
void setup() {
  size(2000, 2000,P3D);
   surface.setResizable(false);
  frameRate(60);
  flowField=new FlowField(50);
}
void draw() {
  translate(width/2, height/2);
  rotateX(PI/4);
  rotateZ(PI/4);
  translate(-width/4,-height/4,-400);
  background(0);
    flowField.vis();
  if (moveToggle) {
  flowField.flowFieldAnim();
  }
} 
void keyPressed() {
  dispToggle=!dispToggle;
}
void mousePressed() {
  moveToggle=!moveToggle;
}
//Magnus Pfleghar
class FlowField {
  float dragX=0.0;
  float dragY=0.0;
  float dragZ=0.0;
  PVector[][][] grid;
  int amt=10;
  int size=1000/amt;
  int seed;
  FlowField(int resolution) {
    grid = new PVector[amt][amt][amt];
    init();
    seed=(int)random(10000);
  }
  void init() {
    noiseSeed(seed);
    float xoff = 0;
    for (int i = 0; i < amt; i++) {
      float yoff = 0;
      for (int j = 0; j < amt; j++) {
        float zoff=0;
        for (int x = 0; x < amt; x++) {
          float theta = map(noise(xoff, yoff,zoff), 0, 1, 0, TWO_PI);
          grid[i][j][x] = new PVector(cos(theta), sin(theta), cos(theta));
          println(this.grid[i][j][x].x+" "+this.grid[i][j][x].y);
          println("CosTheta"+cos(theta));
          zoff+=0.1;
        }
        yoff += 0.1;
      } 
      xoff += 0.1;
    }
  }

  void flowFieldAnim() {
    noiseSeed(seed);
    float xoff = 0;
    for (int i = 0; i < amt; i++) {
      float yoff = 0;
      for (int j = 0; j < amt; j++) {
        float zoff=0;
        for (int x = 0; x < amt; x++) {
          float theta=map(noise(xoff+dragX, yoff+dragY,zoff+dragZ), 0, 1, 0, TWO_PI);
          grid[i][j][x] = new PVector(cos(theta), sin(theta), cos(theta));
          println(this.grid[i][j][x].x+" "+this.grid[i][j][x].y);
          println("CosTheta"+cos(theta));
          zoff+=0.1;
        }
        yoff += 0.1;
      } 
      xoff += 0.1;
    }
    dragX+=0.005;
    dragY+=0.005;
    dragZ+=0.005;
  }
  void vis() {
    for (int i=0; i<amt; i++) {
      for (int a=0; a<amt; a++) {
        for (int z=0; z<amt; z++) {
          show(grid[i][a][z], i*size, a*size,z*size);
        }
      }
    }
  }
  void show(PVector v, float x, float y,float z) {
    pushMatrix();
    translate(x, y, z);
    
    if (!dispToggle) {
      fill(normalComp(v));
      noStroke();
    }else{ noFill();noStroke();}
   
    box(size, size, size);
    if (dispToggle) {
      rotate(v.heading2D());
      float len = v.mag()*20;
      strokeWeight(3);
      stroke(normalComp(v));
      line(0, 0, len, 0);
      fill(0, 0, 120, 255);
      noFill();
      //println(this.grid[i][a].x+" "+this.grid[i][a].y);
      point(0, 0);
    }
    popMatrix();
  }
  
  color normalComp(PVector dir) {
    //https://en.wikipedia.org/wiki/Normal_mapping
    /*  X: -1 to +1 :  Red:     0 to 255
     Y: -1 to +1 :  Green:   0 to 255
     Z:  0 to -1 :  Blue:  128 to 255
     */
    dir.normalize();
    //float angle=degrees(dir.heading());
    float z = sqrt(1 - dir.x*dir.x + dir.y*dir.y);
    println("Z"+z);
    float r=map(dir.x, -1, 1, 0, 255);
    float g=map(dir.y, -1, 1, 255, 0);
    float b=map(dir.z, 0, 1, 128, 255);
    b=128;
    color c = color(r, g, b);
    return c;
  }
}