/**
* @module Vec2
* @fileOverview Contains Vec2 class.
*/
/**
* @class
* A two-dimensional vector. Used to represent coordinates in 2D space.
*/
class Vec2 {
/**
* Create a new Vec2 from ambiguous arguments.
* @param {number|number[]|Vec2} args Arguments to create vector from
* @returns {Vec2} Resulting vector
* @constructor
*/
static from(...args) {
if (args.length === 1) {
if (typeof args[0] === "number")
return new Vec2(args[0], args[0]);
else if (args[0] instanceof Array)
return this.fromArray(args[0]);
else if (args[0] instanceof Vec2)
return args[0].copy();
} else if (args.length === 2)
return new Vec2(args[0], args[1]);
return new Vec2();
}
/**
* Create a new Vec2 from an array.
* @param {number[2]} array Array to create vector from
* @returns {Vec2} Resulting vector
*/
static fromArray(array) {
return new Vec2(array[0], array[1]);
}
/**
* Create a new Vec2 from an angle
* @param {number} rads Angle in radians
* @returns {Vec2} Resulting vector
*/
static fromAngle(rads) {
return new Vec2(Math.cos(rads), Math.sin(rads)).norm();
}
/**
* Create a random Vec2 within a specific radius.
* @param {number} radius Radius to generate in
* @returns {Vec2} Resulting random vector
*/
static random(radius = 1) {
return new Vec2(Math.random(), Math.random()).norm().mulv(Math.random() * radius);
}
/**
* Create a random Vec2 on the edge of a specific radius.
* @param {number} radius Radius to generate on
* @returns {Vec2} Resulting random vector
*/
static randomOuter(radius = 1) {
return new Vec2(
Math.random() - 0.5,
Math.random() - 0.5
).norm().mulv(radius);
}
/**
* Given multiple vectors, find the lowest coordinates in both directions.
* @param {...Vec2} vectors Vectors to compare
* @returns {Vec2} Resulting minimum vector
*/
static min(...vectors) {
let m = null;
for (const vector of vectors) {
if (m === null) {
m = vector.copy();
continue;
}
m.x = Math.min(m.x, vector.x);
m.y = Math.min(m.y, vector.y);
}
if (m === null)
return new Vec2();
return m;
}
/**
* Given multiple vectors, find the highest coordinates in both directions.
* @param {...Vec2} vectors Vectors to compare
* @returns {Vec2} Resulting maximum vector
*/
static max(...vectors) {
let m = null;
for (const vector of vectors) {
if (m === null) {
m = vector.copy();
continue;
}
m.x = Math.max(m.x, vector.x);
m.y = Math.max(m.y, vector.y);
}
if (m === null)
return new Vec2();
return m;
}
/**
* Create a new Vec2.
* @param {number} x X-value of the vector
* @param {number} y Y-value of the vector
*/
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
/**
* Get an exact copy of the vector.
* @returns {Vec2} Copy of vector
*/
copy() {
return new Vec2(this.x, this.y);
}
//
// Addition & Subtraction
//
/**
* Add two vectors together.
* @param {Vec2} other Other vector to add
* @returns {Vec2} New Vec2 with resulting values
*/
add(other) {
return new Vec2(this.x + other.x, this.y + other.y);
}
/**
* Add x and y values to the vector.
* @param {number} x X-value to add
* @param {number} y Y-value to add
* @returns {Vec2} New Vec2 with resulting values
*/
addv(x, y) {
return new Vec2(this.x + x, this.y + y);
}
/**
* Subtract a vector from another.
* @param {Vec2} other Other vector to subtract by
* @returns {Vec2} New Vec2 with resulting values
*/
sub(other) {
return new Vec2(this.x - other.x, this.y - other.y);
}
/**
* Subtract x and y values from the vector.
* @param {number} x X-value to subtract
* @param {number} y Y-value to subtract
* @returns {Vec2} New Vec2 with resulting values
*/
subv(x, y) {
return new Vec2(this.x - x, this.y - y);
}
//
// Multiplication & Division
//
/**
* Multiply two vectors together.
* @param {Vec2} other Other vector to multiply by
* @returns {Vec2} New Vec2 with resulting values
*/
mul(other) {
return new Vec2(this.x * other.x, this.y * other.y);
}
/**
* Multiply the vector by a scalar.
* @param {number} val Scalar value
* @returns {Vec2} New Vec2 with scaled values
*/
mulv(val) {
return new Vec2(this.x * val, this.y * val);
}
/**
* Divide the vector by another.
* @param {Vec2} other Other vector to divide by
* @returns {Vec2} New Vec2 with resulting values
*/
div(other) {
return new Vec2(this.x / other.x, this.y / other.y);
}
/**
* Divide the vector by a scalar.
* @param {number} val Scalar value
* @returns {Vec2} New Vec2 with scaled values
*/
divv(val) {
return new Vec2(this.x / val, this.y / val);
}
//
// Calculations & Conversions
//
/**
* Get the magnitude/length of the vector.
* @returns {number} Magnitude of vector
*/
mag() {
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
}
/**
* Get the distance (magnitude) from the vector to another.
* @param {Vec2} other Other Vec2 to get distance to
* @returns {number} Distance between the two vectors
*/
dist(other) {
return this.sub(other).mag();
}
/**
* Get the dot product of the vector with another.
* @param {Vec2} other Other Vec2 to use
* @returns {number} Dot product of the two vectors
*/
dot(other) {
return this.x * other.x + this.y * other.y;
}
/**
* Get the dot product of the vector with specific x and y values.
* @param {number} x X-value to use
* @param {number} y Y-value to use
* @returns {number} Resulting dot product
*/
dotv(x, y) {
return this.x * x + this.y * y;
}
/**
* Get the angle of the vector (in radians).
* @returns {number} Angle of vector
*/
angle() {
if (this.isZero())
return 0;
return Math.atan2(this.y, this.x);
}
/**
* Get a new vector by rotating (in radians) the existing vector.
* @param {number} rad Radians to rotate by
* @returns {Vec2} Resulting rotated vector
*/
rot(rad) {
const d = this.mag();
const angle = this.angle();
return new Vec2(Math.cos(angle + rad) * d, Math.sin(angle + rad) * d);
}
/**
* Get the normalization of the vector.
* @returns {Vec2} Normalized version of the vector
*/
norm() {
const mag = this.mag();
if (mag === 0)
return new Vec2();
return this.divv(mag);
}
/**
* Get a vector denoting the signs of each axis.
* @returns {Vec2} Sign vector
*/
sign() {
return new Vec2(Math.sign(this.x), Math.sign(this.y));
}
/**
* Invert the vector (inverts the signs of the values).
* @returns {Vec2} Inverted Vec2
*/
invert() {
return new Vec2(-this.x, -this.y);
}
/**
* Calculate the perpendicular vector.
* @returns {Vec2} Perpendicular vector
*/
perp() {
return new Vec2(-this.y, this.x);
}
/**
* Get the normal vector from another vector.
* @param {Vec2} other Other Vec2 to use
* @returns {Vec2} Resulting normal vector
*/
normWith(other) {
return this.sub(other).norm().perp();
}
/**
* Floor the values of the vector. Can be used to round to a specified precision.
* @param {number} v (Optional) Multiplies values by this value before flooring, then divides it afterwords.
* Useful for rounding to a specific precision or specific decimal places.
* @returns {Vec2} Resulting floored Vec2
*/
floor(v = 1) {
return new Vec2(Math.floor(this.x / v) * v, Math.floor(this.y / v) * v);
}
/**
* Ceiling the values of the vector. Can be used to round to a specified precision.
* @param {number} v (Optional) Multiplies values by this value before ceiling, then divides it afterwords.
* Useful for rounding to a specific precision or specific decimal places.
* @returns {Vec2} Resulting rounded up Vec2
*/
ceil(v = 1) {
return new Vec2(Math.ceil(this.x / v) * v, Math.ceil(this.y / v) * v);
}
/**
* Convert the Vec2 to a standard array.
* @returns {number[]} Vector as an array
*/
toArray() {
return [
this.x,
this.y
];
}
/**
* Convert the Vec2 to a gl-matrix array.
* @returns {number[]} Gl-matrix array
*/
toGlArray() {
const vector = vec2.create();
vec2.set(vector, this.x, this.y);
return vector;
}
/**
* Transform the vector using a gl-matrix matrix.
* @param matrix Matrix to transform with
* @returns {Vec2} Transformed vector
*/
transform(matrix) {
const vector = this.toGlArray();
vec2.transformMat3(vector, vector, matrix);
return Vec2.fromArray(vector);
}
/**
* Inversely transform the vector using a gl-matrix matrix.
* @param matrix Matrix to inversely transform with
* @returns {Vec2} Inversely transformed vector
*/
inverseTransform(matrix) {
mat3.invert(matrix, matrix);
return this.transform(matrix);
}
/**
* Convert to a translation matrix.
* @returns Translation matrix
*/
toTranslationMatrix() {
const vector = this.toGlArray();
const matrix = mat3.create();
mat3.fromTranslation(matrix, vector);
return matrix;
}
/**
* Convert to a scaling matrix.
* @returns Scaling matrix
*/
toScaleMatrix() {
const vector = this.toGlArray();
const matrix = mat3.create();
mat3.fromScaling(matrix, vector);
return matrix;
}
/**
* Project onto another vector.
* @param {Vec2} other Vector to project onto
* @returns {Vec2} Projected vector
*/
project(other) {
return other.mulv(this.dot(other) / other.dot(other));
}
/**
* Mirror over another vector.
* @param {Vec2} other Vector to mirror over
* @returns {Vec2} Mirrored vector
*/
mirrorOver(other) {
return this.project(other).mulv(2).sub(this);
}
//
// Checks
//
/**
* Check if the vector is zero or not (both values equal to zero).
* @returns {boolean} Whether the vector is zero
*/
isZero() {
return this.x === 0 && this.y === 0;
}
/**
* Check if another vector is equal within an optionally specified range.
* @param {Vec2} other Other vector to compare with
* @param {number} range (Optional) Precision of the comparison. Set to 0.001 by default, this value is useful for
* avoiding floating-point precision issues (defaults to 0.001).
* @returns {boolean} Whether or not the vectors are equal, within the specified range.
*/
equals(other, range = 0.001) {
return Math.abs(this.x - other.x) < range &&
Math.abs(this.y - other.y) < range;
}
//
// Linear interpolation
//
/**
* Perform linear interpolation between the vector and another.
* @param {Vec2} other Other Vec2 to interpolate to
* @param {number} alpha Progress between the values (0 - 1)
* @returns {Vec2} Resulting Vec2
*/
lerp(other, alpha) {
let d = other.sub(this);
d = d.mulv(alpha);
return this.add(d);
}
/**
* Perform linear interpolation between the vector and another, smoothly.
* @param {Vec2} other Other Vec2 to interpolate to
* @param {number} alpha Progress between the values (0 - 1)
* @param {number} edge Amount (percentage) of the interpolation to smooth (0 - 1)
* @returns {Vec2} Resulting Vec2
*/
smoothLerp(other, alpha, edge = 0.1) {
//if (alpha < edge)
// alpha = alpha / edge
//else if (alpha > 1 - edge)
// alpha = (1 - alpha) / edge;
alpha = alpha * alpha * (3 - 2 * alpha);
return this.add(other.sub(this).mulv(alpha));
}
}
export default Vec2;