@js-basics/vector
Version:
A 3D Vector lib including arithmetic operator overloading (+ - * / % **).
355 lines (311 loc) • 8.57 kB
JavaScript
import {
operatorCalc,
cachedValueOf,
defineVectorLength,
cachedFactory,
cachedFunction,
defineMatrixLength
} from '../operator.js';
import { multiplyMat3Vec, multiplyMat3Mat3, multiplyVecMat3, isNumber, multiplyVecMat4 } from '../utils/math.js';
export { hijackArray } from './array.js';
export function hijackPlayCanvas(pc) {
const { Vec2, Vec3, Vec4, Quat, Mat3: AMat3, Mat4: AMat4, math } = pc;
const { LEFT, FORWARD, UP, RIGHT, ZERO } = Vec3;
const { RAD_TO_DEG, DEG_TO_RAD } = math;
Vec2.prototype.valueOf = function () {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
};
Vec3.prototype.valueOf = function () {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
};
Vec3.prototype.multiply = function (other) {
return multiplyVecMat3(this, other);
};
Vec4.prototype.valueOf = function () {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
};
Vec4.prototype.multiply = function (other) {
return multiplyVecMat4(this, other);
};
AMat3.prototype.valueOf = function () {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
};
AMat4.prototype.valueOf = function () {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
};
cachedValueOf(Vec2);
defineVectorLength(Vec2, 2);
const vec2Factory = cachedFactory(Vec2);
pc.vec2 = (x, y) => {
if (typeof x === 'function') {
return operatorCalc(x, new Vec2());
}
return vec2Factory(x, y);
};
cachedValueOf(Vec3);
defineVectorLength(Vec3, 3);
const vec3Factory = cachedFactory(Vec3);
pc.vec3 = (x, y, z) => {
if (typeof x === 'function') {
return operatorCalc(x, new Vec3());
}
return vec3Factory(x, y, z);
};
cachedValueOf(Vec4);
defineVectorLength(Vec4, 4);
const vec4Factory = cachedFactory(Vec4);
pc.vec4 = (x, y, z, w) => {
if (typeof x === 'function') {
return operatorCalc(x, new Vec4());
}
return vec4Factory(x, y, z, w);
};
pc.calc = operatorCalc;
Object.defineProperty(Quat.prototype, 'left', {
get() {
return this.transformVector(LEFT, new Vec3());
},
set() {
throw new Error('set left not allowed');
}
});
Object.defineProperty(Quat.prototype, 'dir', {
get() {
return this.transformVector(FORWARD, new Vec3());
},
set() {
throw new Error('set dir not allowed');
}
});
Object.defineProperty(Quat.prototype, 'up', {
get() {
return this.transformVector(UP, new Vec3());
},
set() {
throw new Error('set up not allowed');
}
});
Object.defineProperty(Quat.prototype, 'inverse', {
get() {
return this.clone().invert();
},
set() {
throw new Error('set inverse not allowed');
}
});
pc.quat = cachedFunction((x, y, z, w) => {
if (isNumber(x)) {
return new Quat(x, y, z, w);
}
if (!x) {
return new Quat();
}
if (isNumber(y)) {
return new Quat().setFromAxisAngle(x, y * RAD_TO_DEG);
}
return new Quat().setFromMat4(new AMat4().setLookAt(ZERO, x, y || UP));
});
pc.cross = cachedFunction((a, b) => new Vec3().cross(a, b));
pc.rotate = cachedFunction((q, axis, angle) => pc.quat(axis, angle).multiply(q));
pc.deg = degree => degree * DEG_TO_RAD;
Quat.prototype.multiplyQuaternion = function (other) {
return this.clone().mul(other);
};
Quat.prototype.multiply = function (other, y, z, w) {
if (other && isNumber(other.w)) {
return this.multiplyQuaternion(other);
}
return this.multiplyQuaternion(pc.quat(other, y, z, w));
};
cachedValueOf(Quat);
defineMatrixLength(Quat);
const LEFT90 = pc.quat(LEFT, 90);
Quat.prototype.setFromOrientation = function ({ alpha, beta, gamma }, orientation) {
let rot = pc
.quat(UP, alpha * RAD_TO_DEG)
.multiply(RIGHT, beta * RAD_TO_DEG)
.multiply(FORWARD, gamma * RAD_TO_DEG)
.multiply(LEFT90);
if (orientation) {
rot = pc.quat(rot.dir, orientation * RAD_TO_DEG).multiply(rot);
}
this.copy(rot);
return this;
};
Object.defineProperty(Vec3.prototype, 'len', {
get() {
return this.length();
},
set() {
throw new Error('set len not allowed');
}
});
Object.defineProperty(Vec2.prototype, 'len', {
get() {
return this.length();
},
set() {
throw new Error('set len not allowed');
}
});
Object.defineProperty(Vec4.prototype, 'len', {
get() {
return this.length();
},
set() {
throw new Error('set len not allowed');
}
});
AMat3.prototype.multiply = function (other) {
if (other && isNumber(other.z)) {
return multiplyMat3Vec(other);
}
return multiplyMat3Mat3(this, other);
};
AMat3.prototype[Symbol.iterator] = function () {
return [this[0], this[1], this[2]].values();
};
Object.defineProperty(AMat3.prototype, 0, {
get() {
const { data } = this;
return new Vec3(data[0], data[1], data[2]);
},
set({ x, y, z }) {
const { data } = this;
data[0] = x;
data[1] = y;
data[2] = z;
}
});
Object.defineProperty(AMat3.prototype, 1, {
get() {
const { data } = this;
return new Vec3(data[3], data[4], data[5]);
},
set({ x, y, z }) {
const { data } = this;
data[3] = x;
data[4] = y;
data[5] = z;
}
});
Object.defineProperty(AMat3.prototype, 2, {
get() {
const { data } = this;
return new Vec3(data[6], data[7], data[8]);
},
set({ x, y, z }) {
const { data } = this;
data[6] = x;
data[7] = y;
data[8] = z;
}
});
Object.defineProperty(AMat4.prototype, 0, {
get() {
const { data } = this;
return new Vec4(data[0], data[1], data[2], data[3]);
},
set({ x, y, z, w }) {
const { data } = this;
data[0] = x;
data[1] = y;
data[2] = z;
data[3] = w;
}
});
Object.defineProperty(AMat4.prototype, 1, {
get() {
const { data } = this;
return new Vec4(data[4], data[5], data[6], data[7]);
},
set({ x, y, z, w }) {
const { data } = this;
data[4] = x;
data[5] = y;
data[6] = z;
data[7] = w;
}
});
Object.defineProperty(AMat4.prototype, 2, {
get() {
const { data } = this;
return new Vec4(data[8], data[9], data[10], data[11]);
},
set({ x, y, z, w }) {
const { data } = this;
data[8] = x;
data[9] = y;
data[10] = z;
data[11] = w;
}
});
Object.defineProperty(AMat4.prototype, 3, {
get() {
const { data } = this;
return new Vec4(data[12], data[13], data[14], data[15]);
},
set({ x, y, z, w }) {
const { data } = this;
data[12] = x;
data[13] = y;
data[14] = z;
data[15] = w;
}
});
AMat4.prototype.multiply = function (other) {
if (other && isNumber(other.w)) {
return this.transformVec4(other);
}
return new AMat4().mul2(this, other);
};
AMat3.prototype[Symbol.iterator] = function () {
return [this[0], this[1], this[2], this[2]].values();
};
cachedValueOf(AMat3);
defineMatrixLength(AMat3);
cachedValueOf(AMat4);
defineMatrixLength(AMat4);
class Mat3 extends AMat3 {
constructor(...axes) {
super();
const [first] = axes;
if (isNumber(first)) {
for (let i = 0; i < 9; i += 1) {
this.data[i] = first;
}
} else if (isNumber(first[0]?.w)) {
[first[0], first[1], first[2]].forEach(({ x, y, z }, i) => {
this[i] = new Vec3(x, y, z);
});
} else {
axes.forEach((ax, i) => {
this[i] = ax;
});
}
}
}
class Mat4 extends AMat4 {
constructor(...axes) {
super();
const [first] = axes;
if (isNumber(first)) {
for (let i = 0; i < 16; i += 1) {
this.data[i] = first;
}
} else if (first[0] && isNumber(first[0].x) && !isNumber(first[0].w)) {
[first[0], first[1], first[2]].forEach(({ x, y, z }, i) => {
this[i] = new Vec4(x, y, z, 0.0);
});
} else {
axes.forEach((ax, i) => {
this[i] = ax;
});
}
}
}
pc.Mat3 = Mat3;
pc.Mat4 = Mat4;
pc.mat3 = (...axes) => new Mat3(...axes);
pc.mat4 = (...axes) => new Mat4(...axes);
}