cerceis-lib
Version:
Contains list of quality of life functions that is written in TypeScript and es6
290 lines (281 loc) • 9.75 kB
text/typescript
export interface VectorObject {
isVectorObject: boolean;
x: number,
y: number,
z: number,
// Maths
add: (x: VectorObject | number[] | number, y?: number, z?: number) => VectorObject,
limit: (max: number) => VectorObject,
div: (x: VectorObject | number[] | number, y?: number, z?: number) => VectorObject,
mult: (x: VectorObject | number[] | number, y?: number, z?: number) => VectorObject,
heading: () => number,
sub: (x: VectorObject | number[] | number, y?: number, z?: number) => VectorObject,
setMag: (n: number) => VectorObject,
magSq: () => number,
mag: () => number,
normalize: () => VectorObject,
copy: () => VectorObject,
dist: (x: VectorObject | number, y?: number) => number,
// exports
toVector2: () => number[]
toVector3: () => number[]
}
/**
* Structure and logic are based on p5js.
*/
export const createVector = (x: number, y: number, z: number = 0) => {
const vectorObject: VectorObject = {
isVectorObject: true,
x, y, z,
/**
* @param x { VectorObject | Number[] | number }
* @returns
*/
add(x: any, y: number = 0, z: number = 0) {
if (x instanceof Array) {
this.x += x[0] || 0;
this.y += x[1] || 0;
this.z += x[2] || 0;
return this;
}
if (x.isVectorObject) {
this.x += x.x || 0;
this.y += x.y || 0;
this.z += x.z || 0;
return this;
}
this.x += x || 0;
this.y += y || 0;
this.z += z || 0;
return this;
},
limit(max: number) {
const mSq = this.magSq();
if (mSq > max * max) {
this.div(Math.sqrt(mSq)) //normalize it
.mult(max);
}
return this;
},
/**
*
* @param x { VectorObject | Number[] | number }
* @param y { number }
* @param z { number }
* @returns
*/
div(x: any, y: number = 0, z: number = 0) {
if (x.isVectorObject) {
if (
Number.isFinite(x.x) &&
Number.isFinite(x.y) &&
Number.isFinite(x.z) &&
typeof x.x === 'number' &&
typeof x.y === 'number' &&
typeof x.z === 'number'
) {
this.x /= x.x;
this.y /= x.y;
this.z /= x.z;
}
return this;
}
if (x instanceof Array) {
if (
x.every(element => Number.isFinite(element)) &&
x.every(element => typeof element === 'number')
) {
if (x.some(element => element === 0)) {
console.warn('Divide by 0');
return this;
}
if (x.length === 1) {
this.x /= x[0];
this.y /= x[0];
this.z /= x[0];
} else if (x.length === 2) {
this.x /= x[0];
this.y /= x[1];
} else if (x.length === 3) {
this.x /= x[0];
this.y /= x[1];
this.z /= x[2];
}
} else {
console.warn(
'x contains components that are either undefined or not finite numbers'
);
}
return this;
}
const vectorComponents = [...arguments];
if (
vectorComponents.every(element => Number.isFinite(element)) &&
vectorComponents.every(element => typeof element === 'number')
) {
if (vectorComponents.some(element => element === 0)) {
console.warn('Divide by 0');
return this;
}
if (arguments.length === 1) {
this.x /= x;
this.y /= x;
this.z /= x;
}
if (arguments.length === 2) {
this.x /= x;
this.y /= y;
}
if (arguments.length === 3) {
this.x /= x;
this.y /= y;
this.z /= z;
}
} else {
console.warn(
'x, y, or z arguments are either undefined or not a finite number'
);
}
return this;
},
/**
*
* @param x { VectorObject | Number[] | number }
* @param y { number }
* @param z { number }
* @returns
*/
mult(x: any, y: number = 0, z: number = 0) {
if (x.isVectorObject) {
// new p5.Vector will check that values are valid upon construction but it's possible
// that someone could change the value of a component after creation, which is why we still
// perform this check
if (
Number.isFinite(x.x) &&
Number.isFinite(x.y) &&
Number.isFinite(x.z) &&
typeof x.x === 'number' &&
typeof x.y === 'number' &&
typeof x.z === 'number'
) {
this.x *= x.x;
this.y *= x.y;
this.z *= x.z;
} else {
console.warn(
'x contains components that are either undefined or not finite numbers'
);
}
return this;
}
if (x instanceof Array) {
if (
x.every(element => Number.isFinite(element)) &&
x.every(element => typeof element === 'number')
) {
if (x.length === 1) {
this.x *= x[0];
this.y *= x[0];
this.z *= x[0];
} else if (x.length === 2) {
this.x *= x[0];
this.y *= x[1];
} else if (x.length === 3) {
this.x *= x[0];
this.y *= x[1];
this.z *= x[2];
}
} else {
console.warn(
'x contains elements that are either undefined or not finite numbers'
);
}
return this;
}
const vectorComponents = [...arguments];
if (
vectorComponents.every(element => Number.isFinite(element)) &&
vectorComponents.every(element => typeof element === 'number')
) {
if (arguments.length === 1) {
this.x *= x;
this.y *= x;
this.z *= x;
}
if (arguments.length === 2) {
this.x *= x;
this.y *= y;
}
if (arguments.length === 3) {
this.x *= x;
this.y *= y;
this.z *= z;
}
} else {
console.warn(
'x, y, or z arguments are either undefined or not a finite number'
);
}
return this;
},
heading() {
const h = Math.atan2(this.y, this.x);
return h;
},
/*
* @method sub
* @param {VectorObject | Number[]} value the vector to subtract
* @chainable
*/
sub(x: any, y: number = 0, z: number = 0) {
if (x.isVectorObject) {
this.x -= x.x || 0;
this.y -= x.y || 0;
this.z -= x.z || 0;
return this;
}
if (x instanceof Array) {
this.x -= x[0] || 0;
this.y -= x[1] || 0;
this.z -= x[2] || 0;
return this;
}
this.x -= x || 0;
this.y -= y || 0;
this.z -= z || 0;
return this;
},
setMag(n) {
return this.normalize().mult(n);
},
magSq() {
const x = this.x;
const y = this.y;
const z = this.z;
return x * x + y * y + z * z;
},
mag() {
return Math.sqrt(this.magSq());
},
normalize() {
const len = this.mag();
// here we multiply by the reciprocal instead of calling 'div()'
// since div duplicates this zero check.
if (len !== 0) this.mult(1 / len);
return this;
},
copy() {
return createVector(this.x, this.y, this.z);
},
dist(x: any, y: number = 0) {
if (x.isVectorObject) {
return Math.hypot(x.x - this.x, x.y - this.y);
}
return Math.hypot(x - this.x, y - this.y);
},
toVector2(){ return [this.x, this.y] },
toVector3(){ return [this.x, this.y, this.z] },
}
return vectorObject;
}
export const FromVector = { create: createVector }