Source

Vector.js

/**
 * @class Vector
 * @version v1.0.0
 * @author Anurag Hazra
 * @param {number} x
 * @param {number} y
 */
class Vector {

  constructor(x, y) {
    this.x = x || 0;
    this.y = y || 0;
  }

  /**
   * get distance from two vectors
   * @param {Vector} v1 
   * @param {Vector} v2 
   * @return {number}
   */
  static dist(v1, v2) {
    return v1.dist(v2);
  }

  /**
   * get distance squared from two vectors 
   * @param {Vector} v1 
   * @param {Vector} v2 
   * @return {number}
   */
  static distSq(v1, v2) {
    return v1.distSq(v2);
  }

  /**
   * subtract two vectors
   * @param {Vector} v1 
   * @param {Vector} v2 
   * @return {Vector}
   */
  static sub(v1, v2) {
    return new Vector(v1.x - v2.x, v1.y - v2.y);
  }

  /**
   * add two vectors
   * @param {Vector} v1 
   * @param {Vector} v2 
   * @return {Vector}
   */
  static add(v1, v2) {
    return new Vector(v1.x + v2.x, v1.y + v2.y);
  }

  /**
   * create vector from angle
   * @param {number} angle 
   */
  static fromAngle(angle) {
    let v = new Vector(0, 0);
    v.x = Math.cos(angle);
    v.y = Math.sin(angle);
    return v;
  }

  /**
   * create random2d vector
   * @return {Vector}
   */
  static random2D() {
    return Vector.fromAngle(Math.random() * Math.PI * 180);
  }

  /**
   * adds random jitter motion
   * @param {number} a 
   * @param {number} b 
   * @return {Vector}
   */
  jitter(a, b) {
    var v = new Vector(a, b);
    this.x += normalizedRandom() * v.x;
    this.y += normalizedRandom() * v.y;
    return this;
  }

  /**
   * add this vector to another vector
   * @param {Vector|number} x 
   * @param {Number} y 
   * @return {Vector}
   */
  add(x, y) {
    if (arguments.length === 1) {
      this.x += x.x;
      this.y += x.y;
    } else if (arguments.length === 2) {
      this.x += x;
      this.y += y;
    }
    return this;
  }

  /**
   * subtracts this vector to another vector
   * @param {Vector|number} x 
   * @param {Number} y 
   * @return {Vector}
   */
  sub(x, y) {
    if (arguments.length === 1) {
      this.x -= x.x;
      this.y -= x.y;
    } else if (arguments.length === 2) {
      this.x -= x;
      this.y -= y;
    }
    return this;
  }

  /**
   * multiply this vector to a scalar value or a vector
   * @param {Vector|number} v 
   * @return {Vector}
   */
  mult(v) {
    if (typeof v === 'number') {
      this.x *= v;
      this.y *= v;
    } else {
      this.x *= v.x;
      this.y *= v.y;
    }
    return this;
  }

  /**
   * divide this vector to a scalar value or a vector
   * @param {Vector|number} v 
   * @return {Vector}
   */
  div(v) {
    if (typeof v === 'number') {
      this.x /= v;
      this.y /= v;
    } else {
      this.x /= v.x;
      this.y /= v.y;
    }
    return this;
  }

  /**
   * set this vectors angle
   * @param {number} angle 
   */
  setAngle(angle) {
    var len = this.mag();
    this.x = Math.cos(angle) * len;
    this.y = Math.sin(angle) * len;
  }

  /**
   * get the angle thia and given vector
   * @param {Vector} v 
   * @returns {number}
   */
  angle(v) {
    return Math.atan2(this.x * v.y - this.y * v.x, this.x * v.x + this.y * v.y);
  }

  /**
   * @param {Vector} vLeft 
   * @param {Vector} vRight
   * @returns {Vector} 
   */
  angle2(vLeft, vRight) {
    return Vector.sub(vLeft, this).angle(Vector.sub(vRight, this));
  }

  /**
   * rotate this vector by some origin and angle
   * @param {Vector} origin 
   * @param {theta} theta 
   * @returns {Vector}
   */
  rotateBy(origin, theta) {
    var x = this.x - origin.x;
    var y = this.y - origin.y;
    return new Vector(
      x * Math.cos(theta) - y * Math.sin(theta) + origin.x,
      x * Math.sin(theta) + y * Math.cos(theta) + origin.y
    );
  }


  /**
   * get the magnitude of this vector
   * @return {number}
   */
  mag() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }

  /**
   * get the magnitude sqr of this vector
   * @return {number}
   */
  magSq() {
    return (this.x * this.x + this.y * this.y);
  }

  /**
   * set x, y of this vector
   * @param {number} x 
   * @param {number} y 
   * @return {Vector}
   */
  setXY(x, y) {
    this.x = x;
    this.y = y;
    return this;
  }

  /**
   * set the magnitude of this vector
   * @param {number} value 
   * @return {Vector}
   */
  setMag(value) {
    this.normalize();
    this.mult(value);
    return this;
  }

  /**
   * normalize this vector 
   * @return {Vector}
   */
  normalize() {
    let m = this.mag();
    if (m > 0) {
      this.div(m);
    }
    return this;
  }

  /**
   * normalize this vector to a specific length
   * @param {number} length 
   * @return {Vector}
   */
  normalizeTo(length) {
    var mag = this.mag();
    if (mag > 0) {
      mag = length / mag;
      this.mult(mag);
    }
    return this;
  }

  /**
   * limit this vector
   * @param {number} max 
   * @return {Vector}
   */
  limit(max) {
    if (this.mag() > max) {
      this.normalize();
      this.mult(max);
    }
    return this;
  }

  /**
   * get heading of this vector in radians
   * @return {number}
   */
  heading() {
    return (-Math.atan2(-this.y, this.x));
  }

  /**
   * get distance between this and specific vector
   * @param {Vector} v 
   * @return {number}
   */
  dist(v) {
    let dx = this.x - v.x;
    let dy = this.y - v.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  /**
   * get distance sqr between this and specific vector
   * @param {Vector} v 
   * @return {number}
   */
  distSq(v) {
    let dx = this.x - v.x;
    let dy = this.y - v.y;
    return (dx * dx + dy * dy);
  }

  /**
   * copy this vector
   * @return {Vector}
   */
  copy() {
    return new Vector(this.x, this.y);
  }

  /**
   * revert this vector
   * @return {Vector}
   */
  negative() {
    this.x = -this.x;
    this.y = -this.y;
    return this;
  }

  /**
   * return an array representation of this vector
   * @return {Array}
   */
  array() {
    return [this.x, this.y];
  }

  /**
   * return a string representation of this vector
   * @return {String}
   */
  toString() {
    return "[" + this.x + ", " + this.y + ", " + this.z + "]";
  }

  /**
   * 
   * @param {Vector} v 
   * @return {Vector}
   */
  project(v) {
    var coeff = ((this.x * v.x) + (this.y * v.y)) / ((v.x * v.x) + (v.y * v.y));
    this.x = coeff * v.x;
    this.y = coeff * v.y;
    return this;
  }

  /**
   * rotate this vector
   * @param {number} a 
   */
  rotate(a) {
    var b = this.heading() + a;
    var c = this.mag();
    this.x = Math.cos(b) * c;
    this.y = Math.sin(b) * c;
  }
}

export default Vector;