Current File : /home/jeshor13/11bsouth.com/MidtownGame/vehicle.js
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com

// The "Vehicle" class

function Vehicle(loc, dirI, cROW, birthtime) {
  this.position = loc;
  this.acceleration = createVector(0, 0);
  this.velocity = dirI;
  this.dirI = dirI
  this.r = 10;
  this.maxspeed = 4;
  this.maxforce = .5;
  this.cROW = cROW
  this.isDead = false;
  this.birthtime = birthtime
  this.obstructed = false
  this.stop = false

  this.run = function() {
    this.display();
    this.traffic()
    this.update();
    if (this.stop === true) {

    }

    // if (this.obstructed == true) {
    // this.maxspeed = 2
    //this.maxforce = 1;
    // }else{this.maxspeed = 4; this.maxforce = .5}
  };

  // This function implements Craig Reynolds' path following algorithm
  // http://www.red3d.com/cwr/steer/PathFollow.html
  this.follow = function() {


    // Predict location 50 (arbitrary choice) frames ahead
    // This could be based on speed
    var predict = this.velocity.copy();
    predict.normalize();
    predict.mult(90);
    var predictLoc = p5.Vector.add(this.position, predict);

    // Now we must find the normal to the path from the predicted location
    // We look at the normal for each line segment and pick out the closest one

    var normal = null;
    var target = null;
    var worldRecord = 1000000; // Start with a very high record distance that can easily be beaten
    // Loop through all points of the path
    for (var i = 0; i < this.cROW.points.length; i++) {
      // Look at a line segment
      var a = this.cROW.points[i];
      if (i == this.cROW.points.length - 1) {
        var b = this.cROW.points[i];
      } else {
        var b = this.cROW.points[(i + 1) % this.cROW.points.length];
      }
      //println(b);

      // Get the normal point to that line
      var normalPoint = getNormalPoint(predictLoc, a, b);
      // This only works because we know our path goes from left to right
      // We could have a more sophisticated test to tell if the point is in the line segment or not

      var dir = p5.Vector.sub(b, a)
      if (normalPoint.x < min(a.x, b.x) || normalPoint.x > max(a.x, b.x) || normalPoint.y < min(a.y, b.y) || normalPoint.y > max(a.y, b.y)) {
        normalPoint = b.copy();
        a = this.cROW.points[(i + 1) % this.cROW.points.length]
        b = this.cROW.points[(i + 2) % this.cROW.points.length]
        dir = p5.Vector.sub(b, a)
      }


      // How far away are we from the path?
      var distance = p5.Vector.dist(predictLoc, normalPoint);
      // Did we beat the record and find the closest line segment?
      if (distance < worldRecord) {
        worldRecord = distance;
        // If so the target we want to steer towards is the normal
        normal = normalPoint;

        // Look at the direction of the line segment so we can seek a little bit ahead of the normal

        //var dir = p5.Vector.sub(b, a);
        dir.normalize();
        // This is an oversimplification
        // Should be based on distance to path & velocity
        dir.mult(10);
        target = normalPoint.copy();
        target.add(dir);

      }

    }





    // Only if the distance is greater than the path's radius do we bother to steer
    if (worldRecord > this.cROW.radius && target !== null) {
      this.seek(target);
    }


    //Draw the debugging stuff
    if (debug) {
      // Draw predicted future location
      stroke(255);
      fill(200);
      line(this.position.x, this.position.y, predictLoc.x, predictLoc.y);
      ellipse(predictLoc.x, predictLoc.y, 4, 4);

      // Draw normal location
      stroke(255);
      fill(200);
      ellipse(normal.x, normal.y, 4, 4);
      // Draw actual target (red if steering towards it)
      line(predictLoc.x, predictLoc.y, normal.x, normal.y);
      if (worldRecord > this.cROW.radius) fill(255, 0, 0);
      noStroke();
      ellipse(target.x, target.y, 8, 8);
    }

  };


  this.applyForce = function(force) {
    // We could add mass here if we want A = F / M
    this.acceleration.add(force);
  };

  // A method that calculates and applies a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  this.seek = function(target) {
    var desired = p5.Vector.sub(target, this.position); // A vector pointing from the position to the target

    // If the magnitude of desired equals 0, skip out of here
    // (We could optimize this to check if x and y are 0 to avoid mag() square root
    if (desired.mag() === 0) return;

    // Normalize desired and scale to maximum speed
    desired.normalize();
    desired.mult(this.maxspeed);
    // Steering = Desired minus Velocity
    var steer = p5.Vector.sub(desired, this.velocity);
    steer.limit(this.maxforce); // Limit to maximum steering force

    this.applyForce(steer);
  };

  // Method to update position
  this.update = function() {
    // Update velocity
    this.velocity.add(this.acceleration);
    // Limit speed
    this.velocity.limit(this.maxspeed);
    this.position.add(this.velocity);
    // Reset accelerationelertion to 0 each cycle
    this.acceleration.mult(0);
  };

  // Death
  this.borders = function() {

    if (this.position.x >= width - this.r * 2 && millis() - this.birthtime >= 1000) {
      this.isDead = true

    }
    if (this.position.x <= 0 + this.r * 2 && millis() - this.birthtime >= 1000) {
      this.isDead = true

    }
    if (this.position.y >= height - this.r * 2 && millis() - this.birthtime >= 1000) {
      this.isDead = true

    }
    if (this.position.y <= 0 + this.r * 2 && millis() - this.birthtime >= 1000) {
      this.isDead = true
    };
  }

  this.display = function() {
    // Draw a triangle rotated in the direction of velocity
    var theta = (this.velocity.heading() + PI / 2);
    colorMode(HSB);
    var m = map((millis() - this.birthtime), 0, 30000, 1, 255)
    fill(0, 255, m);
    stroke(255);
    strokeWeight(1);
    push();

    translate(this.position.x, this.position.y);
    rotate(theta);
    beginShape();
    vertex(0, -this.r * 2);
    vertex(-this.r, this.r * 2);
    vertex(this.r, this.r * 2);
    endShape(CLOSE);
    pop();
  };

  // A function to get the normal point from a point (p) to a line segment (a-b)
  // This function could be optimized to make fewer new Vector objects
  var getNormalPoint = function(p, a, b) {
    // Vector from a to p
    var ap = p5.Vector.sub(p, a); // subrtacting a (current line point loc) from predicted lcoationt
    // Vector from a to b
    var ab = p5.Vector.sub(b, a); // subrtacting b (next line point loc) from predicted lcoationt
    ab.normalize(); // Normalize the line
    // Project vector "diff" onto line by using the dot product
    ab.mult(ap.dot(ab));
    var normalPoint = p5.Vector.add(a, ab);

    return normalPoint;
  };

  // Separation
  // Method checks for nearby boids and steers away
  this.separate = function() {
    var desiredseparation = this.r * 5;
    var steer = createVector(0, 0, 0);
    var count = 0;
    var predict = this.velocity.copy();
    predict.normalize();
    predict.mult(this.r * noise(millis()) * 10);
    var predictLoc = p5.Vector.add(this.position, predict);
    // For every boid in the system, check if it's too close
    for (var i = 0; i < cars.length; i++) {
      var other = cars[i];
      var predictOther = other.velocity.copy();
      predictOther.normalize();
      predictOther.mult(this.r * noise(millis() + 5) * 10);
      var predictOtherLoc = p5.Vector.add(other.position, predict);
      var d = p5.Vector.dist(predictLoc, predictOtherLoc);
      //var d2 = p5.Vector.dist(this.position, other.position);
      // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if ((d > 0) && (d < desiredseparation)) {
        // Calculate vector pointing away from neighbor
        var diff = p5.Vector.sub(predictLoc, predictOtherLoc);
        //var diff2 = p5.Vector.sub(this.position, other.position);
        diff.normalize();
        diff.div(d); // Weight by distance
        steer.add(diff);
        count++; // Keep track of how many
      }
    }
    // Average -- divide by how many
    if (count > 0) {
      steer.div(count);
    }

    // As long as the vector is greater than 0
    if (steer.mag() > 0) {
      // Implement Reynolds: Steering = Desired - Velocity
      steer.normalize();
      steer.mult(this.maxspeed);
      steer.sub(this.velocity);
      steer.limit(this.maxforce);
      this.applyForce(steer);
      this.obstructed = true
    } else {
      this.obstructed = false
    }


  }

  this.traffic = function() {
    if (this.obstructed === true) {
      var c = this.velocity.copy()
      c.mult(-.1)
      this.applyForce(c)
    }
    var predict = this.velocity.copy();
    predict.normalize();
    predict.mult(20);
    var predictLoc = p5.Vector.add(this.position, predict);
    if (predictLoc.dist(this.cROW.points[0]) < this.position.dist(this.cROW.points[0]) - 5) {
      var g = this.velocity.copy()
      g.normalize()
      g.mult(-this.maxspeed)
      this.applyForce(g)
    }
    if (this.velocity.mag() < .5 && this.obstructed === false) {
      this.applyForce(this.dirI)
    }
    // if (abs(predictLoc.x - this.cROW.light.position.x) < this.r * 2 && this.cROW.light.state == red) {
    //   this.stop = true
    //   this.obstructed = true
    // } else {
    //   this.stop = false
    // }
  }
}