Source: vector3d.js

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/* Vector handling functions                                          (c) Chris Veness 2011-2016  */
/*                                                                                   MIT Licence  */
/* www.movable-type.co.uk/scripts/geodesy/docs/module-vector3d.html                               */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */


/**
 * Library of 3-d vector manipulation routines.
 *
 * @module vector3d
 */


/* Vector3d - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


/**
 * Functions for manipulating generic 3-d vectors.
 *
 * Functions return vectors as return results, so that operations can be chained.
 * @example var v = v1.cross(v2).dot(v3) // ≡ v1×v2⋅v3
 */
class Vector3d { // note prototype-based class not inheritance-based class

    /**
     * Creates a 3-d vector.
     *
     * @param {number} x - X component of vector.
     * @param {number} y - Y component of vector.
     * @param {number} z - Z component of vector.
     *
     * @example
     *   import Vector3d from 'vector3d';
     *   var v = new Vector3d(0.267, 0.535, 0.802);
     */
    constructor(x, y, z) {
        this.x = Number(x);
        this.y = Number(y);
        this.z = Number(z);
    }


    /**
     * Adds supplied vector to ‘this’ vector.
     *
     * @param   {Vector3d} v - Vector to be added to this vector.
     * @returns {Vector3d} Vector representing sum of this and v.
     */
    plus(v) {
        if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');

        return new Vector3d(this.x + v.x, this.y + v.y, this.z + v.z);
    }


    /**
     * Subtracts supplied vector from ‘this’ vector.
     *
     * @param   {Vector3d} v - Vector to be subtracted from this vector.
     * @returns {Vector3d} Vector representing difference between this and v.
     */
    minus(v) {
        if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');

        return new Vector3d(this.x - v.x, this.y - v.y, this.z - v.z);
    }


    /**
     * Multiplies ‘this’ vector by a scalar value.
     *
     * @param   {number}   x - Factor to multiply this vector by.
     * @returns {Vector3d} Vector scaled by x.
     */
    times(x) {
        x = Number(x);

        return new Vector3d(this.x * x, this.y * x, this.z * x);
    }


    /**
     * Divides ‘this’ vector by a scalar value.
     *
     * @param   {number}   x - Factor to divide this vector by.
     * @returns {Vector3d} Vector divided by x.
     */
    dividedBy(x) {
        x = Number(x);

        return new Vector3d(this.x / x, this.y / x, this.z / x);
    }


    /**
     * Multiplies ‘this’ vector by the supplied vector using dot (scalar) product.
     *
     * @param   {Vector3d} v - Vector to be dotted with this vector.
     * @returns {number} Dot product of ‘this’ and v.
     */
    dot(v) {
        if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');

        return this.x * v.x + this.y * v.y + this.z * v.z;
    }


    /**
     * Multiplies ‘this’ vector by the supplied vector using cross (vector) product.
     *
     * @param   {Vector3d} v - Vector to be crossed with this vector.
     * @returns {Vector3d} Cross product of ‘this’ and v.
     */
    cross(v) {
        if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');

        var x = this.y * v.z - this.z * v.y;
        var y = this.z * v.x - this.x * v.z;
        var z = this.x * v.y - this.y * v.x;

        return new Vector3d(x, y, z);
    }


    /**
     * Negates a vector to point in the opposite direction
     *
     * @returns {Vector3d} Negated vector.
     */
    negate() {
        return new Vector3d(-this.x, -this.y, -this.z);
    }


    /**
     * Length (magnitude or norm) of ‘this’ vector
     *
     * @returns {number} Magnitude of this vector.
     */
    get length() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }


    /**
     * Normalizes a vector to its unit vector
     * – if the vector is already unit or is zero magnitude, this is a no-op.
     *
     * @returns {Vector3d} Normalised version of this vector.
     */
    unit() {
        var norm = this.length;
        if (norm == 1) return this;
        if (norm == 0) return this;

        var x = this.x / norm;
        var y = this.y / norm;
        var z = this.z / norm;

        return new Vector3d(x, y, z);
    }


    /**
     * Calculates the angle between ‘this’ vector and supplied vector.
     *
     * @param   {Vector3d} v
     * @param   {Vector3d} [vSign] - If supplied (and out of plane of this and v), angle is signed +ve if
     *   this->v is clockwise looking along vSign, -ve in opposite direction (otherwise unsigned angle).
     * @returns {number} Angle (in radians) between this vector and supplied vector.
     */
    angleTo(v, vSign) {
        if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');

        var sinθ = this.cross(v).length;
        var cosθ = this.dot(v);

        if (vSign !== undefined) {
            if (!(vSign instanceof Vector3d)) throw new TypeError('vSign is not Vector3d object');
            // use vSign as reference to get sign of sinθ
            sinθ = this.cross(v).dot(vSign) < 0 ? -sinθ : sinθ;
        }

        return Math.atan2(sinθ, cosθ);
    }


    /**
     * Rotates ‘this’ point around an axis by a specified angle.
     *
     * @param   {Vector3d} axis - The axis being rotated around.
     * @param   {number}   angle - The angle of rotation (in degrees).
     * @returns {Vector3d} The rotated point.
     */
    rotateAround(axis, angle) {
        if (!(axis instanceof Vector3d)) throw new TypeError('axis is not Vector3d object');

        var θ = angle.toRadians();

        // en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
        // en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
        var p = this.unit();
        var a = axis.unit();

        var s = Math.sin(θ);
        var c = Math.cos(θ);
        var t = 1-c;
        var x = a.x, y = a.y, z = a.z;

        var r = [ // rotation matrix for rotation about supplied axis
            [ t*x*x + c,   t*x*y - s*z, t*x*z + s*y ],
            [ t*x*y + s*z, t*y*y + c,   t*y*z - s*x ],
            [ t*x*z - s*y, t*y*z + s*x, t*z*z + c   ],
        ];

        // multiply r × p
        var rp = [
            r[0][0]*p.x + r[0][1]*p.y + r[0][2]*p.z,
            r[1][0]*p.x + r[1][1]*p.y + r[1][2]*p.z,
            r[2][0]*p.x + r[2][1]*p.y + r[2][2]*p.z,
        ];
        var p2 = new Vector3d(rp[0], rp[1], rp[2]);

        return p2;
        // qv en.wikipedia.org/wiki/Rodrigues'_rotation_formula...
    }


    /**
     * String representation of vector.
     *
     * @param   {number} [dp=3] - Number of decimal places to be used.
     * @returns {string} Vector represented as [x,y,z].
     */
    toString(dp=3) {
        var str = '[' + this.x.toFixed(dp) + ',' + this.y.toFixed(dp) + ',' + this.z.toFixed(dp) + ']';

        return str;
    }

}


export default Vector3d

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */