Skip to content

Instantly share code, notes, and snippets.

@GoToLoop
Last active June 3, 2023 14:26
Show Gist options
  • Save GoToLoop/012e47cd0f818baa45449977678a4823 to your computer and use it in GitHub Desktop.
Save GoToLoop/012e47cd0f818baa45449977678a4823 to your computer and use it in GitHub Desktop.
q5.Vector
<script src=PVector.js></script>
<script src=test.js></script>
"use strict";
for (const p of Object.getOwnPropertyNames(Math)) Q5[p] = Math[p];
function Q5(scope, parent) {
const
$ = this, M = Math,
{ _isObj, _isFun, _isNum, _isP5, _val, _argsErr } = Q5;
var
looper = null;
$._loop = true;
$.noLoop = () => {
$._loop = !!(looper = null);
return $;
}
$.loop = () => {
$._loop = true;
looper || _draw();
return $;
}
function _draw() {
}
$._angleMode = Q5.RADIANS;
$.angleMode = mode => {
if (!mode) return $._angleMode;
$._angleMode = mode;
return $;
};
$._toRadians = a => $._angleMode != Q5.DEGREES? a : a * Q5.DEG_TO_RAD;
$._fromRadians = a => $._angleMode != Q5.DEGREES? a : a * Q5.RAD_TO_DEG;
$.sin = a => M.sin($._toRadians(a));
$.cos = a => M.cos($._toRadians(a));
$.tan = a => M.tan($._toRadians(a));
$.asin = r => $._fromRadians(M.asin(r));
$.acos = r => $._fromRadians(M.acos(r));
$.atan = r => $._fromRadians(M.atan(r));
$.atan2 = (y, x) => $._fromRadians(M.atan2(y, x));
$.createVector = (x, y, z) => new $.Vector(x, y, z);
$.Vector = class Vector extends Q5.Vector {
constructor(x, y, z) {
super(x, y, z);
}
static fromAngle(ang, len, t) {
return super.fromAngle($._toRadians(ang), len, t);
}
static fromAngles(th, ph, len, t) {
return super.fromAngles($._toRadians(th), $._toRadians(ph), len, t);
}
static random2D(t, p) { return super.random2D(t, p, Vector); }
static random3D(t, p) { return super.random3D(t, p, Vector); }
static angleBetween(v, u) {
const
sig = v.x * u.y - v.y * u.x || 1 < 0? -1 : 1,
amt = Vector.dot(v, u) / (v.mag() * u.mag()),
ang = $.acos(Q5.constrain(amt, -1, 1));
return sig * ang;
}
rotate(a, t, _c, _s) { return super.rotate($._toRadians(a), t, _c, _s); }
rotateX(a, t, _c, _s) { return super.rotateX($._toRadians(a), t, _c, _s); }
rotateY(a, t, _c, _s) { return super.rotateY($._toRadians(a), t, _c, _s); }
heading() { return $.atan2(this.y, this.x); }
setHeading(a, t, _c, _s) {
return super.setHeading($._toRadians(a), t, _c, _s);
}
};
const proto = $.Vector.prototype;
Object.defineProperties(proto, {
heading2D: _val(proto.heading),
rotateZ: _val(proto.rotate)
});
}
{
Object.defineProperties(Q5, {
_isObj: { value: o => typeof o == OBJ },
_isFun: { value: f => typeof f == FUN },
_isNum: { value: n => typeof n == NUM },
_isP5: { value: o => o != null && P5 in o },
_isVec: { value: v => v != null && VEC in v },
_val: { value: value => ({ value, writable: true, configurable: true }) },
_argsErr: { value(mtd, len, min) {
throw `Too few args passed to ${mtd.name || mtd}() [${len} < ${min}].`;
}}
});
const
M = Math, P5 = 'noLoop', VEC = 'angleBetween',
OBJ = 'object', FUN = 'function', NUM = 'number',
{ _isObj, _isNum, _isP5, _isVec, _val, _argsErr } = Q5;
Q5.TAU = 2 * M.PI;
Q5.THIRD_PI = M.PI / 3;
Q5.DEG_TO_RAD = M.PI / 180;
Q5.RAD_TO_DEG = 180 / M.PI;
Q5.DEGREES = 'degrees';
Q5.RADIANS = 'radians';
Q5.radians = a => a * Q5.DEG_TO_RAD;
Q5.degrees = a => a * Q5.RAD_TO_DEG;
Q5.lerp = (start, stop, amt = 0) => +start + amt * (stop - start);
Q5.constrain = (num, low, high) => num < low? low : num > high? high : num;
Q5.createVector = (x, y, z) => new Q5.Vector(x, y, z);
Object.assign(Q5.prototype, Q5);
class Vec {
constructor(x, y, z) {
this.x = +x || 0, this.y = +y || 0, this.z = +z || 0;
this._clearMag();
}
_clearMag() {
this._magSq = this._mag = null;
return this;
}
_setMag(mag = 1, magSq = mag * mag) {
[ this._mag, this._magSq ] = arguments;
return this;
}
_calcMag() {
if (this._magSq == null)
this._magSq = (this._mag = M.hypot(this.x, this.y, this.z)) ** 2;
return this;
}
static _new(v, x, y, z) {
return new (_isVec(v)? v : Vec)(x, y, z);
}
static fromAngle(ang, len, t) {
if (arguments.length < 3 && _isObj(len)) t = len, len = 1;
t ||= Vec._new(this), len ||= 1;
return t.set(len * M.cos(ang), len * M.sin(ang), 0)._setMag(len);
}
static fromAngles(th, ph, len, t) {
const
cosTh = M.cos(th),
sinTh = M.sin(th),
cosPh = M.cos(ph),
sinPh = M.sin(ph);
if (arguments.length < 4 && _isObj(len)) t = len, len = 1;
return (t || Vec._new(this)).
set(sinTh * sinPh, -cosTh, sinTh * cosPh).
mult(len ||= 1).
_setMag(len);
}
static random2D(t, p, _cls) {
const
isPjs = _isP5(t),
rnd = p? p : isPjs && t || M;
return (_cls || Vec).fromAngle(
rnd.random() * Q5.TAU,
!isPjs && t || void 0
)._setMag();
}
static random3D(t, p, _cls) {
const
isPjs = _isP5(t),
rnd = p? p : isPjs && t || M;
return (_cls || Vec).fromAngles(
rnd.random() * Q5.TAU,
rnd.random() * Q5.TAU,
!isPjs && t || void 0
)._setMag();
}
static dist(v, u) { return M.hypot(v.x - u.x, v.y - u.y, v.z - u.z); }
static distSq(v, u) {
return (v.x - u.x) ** 2 + (v.y - u.y) ** 2 + (v.z - u.z) ** 2;
}
static dot(v, u) { return v.x * u.x + v.y * u.y + v.z * u.z; }
static reflect(incidentVector, surfaceNormal, t) {
}
static cross(v, u, t) {
const
cx = v.y * u.z - v.z * u.y,
cy = v.z * u.x - v.x * u.z,
cz = v.x * u.y - v.y * u.x;
return (t || Vec._new(this)).set(cx, cy, cz);
}
static angleBetween(v, u) {
if (!v.x && !v.y && !v.z || !u.x && !u.y && !u.z) return 0;
const amt = Vec.dot(v, u) / (v.mag() * u.mag());
return amt <= -1? M.PI : amt >= 1? 0 : M.acos(amt);
}
static lerp(v, u, amt, t) {
return (t && t.set(v) || v.copy()).lerp(u, amt);
}
static add(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x + u.x, v.y + u.y, v.z + u.z)
|| t.set(v.x + u, v.y + u, v.z + u);
}
static sub(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x - u.x, v.y - u.y, v.z - u.z)
|| t.set(v.x - u, v.y - u, v.z - u);
}
static mult(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x * u.x, v.y * u.y, v.z * u.z)
|| t.set(v.x * u, v.y * u, v.z * u);
}
static div(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x / u.x, v.y / u.y, v.z / u.z)
|| t.set(v.x / u, v.y / u, v.z / u);
}
static rem(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x % u.x, v.y % u.y, v.z % u.z)
|| t.set(v.x % u, v.y % u, v.z % u);
}
static pow(v, u, t) {
t ||= Vec._new(this);
return _isObj(u) && t.set(v.x ** u.x, v.y ** u.y, v.z ** u.z)
|| t.set(v.x ** u, v.y ** u, v.z ** u);
}
static reciprocal(v) { return v.reciprocal(); }
static negate(v) { return v.negate(); }
static zero(v) { return v.zero(); }
static equals(v, u) { return v.equals(u); }
static compare(v, u) { return v.x - u.x || v.y - u.y || v.z - u.z; }
compareTo(v) { return this.x - v.x || this.y - v.y || this.z - v.z; }
array() { return [ this.x, this.y, this.z ]; }
array2D() { return [ this.x, this.y ]; }
object() { return { x: this.x, y: this.y, z: this.z }; }
object2D() { return { x: this.x, y: this.y }; }
copy() { return this._new(this.x, this.y, this.z); }
_new(...args) { return new this.constructor(...args); }
get(t) {
if (!t) return t === void 0 && this.copy() || this.array();
else if ('z' in t) t.x = this.x, t.y = this.y, t.z = this.z;
else t[0] = this.x, t[1] = this.y, t[2] = this.z;
return t;
}
set(v, y, z) {
if (y != null) this.x = +v, this.y = +y, z != null && (this.z = +z);
else if (_isNum(v)) this.x = +v, this.y = +v, this.z = +v;
else this.set(v[0] || v.x || 0, v[1] || v.y || 0, v[2] || v.z);
return this._clearMag();
}
angleBetween(v) { return this.constructor.angleBetween(this, v); }
mag() { return this._calcMag()._mag; }
magSq() { return this._calcMag()._magSq; }
setMag(t, len, _mag) {
return _isObj(t)? this.normalize(t, _mag).mult(len)._setMag(len)
: this.normalize().mult(t)._setMag(t);
}
limit(max, t, _mag) {
const w = this, m = +_mag || w.mag();
return m > max? w.setMag(t, max, m) :
_isObj(t)? t && t.set(w) || w.copy() : w;
}
normalize(t, _mag) {
const
w = this, c = w.constructor,
len = arguments.length,
m = +_mag || w.mag(),
dividable = m == m && m != 0 && m != 1;
if (dividable) return (len && c.div(w, m, t) || w.div(m))._setMag();
console.warn("Magnitude", m, "can't be normalized:");
console.table(w);
return !len? w : t && t.set(w) || w.copy();
}
rotate(a, t, _c = M.cos(a), _s = M.sin(a)) {
const
w = this,
x = _c * w.x - _s * w.y,
y = _s * w.x + _c * w.y;
return (_isObj(t)? t || w._new() : w).set(x, y, w.z);
}
rotateX(a, t, _c = M.cos(a), _s = M.sin(a)) {
const
w = this,
y = _c * w.y - _s * w.z,
z = _s * w.y + _c * w.z;
return (_isObj(t)? t || w._new() : w).set(w.x, y, z);
}
rotateY(a, t, _c = M.cos(a), _s = M.sin(a)) {
const
w = this,
x = _s * w.z + _c * w.x,
z = _c * w.z - _s * w.x;
return (_isObj(t)? t || w._new() : w).set(x, w.y, z);
}
heading() { return M.atan2(this.y, this.x); }
setHeading(a, t, _c = M.cos(a), _s = M.sin(a)) {
const w = this, m = w.mag();
return (_isObj(t)? t || w._new() : w).set(_c * m, _s * m);
}
fromAngle(a, l, t) {
if (_isNum(l)) return this.constructor.fromAngle(a, l, t || this);
return this.constructor.fromAngle(a, l || this);
}
fromAngles(th, ph, len, t) {
const w = this, c = w.constructor;
if (_isNum(len)) return c.fromAngles(th, ph, len, t || w);
return c.fromAngles(th, ph, len || w);
}
random2D(t, p) {
const w = this, c = w.constructor;
return _isP5(t) && c.random2D(w, t)
|| c.random2D(t === void 0 && w || t, p);
}
random3D(t, p) {
const w = this, c = w.constructor;
return _isP5(t) && c.random3D(w, t)
|| c.random3D(t === void 0 && w || t, p);
}
dist(v, u) {
const w = this, c = w.constructor;
return u? c.dist(v, u) : c.dist(w, v);
}
distSq(v, u) {
const w = this, c = w.constructor;
return u? c.distSq(v, u) : c.distSq(w, v);
}
dot(v, y, z) {
const w = this, c = w.constructor;
return !_isObj(v)? w.x * v + w.y * y + w.z * z :
y == null? c.dot(w, v) : c.dot(v, y);
}
reflect(surfaceNormal, t) {
}
cross(v, u, t) {
const w = this, c = w.constructor;
return t === void 0 && c.cross(w, v, u) || c.cross(v, u, t);
}
lerp(a, b, c, d) {
var x, y, z, amt;
const w = this, len = arguments.length;
len < 2 && _argsErr(lerp, len, 2);
if (len == 2) ({x, y, z} = a), amt = b; // given vector and amt
else if (len == 3) return w.constructor.lerp(a, b, c); // v1, v2 and amt
else [x, y, z, amt] = arguments; // given x, y, z and amt
return w.set(Q5.lerp(w.x, x, amt),
Q5.lerp(w.y, y, amt),
Q5.lerp(w.z, z, amt));
}
add(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.add(w, v, y === void 0 && w || y);
w.x += v, w.y += y, z != null && (w.z += z);
return w._clearMag();
}
sub(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.sub(w, v, y === void 0 && w || y);
w.x -= v, w.y -= y, z != null && (w.z -= z);
return w._clearMag();
}
mult(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.mul(w, v, y === void 0 && w || y);
w.x *= v, w.y *= y, z != null && (w.z *= z);
return w._clearMag();
}
div(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.div(w, v, y === void 0 && w || y);
w.x /= v, w.y /= y, z != null && (w.z /= z);
return w._clearMag();
}
rem(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.rem(w, v, y === void 0 && w || y);
w.x %= v, w.y %= y, z != null && (w.z %= z);
return w._clearMag();
}
pow(v, y, z) {
const w = this;
if (!_isNum(y)) return w.constructor.pow(w, v, y === void 0 && w || y);
w.x **= v, w.y **= y, z != null && (w.z **= z);
return w._clearMag();
}
reciprocal() {
this.x = 1 / this.x, this.y = 1 / this.y, this.z = 1 / this.z;
return this._clearMag();
}
negate() {
this.x *= -1, this.y *= -1, this.z *= -1;
return this._clearMag();
}
zero() { return this._setMag(this.x = this.y = this.z = 0); }
isNaN() { return this.x != this.x || this.y != this.y || this.z != this.z; }
toString() { return `[ ${this.x}, ${this.y}, ${this.z} ]`; }
hashCode() { return this.x + this.y + this.z; }
equals(o) {
return o == this? true :
_isVec(o) && o.x == this.x && o.y == this.y && o.z == this.z;
}
};
const proto = Vec.prototype;
Object.defineProperties(proto, {
clone: _val(proto.copy),
clear: _val(proto.zero),
mul: _val(proto.mult),
heading2D: _val(proto.heading),
rotateZ: _val(proto.rotate)
});
Object.defineProperties(Vec, {
clear: _val(Vec.zero),
mul: _val(Vec.mult)
});
Object.defineProperty(Q5, 'Vector', _val(Vec));
}
q = new Q5
console.log(v = q.Vector.random3D(q))
console.log(v.lerp(3, 4, 7, 2))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment