116 lines
3.4 KiB
JavaScript
116 lines
3.4 KiB
JavaScript
|
const DIM_NAMES = ["xr", "yg", "zb", "wa"];
|
||
|
const ARRAY_TYPES = [
|
||
|
Int8Array, Int16Array, Int32Array,
|
||
|
Uint8Array, Uint16Array, Uint32Array,
|
||
|
Float32Array, Float64Array,
|
||
|
BigInt64Array, BigUint64Array // Note: These requires values to be of type BigInt
|
||
|
];
|
||
|
|
||
|
export class Vector {
|
||
|
constructor(...values) {
|
||
|
this.type = Float32Array;
|
||
|
|
||
|
for (const value of values) {
|
||
|
if (ARRAY_TYPES.includes(value)) {
|
||
|
this.type = value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.values = new this.type(values.filter(v => !ARRAY_TYPES.includes(v)));
|
||
|
|
||
|
const defineMacro = (index, key) => {
|
||
|
Object.defineProperty(this, key, {
|
||
|
get() { return this.values[index] },
|
||
|
set(value) { this.values[index] = value }
|
||
|
});
|
||
|
};
|
||
|
|
||
|
for (const i in this.values) {
|
||
|
defineMacro(i, i);
|
||
|
|
||
|
if (i >= DIM_NAMES.length)
|
||
|
continue;
|
||
|
|
||
|
for (const names of DIM_NAMES[i]) {
|
||
|
for (const char of names) {
|
||
|
defineMacro(i, char);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clone() {
|
||
|
return new Vector(this.type, ...this.values);
|
||
|
}
|
||
|
|
||
|
static _requireSameVector(vecA, vecB) {
|
||
|
if (!(vecA instanceof Vector))
|
||
|
throw new TypeError("vecA has to be instance of `Vector`");
|
||
|
|
||
|
if (!(vecB instanceof Vector))
|
||
|
throw new TypeError("vecB has to be instance of `Vector`");
|
||
|
|
||
|
if (vecA.values.length !== vecB.values.length)
|
||
|
throw new TypeError("Vectors are of different lengths");
|
||
|
|
||
|
if (vecA.type !== vecB.type)
|
||
|
console.warn("Vectors are not of same array type.. be careful!");
|
||
|
}
|
||
|
|
||
|
_modifyComponents(vec, func) {
|
||
|
if (vec instanceof Vector) {
|
||
|
Vector._requireSameVector(this, vec);
|
||
|
|
||
|
for (const i in vec.values) {
|
||
|
this[i] = func(this[i], vec[i]);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
for (const i in this.values) {
|
||
|
this[i] = func(this[i], vec); // vec is number here
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
// Basic operations
|
||
|
add = (vec) => this._modifyComponents(vec, (a, b) => a + b);
|
||
|
subtract = (vec) => this._modifyComponents(vec, (a, b) => a - b);
|
||
|
multiply = (vec) => this._modifyComponents(vec, (a, b) => a * b);
|
||
|
divide = (vec) => this._modifyComponents(vec, (a, b) => a / b);
|
||
|
|
||
|
// Basic algebra
|
||
|
length() {
|
||
|
return Math.sqrt( this.values.map(v => v * v).reduce((a, b) => a + b, 0) );
|
||
|
}
|
||
|
|
||
|
static normalize(vec) {
|
||
|
return vec.clone().divide(vec.length());
|
||
|
}
|
||
|
|
||
|
static distance(vecA, vecB) {
|
||
|
Vector._requireSameVector(vecA, vecB);
|
||
|
return Math.sqrt( vecA.values.map((v, i) => v - vecB[i]).map(v => v * v).reduce((a, b) => a + b, 0) );
|
||
|
}
|
||
|
|
||
|
static dot(vecA, vecB) {
|
||
|
Vector._requireSameVector(vecA, vecB);
|
||
|
return vecA.values.map((v, i) => v * vecB[i]).reduce((a, b) => a + b, 0);
|
||
|
}
|
||
|
|
||
|
static cross(vecA, vecB) {
|
||
|
Vector._requireSameVector(vecA, vecB);
|
||
|
|
||
|
if (vecA.values.length !== 3)
|
||
|
throw TypeError(`Vector3 required, got Vector${vecA.values.length}`);
|
||
|
|
||
|
return new Vector(vecA.type,
|
||
|
vecA.y * vecB.z - vecA.z * vecB.y,
|
||
|
vecA.z * vecB.x - vecA.x * vecB.z,
|
||
|
vecA.x * vecB.y - vecA.y * vecB.x
|
||
|
);
|
||
|
}
|
||
|
}
|