Source: og/math/Vec2.js

/**
 * @module og/math/Vec2
 */

"use strict";

import { Vec3 } from "./Vec3.js";

/**
 * Class represents a 3d vector.
 * @class
 * @param {number} [x] - First value.
 * @param {number} [y] - Second value.
 */
export class Vec2 {
    constructor(x, y) {
        /**
         * @public
         * @type {number}
         */
        this.x = x || 0.0;

        /**
         * @public
         * @type {number}
         */
        this.y = y || 0.0;
    }
    /** @const */
    static get UP() {
        return new Vec2(0, 1);
    }
    /** @const */
    static get DOWN() {
        return new Vec2(0, -1);
    }
    /** @const */
    static get RIGHT() {
        return new Vec2(1, 0);
    }
    /** @const */
    static get LEFT() {
        return new Vec2(-1, 0);
    }
    /** @const */
    static get ZERO() {
        return new Vec2();
    }

    /**
     * Returns summary vector.
     * @static
     * @param {math.Vec2} a - First vector.
     * @param {math.Vec2} b - Second vector.
     * @returns {math.Vec2} - Summary vector.
     */
    static add(a, b) {
        var res = new Vec2(a.x, a.y);
        res.addA(b);
        return res;
    }

    /**
     * Returns two vectors subtraction.
     * @static
     * @param {math.Vec2} a - First vector.
     * @param {math.Vec2} b - Second vector.
     * @returns {math.Vec2} - Vectors subtraction.
     */
    static sub(a, b) {
        var res = new Vec2(a.x, a.y);
        res.subA(b);
        return res;
    }

    /**
     * Returns scaled vector.
     * @static
     * @param {math.Vec2} a - Input vector.
     * @param {number} scale - Scale value.
     * @returns {math.Vec2}
     */
    static scale(a, scale) {
        var res = new Vec2(a.x, a.y);
        res.scale(scale);
        return res;
    }

    /**
     * Returns two vectors production.
     * @static
     * @param {math.Vec2} a - First vector.
     * @param {math.Vec2} b - Second vector.
     * @returns {math.Vec2}
     */
    static mul(a, b) {
        var res = new Vec2(a.x, a.y);
        res.mulA(b);
        return res;
    }

    /**
     * Returns vector components division product one to another.
     * @static
     * @param {math.Vec2} a - First vector.
     * @param {math.Vec2} b - Second vector.
     * @returns {math.Vec2}
     */
    static div(a, b) {
        var res = new Vec2(a.x, a.y);
        res.divA(b);
        return res;
    }

    /**
     * Get projection of the first vector to the second.
     * @static
     * @param {math.Vec2} b - First vector.
     * @param {math.Vec2} a - Second vector.
     * @returns {math.Vec2}
     */
    static proj_b_to_a(b, a) {
        return a.scaleTo(a.dot(b) / a.dot(a));
    }

    /**
     * Gets angle between two vectors.
     * @static
     * @param {math.Vec2} a - First vector.
     * @param {math.Vec2} b - Second vector.
     * @returns {number}
     */
    static angle(a, b) {
        return Math.acos(a.dot(b) / Math.sqrt(a.length2() * b.length2()));
    }

    /**
     * Makes vectors normalized and orthogonal to each other.
     * @static
     * @param {math.Vec2} normal - Normal vector.
     * @param {math.Vec2} tangent - Tangent vector.
     * @returns {math.Vec2}
     */
    static orthoNormalize(normal, tangent) {
        normal = normal.norm();
        normal.scale(tangent.dot(normal));
        return tangent.sub(normal).normalize();
    }

    /**
     * Converts to 3d vector, third value is 0.0.
     * @public
     * @returns {Vec3}
     */
    toVector3() {
        return new Vec3(this.x, this.y, 0);
    }

    /**
     * Returns clone vector.
     * @public
     * @returns {math.Vec2}
     */
    clone() {
        return new Vec2(this.x, this.y);
    }

    /**
     * Compares with vector. Returns true if it equals another.
     * @public
     * @param {math.Vec2} p - Vector to compare.
     * @returns {boolean}
     */
    equal(p) {
        return this.x === p.x && this.y === p.y;
    }

    /**
     * Copy input vector's values.
     * @param {math.Vec2} point2 - Vector to copy.
     * @returns {math.Vec2}
     */
    copy(point2) {
        this.x = point2.x;
        this.y = point2.y;
        return this;
    }

    /**
     * Gets vector's length.
     * @public
     * @returns {number}
     */
    length() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    /**
     * Returns squared vector's length.
     * @public
     * @returns {number}
     */
    length2() {
        return this.x * this.x + this.y * this.y;
    }

    /**
     * Adds vector to the current.
     * @public
     * @param {math.Vec2}
     * @returns {math.Vec2}
     */
    addA(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    }

    /**
     * Summarize two vectors.
     * @public
     * @param {math.Vec2}
     * @returns {math.Vec2}
     */
    add(v) {
        return new Vec2(this.x + v.x, this.y + v.y);
    }

    /**
     * Subtract vector from the current where results saved on the current instance.
     * @public
     * @param {math.Vec2} v - Subtract vector.
     * @returns {math.Vec2}
     */
    subA(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
    }

    /**
     * Subtract vector from the current.
     * @public
     * @param {math.Vec2} v - Subtract vector.
     * @returns {math.Vec2}
     */
    sub(v) {
        return new Vec2(this.x - v.x, this.y - v.y);
    }

    /**
     * Scale current vector.
     * @public
     * @param {number} scale - Scale value.
     * @returns {math.Vec2}
     */
    scale(scale) {
        this.x *= scale;
        this.y *= scale;
        return this;
    }

    /**
     * Scale current vector to another instance.
     * @public
     * @param {number} scale - Scale value.
     * @returns {math.Vec2}
     */
    scaleTo(scale) {
        return new Vec2(this.x * scale, this.y * scale);
    }

    /**
     * Multiply current vector object to another and store result in the current instance.
     * @public
     * @param {math.Vec2} vec - Multiply vector.
     * @returns {math.Vec2}
     */
    mulA(vec) {
        this.x *= vec.x;
        this.y *= vec.y;
        return this;
    }

    /**
     * Multiply current vector object to another and returns new vector instance.
     * @public
     * @param {math.Vec2} vec - Multiply vector.
     * @returns {math.Vec2}
     */
    mul(vec) {
        return new Vec2(this.x * vec.x, this.y * vec.y);
    }

    /**
     * Divide current vector's components to another. Results stores in the current vector object.
     * @public
     * @param {math.Vec2}
     * @returns {math.Vec2}
     */
    divA(vec) {
        this.x /= vec.x;
        this.y /= vec.y;
        return this;
    }

    /**
     * Gets vectors dot production.
     * @public
     * @param {math.Vec2} v - Another vector.
     * @returns {number}
     */
    dot(v) {
        return v.x * this.x + v.y * this.y;
    }

    /**
     * Gets vectors dot production.
     * @public
     * @param {Array.<number>} arr - Array vector. (exactly 2 entries)
     * @returns {number}
     */
    dotArr(arr) {
        return arr[0] * this.x + arr[1] * this.y;
    }

    /**
     * Gets vectors cross production.
     * @public
     * @param {math.Vec2} v - Another vector.
     * @returns {math.Vec2}
     */
    cross(v) {
        return this.x * v.y - this.y * v.x;
    }

    /**
     * Sets vector to zero.
     * @public
     * @returns {math.Vec2}
     */
    clear() {
        this.x = this.y = 0;
        return this;
    }

    /**
     * Returns normalized vector.
     * @public
     * @returns {math.Vec2}
     */
    normal() {
        var res = new Vec2();
        res.copy(this);

        var length = 1.0 / res.length();

        res.x *= length;
        res.y *= length;

        return res;
    }

    /**
     * Normalize current vector.
     * @public
     * @returns {math.Vec2}
     */
    normalize() {
        var length = 1.0 / this.length();

        this.x *= length;
        this.y *= length;

        return this;
    }

    /**
     * Converts vector to a number array.
     * @public
     * @returns {Array.<number>} - (exactly 2 entries)
     */
    toVec() {
        return [this.x, this.y];
    }

    /**
     * Gets distance to point.
     * @public
     * @param {math.Vec2} p - Distant point.
     * @returns {number}
     */
    distance(p) {
        var vec = Vec2.sub(this, p);
        return vec.length();
    }

    /**
     * Sets vector's values.
     * @public
     * @param {number} x - Value X.
     * @param {number} y - Value Y.
     * @returns {math.Vec2}
     */
    set(x, y) {
        this.x = x;
        this.y = y;
        return this;
    }

    /**
     * Negate current vector.
     * @public
     * @returns {math.Vec2}
     */
    negate() {
        this.x = -this.x;
        this.y = -this.y;
        return this;
    }

    /**
     * Negate current vector to another instance.
     * @public
     * @returns {math.Vec2}
     */
    negateTo() {
        return new Vec2(-this.x, -this.y);
    }

    /**
     * Gets projected point coordinates of the current vector on the ray.
     * @public
     * @param {math.Vec2} pos - Ray position.
     * @param {math.Vec2} direction - Ray direction.
     * @returns {math.Vec2}
     */
    projToRay(pos, direction) {
        var v = Vec2.proj_b_to_a(Vec2.sub(this, pos), direction);
        v.add(pos);
        return v;
    }

    /**
     * Gets angle between two vectors.
     * @public
     * @param {math.Vec2} a - Another vector.
     * @returns {number}
     */
    angle(a) {
        return Vec2.angle(this, a);
    }

    /**
     * Returns two vectors linear interpolation.
     * @public
     * @param {math.Vec2} v2 - End vector.
     * @param {number} l - Interpolate value.
     * @returns {math.Vec2}
     */
    lerp(v1, v2, l) {
        var res = Vec2.clone(this);
        if (l <= 0.0) {
            res.copy(v1);
        } else if (l >= 1.0) {
            res.copy(v2);
        } else {
            res = Vec2.add(v1, Vec2.sub(v2, v1).scale(l));
        }
        return res;
    }

    static get LERP_DELTA() {
        return 1e-6;
    }

    /**
     * Spherically interpolates between two vectors.
     * Interpolates between current and v2 vector by amount t. The difference between this and linear interpolation (aka, "lerp") is that
     * the vectors are treated as directions rather than points in space. The direction of the returned vector is interpolated
     * by the angle and its magnitude is interpolated between the magnitudes of from and to.
     * @public
     * @param {math.Vec2} v2 -
     * @param {number} t - The parameter t is clamped to the range [0, 1].
     * @returns {math.Vec2}
     */
    slerp(v2, t) {
        var res = new Vec2();

        if (t <= 0.0) {
            res.copy(this);
            return;
        } else if (t >= 1.0) {
            res.copy(v2);
            return;
        }

        var omega, sinom, scale0, scale1;
        var cosom = this.dot(v2);

        if (1.0 - cosom > Vec2.LERP_DELTA) {
            omega = Math.acos(cosom);
            sinom = Math.sin(omega);
            scale0 = Math.sin((1.0 - t) * omega) / sinom;
            scale1 = Math.sin(t * omega) / sinom;
        } else {
            scale0 = 1.0 - t;
            scale1 = t;
        }

        return Vec2.add(this.scale(scale0), v2.scale(scale1));
    }
}

/**
 * Vector 2d object creator.
 * @function
 * @param {number} [x] - First cvalue.
 * @param {number} [y] - Second value.
 * @returns {math.Vec2}
 */
export function vec2(x, y) {
    return new Vec2(x, y);
}