@js-basics/vector
Version:
A 3D Vector lib including arithmetic operator overloading (+ - * / % **).
326 lines (310 loc) • 7.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ZERO = exports.Victor = exports.Vector = exports.UP = exports.RIGHT = exports.ONE = exports.LEFT = exports.FORWARD = exports.BOTTOM = exports.BACK = void 0;
exports.calc = calc;
exports.victor = exports.vector = void 0;
var _math = require("./utils/math.cjs");
var _operator = require("./operator.cjs");
var _point = require("./point.cjs");
var _css = require("./utils/css.cjs");
const X = 0;
const Y = 1;
const Z = 2;
const AXES = Symbol('axes');
function square(val) {
return val * val;
}
class AVector {
constructor(...args) {
if (typeof args[0] === 'function') {
(0, _operator.operatorCalc)(args[0], (nx, ny, nz) => {
this[AXES] = [nx, ny, nz];
});
} else if ((0, _math.isArray)(args[0])) {
this[AXES] = [...args[0]];
} else if (args[0] && (0, _math.isNumber)(args[0].x)) {
this[AXES] = [args[0].x || 0, args[0].y || 0, args[0].z || 0];
} else {
this[AXES] = [args[0] || 0, args[1] || 0, args[2] || 0];
}
}
valueOf() {
throw new Error('valueOf() not implemented, looks like you try to calculate outside of calc');
}
/**
* @returns {this}
*/
normalize() {
const {
length
} = this;
return new this.constructor(this.x / length, this.y / length, this.z / length);
}
/**
* @returns {this}
*/
norm() {
return this.normalize();
}
// methods ispired by
// https://evanw.github.io/lightgl.js/docs/vector.html
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
/**
* @param {AVector} v
* @returns {this}
*/
cross(v) {
return new this.constructor(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x);
}
/**
* @param {AVector} v
* @returns {this}
*/
crossNormalize(v) {
const vec = this.cross(v);
const {
length
} = vec;
vec[AXES][X] /= length;
vec[AXES][Y] /= length;
vec[AXES][Z] /= length;
return vec;
}
/**
* @param {AVector} v
* @returns {this}
*/
cn(v) {
return this.crossNormalize(v);
}
toAngles() {
return {
theta: Math.atan2(this.z, this.x),
phi: Math.asin(this.y / this.length)
};
}
/**
* @param {AVector} v
*/
angleTo(v) {
return (0, _math.normRad)((0, _math.acos)(this.dot(v) / (this.length * v.length)));
}
multiply(quat) {
if (quat.x === undefined) {
return this.multiplyMat3(quat);
}
if (quat.w === undefined) {
return this.multiplyVec3(quat);
}
return (0, _math.multQuatVec)(quat, this);
}
multiplyMat3(other) {
return (0, _math.multiplyVecMat3)(this, other);
}
multiplyVec3({
x,
y,
z
}) {
return new this.constructor(this.x * x, this.y * y, this.z * z);
}
/**
* @param {AVector} v
*/
distance(v) {
return Math.sqrt(square(this.x - v.x) + square(this.y - v.y) + square(this.z - v.z));
}
/**
* @param {AVector} v
*/
dist(v) {
return this.distance(v);
}
/**
* @returns {[number, number, number]}
*/
toArray() {
return [this.x, this.y, this.z];
}
swizzle(target) {
const data = target.split('').map(t => this[t]);
if (data.length === 2) {
return new _point.IPoint(data[0], data[1]);
}
return new this.constructor(data[0], data[1], data[2]);
}
// eslint-disable-next-line no-unused-vars
calc(arg) {
throw new Error('calc() not implemented');
}
clone() {
throw new Error('clone() not implemented');
}
equals(v) {
return this.x === v.x && this.y === v.y && this.z === v.z;
}
toJSON() {
return {
x: this.x,
y: this.y,
z: this.z
};
}
toString() {
return JSON.stringify(this.toJSON());
}
toCSSVars(name, target = {}) {
return (0, _css.convertToCSSVars)(name, this.toJSON(), target);
}
get lengthSq() {
return this.dot(this);
}
set lengthSq(_) {
throw new Error('set lengthSq() not implemented');
}
get length() {
return Math.sqrt(this.lengthSq);
}
set length(_) {
throw new Error('set length() not implemented');
}
get lensq() {
return this.lengthSq;
}
set lensq(_) {
throw new Error('set lensq() not implemented');
}
get len() {
return this.length;
}
set len(_) {
throw new Error('set len() not implemented');
}
get x() {
return this[AXES][X];
}
set x(_) {
throw new Error('set x() not implemented');
}
get y() {
return this[AXES][Y];
}
set y(_) {
throw new Error('set y() not implemented');
}
get z() {
return this[AXES][Z];
}
set z(_) {
throw new Error('set z() not implemented');
}
get xy() {
return new _point.IPoint(this[AXES][X], this[AXES][Y]);
}
set xy(_) {
throw new Error('set xz() not implemented');
}
get xz() {
return new _point.IPoint(this[AXES][X], this[AXES][Z]);
}
set xz(_) {
throw new Error('set xz() not implemented');
}
get yz() {
return new _point.IPoint(this[AXES][Y], this[AXES][Z]);
}
set yz(_) {
throw new Error('set yz() not implemented');
}
[Symbol.iterator]() {
return this[AXES].values();
}
}
(0, _operator.cachedValueOf)(AVector);
(0, _operator.defineVectorLength)(AVector, 3);
(0, _operator.cachedMethod)(AVector, 'dot');
(0, _operator.cachedMethod)(AVector, 'cross');
(0, _operator.cachedMethod)(AVector, 'crossNormalize');
(0, _operator.cachedMethod)(AVector, 'toAngles');
(0, _operator.cachedMethod)(AVector, 'angleTo');
(0, _operator.cachedMethod)(AVector, 'rotate');
(0, _operator.cachedMethod)(AVector, 'distance');
(0, _operator.cachedMethod)(AVector, 'toArray');
(0, _operator.cachedGetter)(AVector, 'length');
(0, _operator.cachedGetter)(AVector, 'lengthSq');
class Vector extends AVector {
set x(x) {
this[AXES][X] = x;
}
set y(y) {
this[AXES][Y] = y;
}
set z(z) {
this[AXES][Z] = z;
}
get x() {
return this[AXES][X];
}
get y() {
return this[AXES][Y];
}
get z() {
return this[AXES][Z];
}
/**
* @param {() => number} alg
* @returns {Vector & number}
*/
calc(alg) {
return (0, _operator.operatorCalc)(alg, this);
}
clone() {
return new Vector(this.x, this.y, this.z);
}
}
exports.Vector = Vector;
class Victor extends AVector {
/**
* @returns {Vector & number}
*/
toVector() {
return new Vector(this.x, this.y, this.z);
}
clone() {
return this;
}
}
exports.Victor = Victor;
function calc(alg) {
return (0, _operator.operatorCalc)(alg);
}
const vectorFactory = (0, _operator.cachedFunction)((x, y, z) => new Vector(x, y, z));
/**
* @param {number | () => number} x
* @param {number} [y]
* @param {number} [z]
* @returns {Vector & number}
*/
const vector = (x, y, z) => vectorFactory(x, y, z);
exports.vector = vector;
const victorFactory = (0, _operator.cachedFunction)((x, y, z) => new Victor(x, y, z));
/**
* @param {number | () => number} x
* @param {number} [y]
* @param {number} [z]
* @returns {Victor & number}
*/
const victor = (x, y, z) => victorFactory(x, y, z);
exports.victor = victor;
const ZERO = exports.ZERO = victor(0, 0, 0);
const FORWARD = exports.FORWARD = victor(0, 0, -1);
const BACK = exports.BACK = victor(0, 0, 1);
const LEFT = exports.LEFT = victor(-1, 0, 0);
const RIGHT = exports.RIGHT = victor(1, 0, 0);
const UP = exports.UP = victor(0, 1, 0);
const BOTTOM = exports.BOTTOM = victor(0, -1, 0);
const ONE = exports.ONE = victor(1, 1, 1);