// Flow Field Following
// Daniel Shiffman <http://www.shiffman.net>
// The Nature of Code, Spring 2009
import processing.core.PApplet;
import processing.core.PVector;


public class FlowField {
  PApplet parent;
  
  // A flow field is a two dimensional array of PVectors
  PVector[][] field;
  int cols, rows; // Columns and Rows
  int resolution; // How large is each "cell" of the flow field

  PVector center;
  
  FlowField(PApplet _p, int r) {;
    parent = _p;
    
    resolution = r;
    // Determine the number of columns and rows based on sketch's width and height
    cols = parent.width/resolution;
    rows = parent.height/resolution;
    field = new PVector[cols][rows];
    init();
  }

  FlowField(PApplet _p, int r, PVector _c) {;
  parent = _p;
  
  resolution = r;
  center = _c;
  // Determine the number of columns and rows based on sketch's width and height
  cols = parent.width/resolution;
  rows = parent.height/resolution;
  field = new PVector[cols][rows];
  initCenter(_c);
}
  
  void init() {
    // Reseed noise so we get a new flow field every time
    parent.noiseSeed((int)parent.random(10000));
    float xoff = 0;
    for (int i = 0; i < cols; i++) {
      float yoff = 0;
      for (int j = 0; j < rows; j++) {
        // Use perlin noise to get an angle between 0 and 2 PI
        float theta = PApplet.map(parent.noise(xoff,yoff),0,1,0,PApplet.TWO_PI);
        // Polar to cartesian coordinate transformation to get x and y components of the vector
        field[i][j] = new PVector(PApplet.cos(theta),PApplet.sin(theta));
        yoff += 0.1;
      }
      xoff += 0.1;
    }
  }
  
  void initCenter(PVector _l){
    // init a flow field that generally points to a particular location
    
    // Reseed noise so we get a new flow field every time
    parent.noiseSeed((int)parent.random(10000));
    float xoff = 0;
    for (int i = 0; i < cols; i++) {
      float yoff = 0;
      for (int j = 0; j < rows; j++) {
        // Use perlin noise to get an angle between 0 and 2 PI
        float theta = PApplet.map(parent.noise(xoff,yoff),0,1,0,PApplet.TWO_PI);
        // Polar to cartesian coordinate transformation to get x and y components of the vector
        PVector _v = new PVector(PApplet.cos(theta),PApplet.sin(theta));
        PVector _p = new PVector(i*resolution,j*resolution);
        
        _p.sub(_l);
        _p.mult(-0.005f);
        _v.add(_p);
        
        field[i][j] = _v;
        yoff += 0.1;
      }
      xoff += 0.1;
    }
  }

  // Draw every vector
  void display() {
    for (int i = 0; i < cols; i++) {
      for (int j = 0; j < rows; j++) {
        drawVector(field[i][j],i*resolution,j*resolution,resolution-2);
      }
    }

  }

  // Renders a vector object 'v' as an arrow and a location 'x,y'
  void drawVector(PVector v, float x, float y, float scayl) {
    parent.pushMatrix();
    float arrowsize = 4;
    // Translate to location to render vector
    parent.translate(x,y);
    parent.stroke(100);
    // Call vector heading function to get direction (note that pointing up is a heading of 0) and rotate
    parent.rotate(v.heading2D());
    // Calculate length of vector & scale it to be bigger or smaller if necessary
    float len = v.mag()*scayl;
    // Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction)
    parent.line(0,0,len,0);
    parent.line(len,0,len-arrowsize,+arrowsize/2);
    parent.line(len,0,len-arrowsize,-arrowsize/2);
    parent.popMatrix();
  }

  PVector lookup(PVector lookup) {
    int i = (int) PApplet.constrain(lookup.x/resolution,0,cols-1);
    int j = (int) PApplet.constrain(lookup.y/resolution,0,rows-1);
    return field[i][j].get();
  }


}




