Home Reference Source Test Repository

src/js/util/vector-2d.js

/**
 * Class for representing 2D vectors.
 * @class Vector2D
 */
class Vector2D {

  static EPSILON = 1e-8;

  /**
   * x component of the vector.
   * @type {number}
   */
  x;

  /**
   * y component of the vector.
   * @type {number}
   */
  y;

  /**
   * Constructs a Vector2D object.
   * @param  {number} x - x-component of the vector.
   * @param  {number} y - y-component of the vector.
   * @constructs Vector2D
   */
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  /**
   * Getter for vector length.
   * @return {number} - The length of the vector.
   */
  get length() {
    let len = Math.sqrt(this.x * this.x + this.y * this.y);
    return Math.abs(len) < Vector2D.EPSILON ? 0 : len;
  }

  /**
   * Checks if this vector and another vector are equal. Returns true if the x- and y-components are equal.
   * @param  {Vector2D} vec - Vector to compare with.
   * @return {boolean} - Whether or not the vectors are equivalent.
   */
  equals(vec) {
    return Math.abs(this.x - vec.x) < Vector2D.EPSILON && Math.abs(this.y - vec.y) < Vector2D.EPSILON;
  }

  /**
   * Finds the result of addition with another vector.
   * @param {Vector2D} vec - The vector to add.
   * @return {Vector2D} - The result of vector addition.
   */
  add(vec) {
    return new Vector2D(this.x + vec.x, this.y + vec.y);
  }

  /**
   * Finds the result of subtracting another vector.
   * @param {Vector2D} vec - The vector to subtract.
   * @return {Vector2D} - The result of vector subtraction.
   */
  sub(vec) {
    return new Vector2D(this.x - vec.x, this.y - vec.y);
  }

  /**
   * Finds the dot product between this vector and another vector.
   * @param  {Vector2D} vec - Vector to find a dot product with.
   * @return {number} - The value of the dot product.
   */
  dot(vec) {
    return this.x * vec.x + this.y * vec.y;
  }

  /**
   * Finds the "cross product" between this vector and another vector.
   * @param  {Vector2D} vec - Vector to find the cross product with.
   * @return {number} - The value of the cross product. Zero means the vectors are parallel.
   */
  cross(vec) {
    return this.x * vec.y - this.y * vec.x;
  }

  /**
   * Finds the vector resulting from multiplication by a scalar.
   * @param  {number} n - The scalar to multiply by.
   * @return {Vector2D} - Vector resulting from the scale.
   */
  scale(n) {
    return new Vector2D(this.x * n, this.y * n);
  }

  /**
   * Finds the vector resulting from a rotation in degrees.
   * @param  {number} deg - Angle (in degrees) to rotate.
   * @return {Vector2D} - Vector resulting from the rotation.
   */
  rotateDegrees(deg) {
    return this.rotateRadians(deg * (Math.PI / 180));
  }

  /**
   * Finds the vector resulting from a rotation in radians.
   * @param  {number} rad - Angle (in radians) to rotate.
   * @return {Vector2D} - Vector resulting from the rotation.
   */
  rotateRadians(rad) {
    let cos = Math.cos(rad);
    let sin = Math.sin(rad);
    return new Vector2D(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
  }

  /**
   * Finds the angle between two vectors (in degrees).
   * @param  {Vector2D} vec - Vector to find the angle to.
   * @return {number} - Angle between the vectors in degrees.
   */
  degreesTo(vec) {
    let angle = Math.atan2(vec.y, vec.x) - Math.atan2(this.y, this.x);
    angle *= 180 / Math.PI;
    angle = (angle + 360) % 360;
    if (Math.round(angle) !== angle && Math.abs(Math.round(angle) - angle) < Vector2D.EPSILON) {
      return Math.round(angle);
    }
    return angle;
  }

  /**
   * Finds the vector projection of this vector onto another vector.
   * @param  {Vector2D} vec - Vector to project onto.
   * @return {Vector2D} - Vector resulting from the vector projection.
   */
  projectOnto(vec) {
    return vec.scale(this.dot(vec) / vec.dot(vec));
  }
}

export { Vector2D };
export default Vector2D;