math/vector.js

import { deprecate } from "../logger/index.js";
import { TWO_PI } from "./constants.js";

const obj = {
  x: 0,
  y: 0
}
/**
 * This is a 2D vector class.
 * 
 */
export class Vector2 {
  /**
   * @param {number} [x] the x coordinate of the vector
   * @param {number} [y] the y coordinate of the vector
   */
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
  /**
   * @deprecated
   *Calculates length squared of vector and returns it
   */
  magnitudeSquared() {
    deprecate("Vector2().magnitudeSquared()", "Vector2.magnitudeSquared()")
    return Vector2.magnitudeSquared(this)
  }
  /**
   * @deprecated
   *Calculates length of this vector and returns 
   * it
   * 
   * @returns {number}
   */
  magnitude() {
    deprecate("Vector2().magnitude()", "Vector2.magnitude()")
    return Vector2.magnitude(this)
  }
  /**
   * @deprecated
   * Sets a vector to have the given length.
   * 
   * @param {number} length 
   */
  setMagnitude(length) {
    deprecate("Vector2().setMagnitude()", "Vector2.setMagnitude()")
    Vector2.setMagnitude(this, length, this)
    return this
  }
  /**
   * @deprecated
   *Calculates length of this vector to another vector
   * @param { Vector2} v the other vector
   */
  distanceTo(v) {
    deprecate("Vector2().distanceTo()", "Vector2.distanceTo()")

    return Vector2.distanceTo(this, v)
  }
  /**
   *Calculates length squared of this vector to another vector
   * 
   * @deprecated
   * @param { Vector2} v the other vector
   * @returns {number}
   */
  distanceToSquared(v) {
    deprecate("Vector2().distanceToSquared()", "Vector2.distanceToSquared()")
    return Vector2.distanceTo(this, v)
  }
  /**
   * Adds a given vector into this 
   * 
   * @deprecated
   * @param { Vector2} v
   */
  add(v) {
    deprecate("Vector2().add()", "Vector2.add()")
    Vector2.add(this, v, this)
    return this
  }
  /**
   * Adds a scalar value into this vector's
   * x and y values
   * 
   * @deprecated
   * @param {number} n
   */
  addScalar(n) {
    deprecate("Vector2().addScalar()", "Vector2.addScalar()")
    Vector2.addScalar(this, n, this)
    return this
  }
  /**
   * Subtracts a given vector from this vector
   * 
   * @deprecated
   * @param { Vector2} v
   * @returns {this}
   */
  sub(v) {
    deprecate("Vector2().sub()", "Vector2.sub()")
    Vector2.sub(this, v, this)
    return this
  }
  /**
   * Subtracts a scalar value from this vector's x and y values.
   * 
   * @deprecated
   * @param {number} n
   * @returns {this}
   */
  subScalar(n) {
    deprecate("Vector2().subScalar()", "Vector2.subScalar()")
    Vector2.subScalar(this, n, this)
    return this
  }
  /**
   * Multiplies this vector with a scalar.
   * 
   * @deprecated
   * @param {number} n 
   * @returns {this}
   */
  multiply(n) {
    deprecate("Vector2().multiply()", "Vector2.multiply()")
    Vector2.multiplyScalar(this, n, this)
    return this
  }
  /**
   * Rotates this vector by a given angle in radians.
   * 
   * @deprecated
   * @param {number} cos
   * @param {number} sin 
   * @returns {this}
   */
  rotateFast(cos, sin) {
    deprecate("Vector2().rotateFast()", "Vector2.rotateFast()")
    Vector2.rotateFast(this, cos, sin, this)
    return this
  };
  /**
   * 
   * @deprecated
   * Returns an array with x and y values of
   * this vector pushed into the array in
   * that order.
   * 
   * @param {number[]} [target = []] The array to
   * push values.Defaults to creating a new
   * array if not provided one
   * @returns number[]
   */
  toArray(target = [], offset = 0) {
    deprecate("Vector2().toArray()", "Vector2.toArray()")
    target[offset] = this.x
    target[offset + 1] = this.y
    return target;
  }
  /**
   * Copies x and y values of this vector to 
   * a new vector and returns the new vector.
   * @deprecated
   * @return Vector2
   */
  clone() {
    deprecate("Vector2().clone()", "Vector2.copy()")
    return Vector2.copy(this)
  }
  /**
   * Copies x and y values of another vector
   * to this vector.
   * 
   * @deprecated
   * @param { Vector2} v 
   * @return this
   */
  copy(v) {
    deprecate("Vector2().copy()", "Vector2.copy()")
    Vector2.copy(v, this)
    return this
  }
  /**
   * Sets x and y values of this vector to the given parameter.
   * 
   * @deprecated
   * @param {Number} x 
   * @param {Number} y
   */
  set(x, y) {
    deprecate("Vector2().set()", "Vector2.set()")
    Vector2.set(this, x, y)
    return this
  }
  /**
   * Returns a vector of this reflected on a sirface perpendicular to the normal.
   * 
   * @deprecated
   * @param {Vector2} normal the unit vector perpendicular to reflection surface
   * @param { Vector2} [target]
   * @return { Vector2}
   */
  reflect(normal, target = new Vector2()) {
    deprecate("Vector2().reflect()", "Vector2.reflect()")
    Vector2.reflect(this, normal, target)
    return target
  }
  /**
   * Forces this vector to have a length 
   * between the min and max.
   * 
   * @deprecated
   * @param {number} [min = 0] The smallest value 
   * the length of this vector is allowed to have.
   * @param {number} [max = 1] The biggest value the length of this vector is allowed to have.
   * @returns {this}
   */
  clamp(min = 0, max = 1) {
    deprecate("Vector2().clamp()", "Vector2.clamp()")
    Vector2.clampMagnitude(this, min, max, this)
    return this
  }
  /**
   * Calculates the dot product of two vectors.
   * 
   * @deprecated
   * @param { Vector2} v
   * @returns {number}
   */
  dot(v) {
    deprecate("Vector2().dot()", "Vector2.dot()")
    return Vector2.dot(this, v)
  }
  /**
   * Calculates the cross product of two vectors.
   * 
   * @deprecated
   * @param { Vector2} v
   * @returns {number}
   */
  cross(v) {
    deprecate("Vector2().cross()", "Vector2.cross()")
    return Vector.cross(this, v)
  }
  /**
   * Makes this vector a unit vector by 
   * dividing its components with its length.
   * 
   * @deprecated
   * @returns {this}
   */
  normalize() {
    deprecate("Vector2().normalize()", "Vector2.normalize()")
    Vector2.normalize(this, this)
    return this
  }
  /**
   * Returns a scaled vector normal to this vector,when scaled to 1,it returns a unit vector.
   * 
   * @deprecated
   * @param {number} l the length of the vector returned.
   * @param { Vector2} [target = Vector2] Vector2 in which results are stored.
   * @returns { Vector2}
   */
  normal(l = 1, target = new Vector2()) {
    deprecate("Vector2().normal()", "Vector2.normal()")
    Vector2.normal(this, target)
    Vector2.normalize(target, target)
    Vector2.multiplyScalar(target, l, target)
    return target
  }
  /**
   * Returns the normal to a vector, the normal has the same length as the vector.
   * 
   * @deprecated
   * @param { Vector2} [target = Vector2] Vector2 in which results are stored.
   *  @returns { Vector2}
   */
  normalFast(target = new Vector2()) {
    deprecate("Vector2().normalFast()", "Vector2.normal()")
    Vector2.set(target,-this.y, this.x)
    return this
  }
  /**
   * Rotates this vector by a given angle in radians.
   * 
   * @deprecated
   * @param {number} rad Angle in radians
   * @returns {this}
   */
  rotate(rad) {
    deprecate("Vector2().rotate()", "Vector2.rotate()")
    Vector2.rotate(this, rad, this)
    return this
  }
  /**
   * Divides this vector with a scalar.
   * 
   * @deprecated
   * @param {number} n 
   * @returns {this}
   */
  divide(n) {
    deprecate("Vector2().divide()", "Vector2.divide()")
    this.multiply(1 / n)
    return this
  }
  /**
   * Checks to see if this vector is equal to
   * another vector.
   * 
   * @deprecated
   * @param { Vector2} v
   * @returns {boolean}
   */
  equals(v) {
    deprecate("Vector2().equals()", "Vector2.equals()")
    return Vector2.equal(this, v)
  }
  /**
   * Checks if the vector length is zero.
   * 
   * @deprecated
   * @returns {boolean}
   */
  equalsZero() {
    deprecate("Vector2().equalsZero()", "Vector2.equalsZero()")
    return Vector2.equalsZero(this)
  }
  /**
   * Negates the values of this vector.
   * 
   * @deprecated
   * @returns {this}
   */
  reverse() {
    deprecate("Vector2().reverse()", "Vector2.reverse()")
    Vector2.reverse(this, this)
    return this
  }
  /**
   * @param {Vector_like} v
   */
  static magnitudeSquared(v) {
    return v.y ** 2 + v.x ** 2
  }
  /**
   * @param {Vector_like} v
   */
  static magnitude(v) {
    return Math.sqrt(Vector2.magnitudeSquared(v));
  }
  /**
   * @param {Vector_like} v
   * @param {number} length
   */
  static setMagnitude(v, length, out = new Vector2()) {
    Vector2.normalize(v, out)
    Vector2.multiplyScalar(v, length, out)
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static distanceTo(v1, v2) {
    obj.x = v1.x - v2.x
    obj.y = v1.y - v2.y
    return Vector2.magnitude(obj)
  }
  /**
   * Calculates length squared of this vector to another vector
   * 
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static distanceToSquared(v1, v2) {
    obj.x = v1.x - v2.x
    obj.y = v1.y - v2.y
    return Vector2.magnitudeSquared(obj)
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   * @param {Vector_like} [out]
   */
  static add(v1, v2, out = new Vector2()) {
    out.x = v1.x + v2.x
    out.y = v1.y + v2.y
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {number} n
   * @param {Vector_like} [out]
   */
  static addScalar(v1, n, out = new Vector2()) {
    out.x = v1.x + n
    out.y = v1.y + n
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   * @param {Vector_like} [out]
   */
  static sub(v1, v2, out = new Vector2()) {
    out.x = v1.x - v2.x
    out.y = v1.y - v2.y
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {number} n
   * @param {Vector_like} [out]
   */
  static subScalar(v1, n, out = new Vector2()) {
    out.x = v1.x - n
    out.y = v1.y - n
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   * @param {Vector_like} [out]
   */
  static multiply(v1, v2, out = new Vector2()) {
    out.x = v1.x * v2.x
    out.y = v1.y * v2.y
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {number} n
   * @param {Vector_like} [out]
   */
  static multiplyScalar(v1, n, out = new Vector2()) {
    out.x = v1.x * n
    out.y = v1.y * n
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   * @param {Vector_like} [out]
   */
  static divide(v1, v2, out = new Vector2()) {
    out.x = v1.x / v2.x
    out.y = v1.y / v2.y
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {number} n
   * @param {Vector_like} [out]
   */
  static divideScalar(v1, n, out = new Vector2()) {
    return Vector2.multiplyScalar(v1, 1 / n, out)
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static dot(v1, v2) {
    return v1.x * v2.x + v1.y * v2.y
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static cross(v1, v2) {
    return v1.x * v2.y - v1.y * v2.x
  }
  /**
   * @param {Vector_like} v1
   * @param {number} n
   * @param {Vector_like} out
   */
  static crossScalar(v1, n, out = new Vector2()) {
    out.x = v1.y * -n
    out.y = v1.x * n
    return out
  }
  /**
   * @param {Vector_like} v
   * @param {Vector_like} [out=new Vector2()] 
   */
  static normalize(v, out = new Vector2()) {
    const length = Vector2.magnitude(v)
    if (length == 0) return out
    out.x = v.x / length
    out.y = v.y / length
    return out
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static equal(v1, v2) {
    return v1.x === v2.x && v1.y === v2.y
  }
  /**
   * @param {Vector_like} v1
   * @param {Vector_like} v2
   */
  static absEqual(v1, v2) {
    return (
      Math.abs(v1.x) === Math.abs(v2.x) ||
      Math.abs(v1.y) === Math.abs(v2.y)
    )
  }
  /**
   * @param {Vector_like} v
   */
  static equalsZero(v) {
    return v.x === 0 && v.y === 0
  }
  /**
   * @param {Vector_like} v
   * @param {Vector_like} out
   */
  static normal(v, out = new Vector2()) {
    return Vector2.set(out, -v.y, v.x);
  }
  /**
   * @param {Vector_like} v
   * @param {number} angle
   * @param {Vector_like} out
   */
  static rotate(v, angle, out = new Vector2()) {
    return Vector2.rotateFast(v, Math.cos(angle), Math.sin(angle), out)
  }
  /**
   * @param {Vector_like} v
   * @param {number} cos
   * @param {number} sin
   * @param {Vector_like} out
   */
  static rotateFast(v, cos, sin, out = new Vector2()) {
    const x = v.x
    out.x = x * cos - v.y * sin
    out.y = x * sin + v.y * cos
    return out
  }
  /**
   * @param {Vector_like} v
   * @param {Vector_like} [out=new Vector2()] 
   */
  static copy(v, out = new Vector2()) {
    out.x = v.x
    out.y = v.y
    return out
  }
  /**
   * @param {Vector_like} v
   * @param {number} x
   * @param {number} y
   */
  static set(v, x, y) {
    v.x = x
    v.y = y
    return v
  }
  /**
   * @param {Vector_like} v
   * @param {Vector_like} [out=new Vector2()] 
   */
  static reverse(v, out = new Vector2()) {
    return Vector2.multiplyScalar(v, -1, out)
  }
  /**
   * @param {Vector_like} v
   * @param {Vector_like} normal
   * @param {Vector_like} [out]
   */
  static reflect(v, normal, out = new Vector2()) {
    const multiplier = Vector2.dot(v, normal) * 2
    out.x = normal.x * multiplier - v.x
    out.y = normal.y * multiplier - v.y

    return out
  }
  /**
   * @param {Vector_like} v
   * @param {number} min
   * @param {number} max
   * @param {Vector_like} out
   */
  static clampMagnitude(v, min, max, out) {
    if (Vector2.equalsZero(v)) return Vector2.copy(v, out)
    let length = Vector2.magnitude(v)
    if (length > max)
      return Vector2.multiplyScalar(v, max / length, out)
    if (length < min)
      return Vector2.multiplyScalar(v, min / length, out)
    return Vector2.copy(v, out)
  }
  /**
   * Gets the angle (in degrees) between two
   * vectors in the range 0° to 360° in the anticlockwise direction from v1 to v2
   * 
   * @param { Vector2} v1 start of the angle
   * @param { Vector2} v2 end of the angle
   * @returns {number}
   */
  static getAbsDegBtwn(v1, v2) {
    let a = Vector2.cross(v1,v2)
    let deg = Vector2.getDegBtwn(v1, v2)
    return a < 0 ? deg : 360 - deg
  }
  /**
   * Same as ` Vector2.getAbsDegBtwn` but returns in radians.
   * 
   * @param { Vector2 } v1 start of the angle
   * @param { Vector2 } v2 end of the angle
   * @returns {number}
   **/
  static getAbsAngleBetween(v1, v2) {
    const a = Vector2.cross(v1, v2)
    const deg = Vector2.getAngleBetween(v1, v2)

    return a < 0 ? deg : Math.PI * 2 - deg
  }
  /**
   * Gets the angle (in radians) between two
   * vectors in the shortest direction from v1 to v2 in the range of `0` to `Math.PI`
   * 
   * @param { Vector2} v1 start of the angle
   * @param { Vector2} v2 end of the angle
   * @returns {number}
   */
  static getAngleBetween(v1, v2) {
    return Math.acos(Vector2.dot(v1, v2) / (Vector2.magnitude(v1) * Vector2.magnitude(v2)))
  }
  /**
   * Gets the angle (in degrees) between two
   * vectors in shortest direction from v1 to v2 in the range `0°` to `180°`
   * 
   * @param { Vector2} v1 start of the angle
   * @param { Vector2} v2 end of the angle
   * @returns {number}
   */
  static getDegBtwn(v1, v2) {
    return Vector2.getAngleBetween(v1, v2) * 180 / Math.PI
  }
  /**
   * Returns a unit vector pointing in the
   * given angle starting from the positive x axis.
   * 
   * @param {number} radian angle in radians from 0 to `Math.PI * 2`
   * @param { Vector2} [out] Vector2 to store results in.
   * @returns { Vector2}
   */
  static fromAngle(radian, out = new Vector2()) {
    Vector2.set(out, Math.cos(radian), Math.sin(radian))
    return out
  }
  /**
   * Generates a new unit Vector2 in a random direction
   * 
   * @param { Vector2} [out]
   * @returns { Vector2}
   */
  static random(out) {
    return Vector2.fromAngle(Math.random() * TWO_PI, out)
  }
  /**
   * Returns a Vector2 that has been lerped between v1 and v2
   * @param { Vector2} v1 the vector to lerp from
   * @param { Vector2} v2 the vector to lerp from
   * @param {number} t a value from 0 to 1 to scale the new Vector2 between v1 and v2
   * @param { Vector2} [out] the vector to store results into
   * 
   * @returns { Vector2}
   */
  static lerp(v1, v2, t, out = new Vector2()) {
    Vector2.set(
      out,
      (v2.x - v1.x) * t + v1.x,
      (v2.y - v1.y) * t + v1.y
    )
    return out
  }
  /**
   * Returns the angle in radians between the positive x-axis and the vector.
   * 
   * @param { Vector2} v
   * @returns {number}
   */
  static toAngle(v) {
    let a = Math.atan2(v.y, v.x)
    return a < 0 ? TWO_PI + a : a
  }
  /**
   * @this {Vector2}
   */
  [Symbol.iterator] = function*() {
    yield this.x
    yield this.y
  }
  /**
   * A vector whose x and y values will remain 0.
   * 
   * @static
   * @readonly
   * @type { Vector2}
   */
  static ZERO = Object.freeze(new Vector2())
}
/**
 * @deprecated
 */
export class Vector extends Vector2 {
  /**
   * @param {number} x the x coordinate of the vector
   * @param {number} y the y coordinate of the vector
   */
  constructor(x, y) {
    super(x, y)
    console.error("The class `Vector` is depreciated since v0.4.13.Use Vector2 instead.")
  }
}

/**
 * @deprecated
 */
export class Vec2 extends Vector2 {
  /**
   * @param {number} x the x coordinate of the vector
   * @param {number} y the y coordinate of the vector
   */
  constructor(x, y) {
    super(x, y)
    console.error("The class `Vec2` is depreciated since v0.4.13.Use Vector2 instead.")
  }
}