duckengine
Version:
A 2D Game Engine for the web.
712 lines (639 loc) • 16.2 kB
text/typescript
import { Duck } from '../..';
import degToRadians from '../../utils/degToRadians';
import clamp from './clamp';
/**
* @class Vector2
* @classdesc Creates a Vector2
* @description The Vector2 Class. Represents a point
* @since 2.0.0
*/
export default class Vector2 {
/**
* @constructor Vector2
* @description Creates a Vector2 instance
* @param {number} [x=0] X position, optional -> defaults: 0
* @param {number} [y=0] Y position, optional -> defaults: 0
* @since 2.0.0
*/
constructor(public x = 0, public y = 0) {}
/**
* @memberof Vector2
* @description Sets the values of the Vector2
* @param {number} x X position to setValue of
* @param {number} y Y position to setValue of
* @returns {Vector2}
* @since 2.0.0
*/
public setValues(x: number, y: number) {
this.x = x;
this.y = y;
return this;
}
/**
* @memberof Vector2
* @description Sets the values of the Vector2 based on another Vector2
* @param {number} vector Vector2 to use to set the position
* @returns {Vector2}
* @since 2.0.0
*/
public setValuesVec(vector: Vector2) {
this.x = vector.x;
this.y = vector.y;
return this;
}
/**
* @memberof Vector2
* @description Adds a Vector2
* @param {Vector2} vector Vector2 to be added to
* @returns {Vector2}
* @since 2.0.0
*/
public add(vector: Vector2) {
this.x += vector.x;
this.y += vector.y;
return this;
}
/**
* @memberof Vector2
* @description Adds a number to the x and y properties
* @param {number} number Number to add to x and y properties
* @returns {Vector2}
* @since 2.0.0
*/
public addNumber(number: number) {
this.x += number;
this.y += number;
return this;
}
/**
* @memberof Vector2
* @description Subtracts a Vector2
* @param {Vector2} vector Vector2 to be subtracted to
* @returns {Vector2}
* @since 2.0.0
*/
public subtract(vector: Vector2) {
this.x -= vector.x;
this.y -= vector.y;
return this;
}
/**
* @memberof Vector2
* @description Subtracts a number from the x and y properties
* @param {number} number Number to subtract from x and y properties
* @returns {Vector2}
* @since 2.0.0
*/
public subtractNumber(number: number) {
this.x -= number;
this.y -= number;
return this;
}
/**
* @memberof Vector2
* @description Multiplies a Vector2
* @param {Vector2} vector Vector2 to be multiplied to
* @returns {Vector2}
* @since 2.0.0
*/
public multiply(vector: Vector2) {
this.x *= vector.x;
this.y *= vector.y;
return this;
}
/**
* @memberof Vector2
* @description Multiplies a number to the x and y properties
* @param {number} number Number to multiply to x and y properties
* @returns {Vector2}
* @since 2.0.0
*/
public multiplyNumber(number: number) {
this.x *= number;
this.y *= number;
return this;
}
/**
* @memberof Vector2
* @description Divides a Vector2
* @param {Vector2} vector Vector2 to be divided to
* @returns {Vector2}
* @since 2.0.0
*/
public divide(vector: Vector2) {
this.x /= vector.x;
this.y /= vector.y;
return this;
}
/**
* @memberof Vector2
* @description Divides a number to the x and y properties
* @param {number} number Number to divide to x and y properties
* @returns {Vector2}
* @since 2.0.0
*/
public divideNumber(number: number) {
this.x /= number;
this.y /= number;
return this;
}
/**
* @memberof Vector2
* @description Rounds the Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public round() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
}
/**
* @memberof Vector2
* @description Gets the angle between two Vector2s
* @param {Vector2} vector A Vector2 to get the angle between from
* @returns {number}
* @since 2.0.0
*/
public angleBetween(vector: Vector2) {
return Math.atan2(
this.x * vector.y - this.y * vector.x,
this.x * vector.x + this.y * vector.y
);
}
/**
* @memberof Vector2
* @description Gets the angle to two Vector2s
* @param {Vector2} vector A Vector2 to get the angle to from
* @returns {number}
* @since 2.0.0
*/
public angleTo(vector: Vector2) {
return Math.atan2(vector.y - this.y, vector.x - this.x);
}
/**
* @memberof Vector2
* @description Clones the current Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public clone() {
return new Vector2(this.x, this.y);
}
/**
* @memberof Vector2
* @description Gets the distance from another Vector2
* @param {Vector2} vector A Vector2 to get the distance from
* @returns {number}
* @since 2.0.0
*/
public distance(vector: Vector2) {
return Math.sqrt(
(vector.x - this.x) * (vector.x - this.x) +
(vector.y - this.y) * (vector.y - this.y)
);
}
/**
* @memberof Vector2
* @description Gets the distance squared from another Vector2
* @param {Vector2} vector A Vector2 to get the distance from
* @returns {number}
* @since 2.0.0
*/
public distanceSqr(vector: Vector2) {
return (
(vector.x - this.x) * (vector.x - this.x) +
(vector.y - this.y) * (vector.y - this.y)
);
}
/**
* @memberof Vector2
* @description Gets the dot product with another Vector2
* @param {Vector2} vector A Vector2 to get the dot product from
* @returns {number}
* @since 2.0.0
*/
public dot(vector: Vector2) {
return this.x * vector.x + this.y * vector.y;
}
/**
* @memberof Vector2
* @description Gets the cross dot product with another Vector2
* @param {Vector2} vector A Vector2 to get the cross dot product from
* @returns {number}
* @since 2.0.0
*/
public crossProduct(vector: Vector2) {
return this.x * vector.y - this.y * vector.x;
}
/**
* @memberof Vector2
* @description Checks if another Vector2 is equal on both axises
* @param {Vector2} vector A Vector2 to compare with
* @returns {boolean}
* @since 2.0.0
*/
public equals(vector: Vector2) {
return this.x === vector.x && this.y === vector.y;
}
/**
* @memberof Vector2
* @description Gets the perpendicular values of the Vector2
* @param {Vector2} [resultVector] The new Vector2 to save the value to, optional -> defaults: new Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public perpendicular(resultVector?: Vector2) {
resultVector = resultVector || new Vector2();
return resultVector.setValues(-this.y, this.x);
}
/**
* @memberof Vector2
* @description Gradually interpolates the Vector2 towards another Vector2 by an amount
* @param {Vector2} current The Current Vector
* @param {Vector2} target The Target Vector2
* @param {number} maxDistanceDelta The amount to increase by
* @returns {Vector2}
* @since 2.0.0
*/
public moveTowards(
current: Vector2,
target: Vector2,
maxDistanceDelta: number
) {
const toVector_x = target.x - current.x;
const toVector_y = target.y - current.y;
const sqDist = toVector_x * toVector_x + toVector_y * toVector_y;
if (
sqDist === 0 ||
(maxDistanceDelta >= 0 &&
sqDist <= maxDistanceDelta * maxDistanceDelta)
) {
return target;
}
const dist = Math.sqrt(sqDist);
const newX = current.x + (toVector_x / dist) * maxDistanceDelta;
const newY = current.y + (toVector_y / dist) * maxDistanceDelta;
return new Vector2(newX, newY);
}
/**
* @memberof Vector2
* @description Normalizes the Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public normalize() {
const mag = this.magnitude();
if (mag > 0) {
this.divideNumber(mag);
}
return this;
}
/**
* @memberof Vector2
* @description Gets the normal value of the Vector2 and another Vector2
* @param
* @param {Vector2} [resultVector] The new Vector2 to save the value to, optional -> defaults: new Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public getNormal(vector: Vector2, resultVector?: Vector2) {
resultVector = resultVector || new Vector2();
return resultVector
.setValues(vector.y - this.y, this.x - vector.x)
.normalize();
}
/**
* @memberof Vector2
* @description Determines if Vector2.x and Vector2.y are both equal to 0
* @returns {boolean}
* @since 2.0.0
*/
public isZero() {
return this.x === 0 && this.y === 0;
}
/**
* @memberof Vector
* @description Scales the Vector2 by a scalar Vector2
* @param {Vector2} scalar A Vector2 that is used to scale the current Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public scale(scalar: Vector2) {
this.x *= scalar.x;
this.y *= scalar.y;
return this;
}
/**
* @memberof Vector2
* @description Sets Vector2.x and Vector2.y to their negative values
* @returns {Vector2}
* @since 2.0.0
*/
public negate() {
this.x = -this.x;
this.y = -this.y;
return this;
}
/**
* @memberof Vector2
* @description Returns the magnitude or length of the Vector2
* @returns {number}
* @since 2.0.0
*/
public magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
/**
* @memberof Vector2
* @description Returns the magnitude/lenth squared of the Vector2
* @returns {number}
* @since 2.0.0
*/
public magnitudeSqr() {
return this.x * this.x + this.y * this.y;
}
/**
* @memberof Vector2
* @description Scales the Vector2 by the magnitude or length
* @param magnitude The magnitude or length of the Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public scaleToMagnitude(magnitude: number) {
const k = magnitude / this.magnitude();
this.x *= k;
this.y *= k;
return this;
}
/**
* @memberof Vector2
* @description Returns the string version of the Vector2
* @example console.log(new Vector2(0, 0).toString()) // Vector2(0, 0)
* @returns {string}
* @since 2.0.0
*/
public toString() {
return `Vector2(${this.x}, ${this.y})`;
}
/**
* @memberof Vector2
* @description Sets the values to be precise by using Number.toPrecision
* @param {number} precision The precision
* @returns {Vector2}
* @since 2.0.0
*/
public toPrecision(precision: number) {
this.x = Number(this.x.toPrecision(precision));
this.y = Number(this.y.toPrecision(precision));
return this;
}
/**
* @memberof Vector2
* @description Adds to the Vector2 by an amount
* @param {number} dx Delta x, the amount to increase the x value by
* @param {number} dy Delta y, the amount to increase the y value by
* @returns {Vector2}
* @since 2.0.0
*/
public translate(dx: number, dy: number) {
this.x += dx;
this.y += dy;
return this;
}
/**
* @memberof Vector2
* @description Adds to the Vector2.x by an amount
* @param {number} dx Delta x, the amount to increase the x value by
* @returns {Vector2}
* @since 2.0.0
*/
public translateX(dx: number) {
this.x += dx;
return this;
}
/**
* @memberof Vector2
* @description Adds to the Vector2.y by an amount
* @param {number} dy Delta y, the amount to increase the y value by
* @returns {Vector2}
* @since 2.0.0
*/
public translateY(dy: number) {
this.x += dy;
return this;
}
/**
* @memberof Vector2
* @description Gets the dot product using three different Vector2s
* @param {Vector2} a A Vector2
* @param {Vector2} b A Vector2
* @param {Vector2} c A Vector2
* @param {Vector2} [resultVector=Vector2] A Vector2 that the result is saved in, optional -> defaults: new Vector2
* @returns {Vector2}
* @since 2.0.0
*/
public tripleProduct(
a: Vector2,
b: Vector2,
c: Vector2,
resultVector?: Vector2
) {
resultVector = resultVector || new Vector2();
const ac = a.dot(c);
const bc = b.dot(c);
return resultVector.setValues(b.x * ac - a.x * bc, b.y * ac - a.y * bc);
}
/**
* @memberof Vector2
* @description Clamps the values to a min and max
* @param {number} min The min value
* @param {number} max The max value
* @returns {Vector2}
* @since 2.0.0
*/
public clamp(min: number, max: number) {
this.x = clamp(this.x, min, max);
this.y = clamp(this.y, min, max);
return this;
}
/**
* @memberof Vector2
* @description Clamps the values to a min
* @param {number} min The min value
* @returns {Vector2}
* @since 2.0.0
*/
public clampMin(min: number) {
if (this.x < min) {
this.x = min;
}
if (this.y < min) {
this.y = min;
}
return this;
}
/**
* @memberof Vector2
* @description Clamps the values to a max
* @param {number} max The max value
* @returns {Vector2}
* @since 2.0.0
*/
public clampMax(max: number) {
if (this.x > max) {
this.x = max;
}
if (this.y > max) {
this.y = max;
}
return this;
}
/**
* @memberof Vector2
* @description Rotates the Vector2 based on degrees
* @param {number} degrees The angle in degrees
* @param {Vector2} [center] The center Vector relative to the Vector, optional -> defaults: Vector2.ZERO
* @returns {Vector2}
* @since 2.0.0
*/
public rotate(degrees: number, center = Vector2.ZERO) {
const radians = degToRadians(degrees);
const cx = center.x || 0;
const cy = center.y || 0;
const c = Math.cos(radians),
s = Math.sin(radians);
const x = this.x - cx;
const y = this.y - cy;
this.x = x * c - y * s + cx;
this.y = x * s + y * c + cy;
return this;
}
/**
* @memberof Vector2
* @description Reflects the Vector2, returns the opposite value on a number line
*
* @example new Vector2(100, 50).reflect() // Vector2(-100, -50)
*
* @returns {Vector2}
* @since 2.0.0
*/
public reflect() {
this.x *= -1;
this.y *= -1;
return this;
}
/**
* @memberof Vector2
* @description Gets the absolute value of the vector
* @returns {Vector2}
* @since 2.0.0
*/
public abs() {
this.x = Math.abs(this.x);
this.y = Math.abs(this.y);
return this;
}
// STATIC
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with 0 set as x and y
* @returns {Vector2}
* @since 2.0.0
*/
public static get ZERO() {
return new Vector2(0, 0);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with 0 set as x and -1 set as y
* @returns {Vector2}
* @since 2.0.0
*/
public static get UP() {
return new Vector2(0, -1);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with 0 set as x and 1 set as y
* @returns {Vector2}
* @since 2.0.0
*/
public static get DOWN() {
return new Vector2(0, 1);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with -1 set as x and 0 set as y
* @returns {Vector2}
* @since 2.0.0
*/
public static get LEFT() {
return new Vector2(-1, 0);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with 1 set as x and 0 set as y
* @returns {Vector2}
* @since 2.0.0
*/
public static get RIGHT() {
return new Vector2(1, 0);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with passed parameters, if no parameters are passed, a Vector2.ZERO is returned
* @param {number} [x] X position, optional -> defaults: 0
* @param {number} [y] Y position, optional -> defaults: 0
* @returns {Vector2}
* @since 2.0.0
*/
public static CREATE(x?: number, y?: number) {
if (!x && !y) return this.ZERO; // none
if (x && !y) return new Vector2(x); // only x
if (!x && y) return new Vector2(0, y); // only y
return new Vector2(x, y); // both
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with passed vector2Like object
* @param {Duck.Types.Math.Vector2Like} vector2Like An object with x and y properties
* @returns {Vector2}
* @since 2.0.0
*/
public static fromVector2Like(vector2Like: Duck.Types.Math.Vector2Like) {
return new Vector2(vector2Like.x, vector2Like.y);
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2Like object with passed Vector2
* @param {Vector2} vector2 A Vector2 to convert to Vector2Like object
* @returns {Vector2}
* @since 2.0.0
*/
public static toVector2Like(vector2: Vector2) {
return {
x: vector2.x,
y: vector2.y,
};
}
/**
* @memberof Vector2
* @static
* @description Returns a Vector2 with values from a passed Vector2
* @param {Vector2} vector Vector2 to create a Vector2 from
* @returns {Vector2}
* @since 2.0.0
*/
public static fromVec(vector: Vector2) {
return new Vector2(vector.x, vector.y);
}
}