UNPKG

atum

Version:

A Procedural Graph Based Javascript Library

475 lines (433 loc) 13 kB
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;