/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* 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
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */