atum
Version:
A Procedural Graph Based Javascript Library
475 lines (433 loc) • 13 kB
JavaScript
class Vector {
/**
* @class Vector
*
* This is a basic vector class that is used for geometry, position inforamtion,
* movement infomation, and more complex structures.
* The vector class follows a immutable paradigm where changes are not made to the
* vectors themselves. Any change to a vector is returned as a new vector that
* must be captured.
*
* @description This vector class was constructed so that it can mirror two types of common
* point/vector type objects. This is having object properties stored as object
* properties (eg. vector.x, vector.y) or as list properties, [x, y] which can
* be accessed by vector[0], or vector[1].
*
* @summary Create a 2D Vector object
*
* @property {number} x The x vector component
* @property {number} y The y vector component
* @property {number} 0 The x vector component
* @property {number} 1 The y vector component
*
* @param {number|Vector} x The x component or another vector
* @param {number} [y] The y component
*/
constructor(x, y) {
if (x instanceof Vector || (x.x && x.y) && !y) {
this._set(x.x, x.y);
} else {
this._set(x, y);
}
}
//---- Helper Functions ----
/**
* Internal Helper Function for setting variable properties
*
* @private
* @param {number} x The x component
* @param {number} y The y component
* @memberof Vector
*/
_set(x, y) {
this.__proto__[0] = x;
this.__proto__[1] = y;
this.x = x;
this.y = y;
}
/**
* Get the vector key:Symbol representation
*
* @returns {Symbol} The vector key element
* @memberof Vector
*/
key() {
return this.list();
// return Symbol(this.list()); // Not currently working as a key symbol
}
/**
* Get the vector in list form
*
* @returns {number[]} List representation of the vector of length 2
* @memberof Vector
*/
list() {
return [this.x, this.y];
}
/**
* Returns the vector as a string of (x, y)
*
* @returns {string} The string representation of a vector in (x, y) form
* @memberof Vector
*/
toString() {
return `(${this.x}, ${this.y})`;
}
/**
* Get a copy of the input vector
*
* @param {Vector} v the vector to be coppied
* @returns {Vector} The vector copy
* @memberof Vector
*/
static copy(v) {
return new Vector(v.x, v.y);
}
/**
* Returns the vector as a string of (x, y)
*
* @returns {string} The string representation of a vector in (x, y) form
* @memberof Vector
*/
toString() {
return `(${this.x}, ${this.y})`;
}
//---- Basic Math Functions ----
/**
* Add two vectors element wise
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns {Vector} The vector result of adding the two vectors
* @memberof Vector
*/
static add(a, b) {
return new Vector(a.x + b.x, a.y + b.y);
}
/**
* Add this vector with another vector element wise
*
* @param {Vector} other The other vector
* @returns {Vector} The vector result of adding the two vectors
* @memberof Vector
*/
add(other) {
return Vector.add(this, other);
}
/**
* Subtract two vectors element wise
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second Vector
* @returns {Vector} The vector result of subtracting the two vectors
* @memberof Vector
*/
static subtract(a, b) {
return new Vector(a.x - b.x, a.y - b.y);
}
/**
* Subtract this vector with another vector element wise
*
* @param {Vector} other The other vector
* @returns {Vector} The vector result of subtracting the two vectors
* @memberof Vector
*/
subtract(other) {
return Vector.subtract(this, other);
}
/**
* Multiply the vector by a scalar value
*
* @param {number} scalar The number to multiply the vector by
* @returns {Vector} The result of multiplying the vector by a scalar
* element wise
* @memberof Vector
*/
multiply(scalar) {
return new Vector(this.x * scalar, this.y * scalar);
}
/**
* Divide the vector by a scalar value
*
* @param {number} scalar
* @returns {Vector} The result of multiplying the vector by a scalar
* @memberof Vector
*/
divide(scalar) {
return new Vector(this.x / scalar, this.y / scalar);
}
//---- Advanced Vector Functions ----
/**
* Get the magnitude of the vector
*
* @returns {number} The magniture of the vector
* @memberof Vector
*/
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
// Get the unit vector
/**
* Get the normal vector of the current vector.
*
* @returns {Vector} A vector that is the normal compenent of the vector
* @memberof Vector
*/
normalize() {
return Vector.divide(this.magnitude());
}
/**
* Get the get the current vector rotated by a certain ammount
*
* @param {number} radians
* @returns {Vector} The vector that results from rotating the current
* vector by a particular ammount
* @memberof Vector
*/
rotate(radians) {
const c = Math.cos(radians);
const s = Math.sin(radians);
return new Vector(c * this.x - s * this.y, s * this.x + c * this.y);
}
/**
* Get the dot product of two vectors
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns {number} The dot product of the two vectors
* @memberof Vector
*/
static dot(a, b) {
return a.x * b.x + a.y * b.y;
}
/**
* Get the average location between several vectors
*
* @param {Vector[]} vectors The list of vectors to average
* @memberof Vector
*/
static avg(vectors) {
let average = Vector.zero();
for (const vector of vectors) {
average = Vector.add(average, vector);
}
return average.divide(vectors.length);
}
/**
* Get the dot product of this vector and another vector
*
* @param {Vector} other The other vector
* @returns {number} The dot product of this and the other vector
* @memberof Vector
*/
dot(other) {
return Vector.dot(this, other);
}
/**
* Get the cross product of two vectors
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns {number} The cross product of the two vectors
* @memberof Vector
*/
static cross(a, b) {
return a.x * b.y - a.y * b.x;
}
/**
* Get the cross product of this and the other vector
*
* @param {Vector} other The other vector
* @returns {number} The cross product of this and the other vector
* @memberof Vector
*/
cross(other) {
return Vector.cross(this, other);
}
//---- Purely Static Vector Functions ----
/**
* Get the midpoint between two vectors
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns The midpoint of two vectors
* @memberof Vector
*/
static midpoint(a, b) {
return new Vector((a.x + b.x) / 2, (a.y + b.y) / 2);
}
/**
* Get the projection of vector a onto vector b
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns The projection vector of a onto b
* @memberof Vector
*
* @todo Add assertion for non-zero length b vector
*/
static proj(a, b) {
return b.multiply(Vector.dot(a, b) / Math.pow(b.magnitude(), 2));
}
/**
* Get the angle between two vectors
*
* @static
* @param {Vector} a The frist vector
* @param {Vector} b The second vector
* @returns The angle between vector a and vector b
* @memberof Vector
*/
static angle(a, b) {
return Math.acos(Vector.dot(a, b) / (a.magnitude() * b.magnitude()));
}
/**
* Get the euclidean distance between two vectors
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns The euclidean distance between a and b
* @see {@link dist2}
* @memberof Vector
*/
static distance(a, b) {
return Math.sqrt(Vector.dist2(a, b));
}
/**
* Get the euclidean distnace squared between two vectors.
* This is used as a helper for the distnace function but can be used
* to save on speed by not doing the square root operation.
*
* @static
* @param {Vector} a The first vector
* @param {Vector} b The second vector
* @returns The euclidean distance squared between vector a and vector b
* @see {@link distnace}
* @memberof Vector
*/
static dist2(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return dx * dx + dy * dy;
}
/**
* Get the shortest distance between the point p and the line
* segment v to w.
*
* @static
* @param {Vector} p The vector point
* @param {Vector} v The first line segment endpoint
* @param {Vector} w The second line segment endpoint
* @returns The shortest euclidean distance between point
* @see {@link distToSeg2}
* @see {@link http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment}
* @memberof Vector
*/
static distToSeg(p, v, w) {
return Math.sqrt(Vector.distToSeg2(p, v, w));
}
/**
* Get the shortest distance squared between the point p and the line
* segment v to w.
*
* @static
* @param {Vector} p The vector point
* @param {Vector} v The first line segment endpoint
* @param {Vector} w The second line segment endpoint
* @returns The shortest euclidean distance squared between point
* @see {@link distToSeg}
* @see {@link http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment}
* @memberof Vector
*/
static distToSegSquared(p, v, w) {
const l = Vector.dist2(v, w);
if (l === 0) { return Vector.dist2(p, v); }
let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l;
t = Math.max(0, Math.min(1, t));
return Vector.dist2(p, new Vector(v.x + t * (w.x - v.x),
v.y + t * (w.y - v.y)));
}
/**
* Get the two normal vectors that are perpendicular to the current vector
*
* @returns {Vector[]} The two normal vectors that are perpendicular
* to the vector. The first vector is the normal vector that is +90 deg or
* +PI/2 rad. The second vector is the noraml vector that is -90 deg or
* -PI/2 rad.
* @memberof Vector
*/
perpendiculars() {
const plus90 = new Vector(-this.y, this.x).normalize();
const minus90 = new Vector(this.y, -this.x).normalize();
return [plus90, minus90];
}
//---- Standard Static Vector Objects ----
/**
* Get a vector of no magnitude and no direction
*
* @static
* @function
* @returns {Vector} Vector of magnitude zero
* @memberof Vector
*/
static zero() {
"use strict";
return new Vector(0, 0);
}
/**
* Get the unit vector pointing in the positive y direction
*
* @static
* @function
* @returns {Vector} Unit vector pointing up
* @memberof Vector
*/
static up() {
"use strict";
return new Vector(0, 1);
}
/**
* Get the unit vector pointing in the negative y direction
*
* @static
* @function
* @returns {Vector} Unit vector pointing down
* @memberof Vector
*/
static down() {
"use strict";
return new Vector(0, -1);
}
/**
* Get the unit vector pointing in the negative x direction
*
* @static
* @function
* @returns {Vector} Unit vector pointing right
* @memberof Vector
*/
static left() {
"use strict";
return new Vector(-1, 0);
}
/**
* Get the unit vector pointing in the positive x direction
*
* @static
* @function
* @returns {Vector} Unit vector pointing right
* @memberof Vector
*/
static right() {
"use strict";
return new Vector(1, 0);
}
}
export default Vector;