Source: math/Vector4.js

import { vec4 } from 'gl-matrix';
import Class from '../core/Class';

/**
 * 四维向量
 * @class
 */
const Vector4 = Class.create(/** @lends Vector4.prototype */ {
    /**
     * 类名
     * @type {String}
     * @default Vector4
     */
    className: 'Vector4',
    /**
     * @type {Boolean}
     * @default true
     */
    isVector4: true,
    /**
     * Creates a new empty vec4
     * @param {Number} [x=0] X component
     * @param {Number} [y=0] Y component
     * @param {Number} [z=0] Z component
     * @param {Number} [w=0] W component
     * @constructs
     */
    constructor(x = 0, y = 0, z = 0, w = 0) {
        /**
         * 数据
         * @type {Float32Array}
         */
        this.elements = vec4.fromValues(x, y, z, w);
    },
    /**
     * Copy the values from one vec4 to this
     * @param  {Vector4} m the source vector
     * @return {Vector4} this
     */
    copy(v) {
        vec4.copy(this.elements, v.elements);
        return this;
    },
    /**
     * Creates a new vec4 initialized with values from this vector
     * @return {Vector4} a new Vector4
     */
    clone() {
        const elements = this.elements;
        return new this.constructor(elements[0], elements[1], elements[2], elements[3]);
    },
    /**
     * 转换到数组
     * @param  {number[]|TypedArray}  [array=[]] 数组
     * @param  {Number} [offset=0] 数组偏移值
     * @return {Array}
     */
    toArray(array = [], offset = 0) {
        const elements = this.elements;
        array[0 + offset] = elements[0];
        array[1 + offset] = elements[1];
        array[2 + offset] = elements[2];
        array[3 + offset] = elements[3];
        return array;
    },
    /**
     * 从数组赋值
     * @param  {number[]|TypedArray} array  数组
     * @param  {Number} [offset=0] 数组偏移值
     * @return {this}
     */
    fromArray(array, offset = 0) {
        const elements = this.elements;
        elements[0] = array[offset + 0];
        elements[1] = array[offset + 1];
        elements[2] = array[offset + 2];
        elements[3] = array[offset + 3];
        return this;
    },
    /**
     * Set the components of a vec4 to the given values
     * @param {Number} x X component
     * @param {Number} y Y component
     * @param {Number} z Z component
     * @param {Number} w W component
     * @returns {Vector4} this
     */
    set(x, y, z, w) {
        vec4.set(this.elements, x, y, z, w);
        return this;
    },
    /**
     * Adds two vec4's
     * @param {Vector4} a
     * @param {Vector4} [b] 如果不传,计算 this 和 a 的和
     * @returns {Vector4} this
     */
    add(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.add(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Subtracts vector b from vector a
     * @param {Vector4} a
     * @param {Vector4} [b] 如果不传,计算 this 和 a 的差
     * @returns {Vector4} this
     */
    subtract(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.subtract(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Multiplies two vec4's
     * @param {Vector4} a
     * @param {Vector4} [b] 如果不传,计算 this 和 a 的积
     * @returns {Vector4} this
     */
    multiply(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.multiply(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Divides two vec4's
     * @param {Vector4} a
     * @param {Vector4} [b] 如果不传,计算 this 和 a 的商
     * @returns {Vector4} this
     */
    divide(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.divide(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Math.ceil the components of this
     * @returns {Vector4} this
     */
    ceil() {
        vec4.ceil(this.elements, this.elements);
        return this;
    },
    /**
     * Math.floor the components of this
     * @returns {Vector4} this
     */
    floor() {
        vec4.floor(this.elements, this.elements);
        return this;
    },
    /**
     * Returns the minimum of two vec4's
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @returns {Vector4} this
     */
    min(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.min(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Returns the maximum of two vec4's
     * @param  {Vector4} a
     * @param  {Vector4} [b]  如果不传,计算 this 和 a 的结果
     * @returns {Vector4} this
     */
    max(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.max(this.elements, a.elements, b.elements);
        return this;
    },
    /**
     * Math.round the components of this
     * @returns {Vector4} this
     */
    round() {
        vec4.round(this.elements, this.elements);
        return this;
    },
    /**
     * Scales this by a scalar number
     * @param  {Number} scale amount to scale the vector by
     * @returns {Vector4} this
     */
    scale(scale) {
        vec4.scale(this.elements, this.elements, scale);
        return this;
    },
    /**
     * Adds two vec4's after scaling the second vector by a scalar value
     * @param  {Number} scale the amount to scale the second vector by before adding
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @returns {Vector4} this
     */
    scaleAndAdd(scale, a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        vec4.scaleAndAdd(this.elements, a.elements, b.elements, scale);
        return this;
    },
    /**
     * Calculates the euclidian distance between two vec4's
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @return {Number} distance between a and b
     */
    distance(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        return vec4.distance(a.elements, b.elements);
    },
    /**
     * Calculates the squared euclidian distance between two vec4's
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @return {Number} squared distance between a and b
     */
    squaredDistance(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        return vec4.squaredDistance(a.elements, b.elements);
    },
    /**
     * Calculates the length of this
     * @return {Number} length of this
     */
    length() {
        return vec4.length(this.elements);
    },
    /**
     * Calculates the squared length of this
     * @return {Number} squared length of this
     */
    squaredLength() {
        return vec4.squaredLength(this.elements);
    },
    /**
     * Negates the components of this
     * @returns {Vector4} this
     */
    negate() {
        vec4.negate(this.elements, this.elements);
        return this;
    },
    /**
     * Returns the inverse of the components of a vec4
     * @param  {Vector4} [a=this]
     * @returns {Vector4} this
     */
    inverse(a) {
        if (!a) {
            a = this;
        }
        vec4.inverse(this.elements, a.elements);
        return this;
    },
    /**
     * Normalize this
     * @returns {Vector4} this
     */
    normalize() {
        vec4.normalize(this.elements, this.elements);
        return this;
    },
    /**
     * Calculates the dot product of two vec4's
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @return {Number}  product of a and b
     */
    dot(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        return vec4.dot(a.elements, b.elements);
    },
    /**
     * Performs a linear interpolation between two vec4's
     * @param  {Vector4} v
     * @param  {Number} t interpolation amount between the two vectors
     * @returns {Vector4} this
     */
    lerp(v, t) {
        vec4.lerp(this.elements, this.elements, v.elements, t);
        return this;
    },
    /**
     * Generates a random vector with the given scale
     * @param  {Number} [scale=1] Length of the resulting vector. If ommitted, a unit vector will be returned
     * @returns {Vector4} this
     */
    random(scale) {
        scale = scale || 1;
        this.elements[0] = Math.random();
        this.elements[1] = Math.random();
        this.elements[2] = Math.random();
        this.elements[3] = Math.random();
        this.normalize();
        this.scale(scale);
        return this;
    },
    /**
     * Transforms the vec4 with a mat4
     * @param  {Matrix4} m matrix to transform with
     * @returns {Vector4} this
     */
    transformMat4(m) {
        vec4.transformMat4(this.elements, this.elements, m.elements);
        return this;
    },
    /**
     * Transforms the vec4 with a quat
     * @param  {Quaternion} q quaternion to transform with
     * @returns {Vector4} this
     */
    transformQuat(q) {
        vec4.transformQuat(this.elements, this.elements, q.elements);
        return this;
    },
    /**
     * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @return {Boolean} True if the vectors are equal, false otherwise.
     */
    exactEquals(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        return vec4.exactEquals(a.elements, b.elements);
    },
    /**
     * Returns whether or not the vectors have approximately the same elements in the same position.
     * @param  {Vector4} a
     * @param  {Vector4} [b] 如果不传,计算 this 和 a 的结果
     * @return {Boolean} True if the vectors are equal, false otherwise.
     */
    equals(a, b) {
        if (!b) {
            b = a;
            a = this;
        }
        return vec4.equals(a.elements, b.elements);
    },
    /**
     * X component
     * @type {Number}
     */
    x: {
        get() {
            return this.elements[0];
        },
        set(value) {
            this.elements[0] = value;
        }
    },
    /**
     * Y component
     * @type {Number}
     */
    y: {
        get() {
            return this.elements[1];
        },
        set(value) {
            this.elements[1] = value;
        }
    },
    /**
     * Z component
     * @type {Number}
     */
    z: {
        get() {
            return this.elements[2];
        },
        set(value) {
            this.elements[2] = value;
        }
    },
    /**
     * W component
     * @type {Number}
     */
    w: {
        get() {
            return this.elements[3];
        },
        set(value) {
            this.elements[3] = value;
        }
    }
});

/**
 * Alias for {@link Vector4#subtract}
 * @function
 */
Vector4.prototype.sub = Vector4.prototype.subtract;

/**
 * Alias for {@link Vector4#multiply}
 * @function
 */
Vector4.prototype.mul = Vector4.prototype.multiply;

/**
 * Alias for {@link Vector4#divide}
 * @function
 */
Vector4.prototype.div = Vector4.prototype.divide;

/**
 * Alias for {@link Vector4#distance}
 * @function
 */
Vector4.prototype.dist = Vector4.prototype.distance;

/**
 * Alias for {@link Vector4#squaredDistance}
 * @function
 */
Vector4.prototype.sqrDist = Vector4.prototype.squaredDistance;

/**
 * Alias for {@link Vector4#length}
 * @function
 */
Vector4.prototype.len = Vector4.prototype.length;

/**
 * Alias for {@link Vector4#squaredLength}
 * @function
 */
Vector4.prototype.sqrLen = Vector4.prototype.squaredLength;

export default Vector4;