👔Vector
Last updated
Last updated
support vector operations:u + v, u - v, k * v ...
u + v: u.plus(v)
u - v: u.minus(v)
k * v: v.times(k)
au + bv + cw: linearCombination([a,b,c], [u,v,w])
replit ⟩ Vector (v1.1) , require -> Number+ext
// 2023.01.22 - 21:04 (/) refactor vec(x,y) -> vec(...args)
// 2023.01.21 - 13:18 (+) .clampInRect()
// 2023.01.15 - 19:56 (+) linearCombination()
// 2023.01.15 - 08:50 (?) refactor, first recorded.
// ------------------------------------------------------
const {sin, cos, sqrt} = Math;
// ⭐️ import
// ------------------------------------------------------
import { } from './Number+ext.js'; // 👔 Number + ext
// ⭐️ Vector
// ------------------------------------------------------
// - vec(x, y), vec(v)
// - polar(r, theta)
// - linearCombination(scalars, vectors)
// ------------------------------------------------------
// 🔸 .x
// 🔸 .y
// 🔸 .coords : [x, y]
// 🔸 .inverse : -v (additive inverse)
// 🔹 .plus(...args) : (v), (x, y)
// 🔹 .minus(...args) : (v), (x, y)
// 🔹 .times(k) : u * k (scalar product)
// ------------------------------------------------------
// 🔸 .length
// 🔹 .distanceTo(...args) : (v), (x, y)
// ------------------------------------------------------
// 🔹 .toString()
// ------------------------------------------------------
//
class Vector {
// 🟧 Vector.zero
static get zero() { return new Vector(0, 0) }
// init
constructor(x, y) {
this.x = x;
this.y = y;
}
// 🔸 .coords
get coords() { return [this.x, this.y] }
// -----------
// -v
// -----------
// 🔸 .inverse
get inverse() { return new Vector(-this.x, -this.y) }
// -------------
// u + v
// -------------
// 🔹 u.plus(v) or u.plus(x, y)
plus(...args) {
// treat as u + v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x + v.x, this.y + v.y)
}
// treat as u + (x, y)
if (args.length === 2) return this.plus(vec(...args));
// throw error otherwise
throw new Error(`Vector.plus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// u - v
// -------------
// 🔹 u.minus(v) or u.minus(x, y)
minus(...args) {
// treat as u - v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x - v.x, this.y - v.y);
}
// treat as u - (x, y)
if (args.length === 2) return this.minus(vec(...args));
// throw error otherwise
throw new Error(`Vector.minus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// k * v
// -------------
// 🔹 .times(k)
times(k) { return new Vector(this.x * k, this.y * k) }
// ------------------------
// distance related
// ------------------------
// 🔸 .length
get length() {
const {x, y} = this;
return sqrt(x*x + y*y);
}
// 🔹 .distanceTo(v) or .distanceTo(x, y)
distanceTo(...args) {
// u.distanceTo(v)
if (args.length === 1) {
const v = args[0];
return this.minus(v).length;
}
// u.distanceTo(x, y)
if (args.length === 2) {
return this.distanceTo(vec(...args));
}
// throw error otherwise
throw new Error(`Vector.distanceTo() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// 🔹 .isEqualTo(v)
isEqualTo(v, {threshold = 100 * Number.EPSILON}={}) {
return areEqualNumbers(0, this.distanceTo(v), {threshold});
}
// ---------------
// rect
// ---------------
clampInRect(rect) {
const {x: x1, y: y1} = rect.origin;
const {x: x2, y: y2} = rect.bottomRight;
return vec(this.x.clamp(x1, x2), this.y.clamp(y1, y2)); // 👔 Number + ext
}
// ---------------
// debug
// ---------------
// 🔹 .toString()
toString() { return `(${this.x}, ${this.y})` }
// ⭐️ custom type name
// ❗️ note: typeof vec(1,2) === 'object'
get [Symbol.toStringTag]() { return 'Vector' }
}
// ----------------------------------------
// ⭐️ convenience factory functions
// ----------------------------------------
// vec(x, y), vec(v) // 👔 Vector (v1.1)
function vec(...args) {
const len = args.length;
// treat as vec(x, y)
if (len === 2) return new Vector(...args);
// treat as vec(v)
if (len === 1) {
const {x, y} = args[0];
return new Vector(x, y)
};
// throw error otherwise
throw new Error(`vec(): expecting 1 or 2 arguments, but got ${len}!`);
}
// polar(r, theta)
function polar(r, theta) {
return vec(r * cos(theta), r * sin(theta))
}
// linear combination: a1*v1 + a2*v2 + ... + an*vn
function linearCombination(scalars, vectors) {
return vectors
.map((v, i) => v.times(scalars[i] || 0))
.reduce((sum, vec) => sum.plus(vec), Vector.zero);
}
// --------------
// ⭐️ helpers
// --------------
// ⭐️ areEqualNumbers(x, y {threshold})
// - Number.EPSILON = 2^(-52) ≈ 2.2 * 10^(-16)
function areEqualNumbers(x, y, {threshold = Number.EPSILON}={}) {
return Math.abs(x - y) < threshold;
}
// 📤 export
// -----------------------------
export { vec, polar, Vector, linearCombination }; // export ES module
// module.exports = { vec, polar, Vector, linearCombination };
Rect - origin, size, width, height ...
Canvas+ext - use vec()
.
elem.position() - (.borderBox) position relative to some coordinates system.
replit ⟩ vector (with canvas example)
0: (?) first recorded, refactor.
1: (+) linearCombination()
2: (+) .clampInRect()
// 2023.01.15 - 08:50 (?) first recorded
// ------------------------------------------------------
const {sin, cos} = Math;
// ⭐️ Vector
// ------------------------------------------------------
// 🔸 .x
// 🔸 .y
// 🔸 .coords : [x, y]
// 🔸 .inverse : -v (additive inverse)
// 🔹 .add(v) : u + v
// 🔹 .plus(x, y) : u + (x, y)
// 🔹 .minus(v) : u - v
// 🔹 .subtract(x, y) : u - (x, y)
// 🔹 .times(k) : u * k (scalar product)
// ------------------------------------------------------
// 🔹 .toString()
//
class Vector {
// init
constructor(x, y) {
this.x = x;
this.y = y;
}
// 🔸 .coords
get coords() { return [this.x, this.y] }
// -------------
// u + v
// -------------
// 🔹 u.add(v)
add(v) { return new Vector(this.x + v.x, this.y + v.y) }
// 🔹 u.plus(x, y)
plus(x, y) { return this.add(new Vector(x, y)) }
// -----------
// -v
// -----------
// 🔸 .inverse
get inverse() { return new Vector(-this.x, -this.y) }
// -------------
// u - v
// -------------
// 🔹 u.minus(v)
subtract(v) { return new Vector(this.x - v.x, this.y - v.y) }
// 🔹 u.subtract(x, y)
minus(x, y) { return this.subtract(new Vector(x, y)) }
// -------------
// k * v
// -------------
// 🔹 .times(k)
times(k) { return new Vector(this.x * k, this.y * k) }
// ---------------
// helpers
// ---------------
// 🔹 .toString()
toString() { return `(${this.x}, ${this.y})` }
}
// ----------------------------------------
// ⭐️ convenience factory functions
// ----------------------------------------
function vec(x, y) { return new Vector(x, y) }
function polar(r, theta) {
return vec(r * cos(theta), r * sin(theta))
}
// export
export {
vec, polar, Vector,
};
// 2023.01.15 - 19:56 (+) linearCombination()
// 2023.01.15 - 08:50 (?) refactor, first recorded.
// ------------------------------------------------------
const {sin, cos, sqrt} = Math;
// ⭐️ Vector
// ------------------------------------------------------
// - vec(x, y)
// - polar(r, theta)
// - linearCombination(scalars, vectors)
// ------------------------------------------------------
// 🔸 .x
// 🔸 .y
// 🔸 .coords : [x, y]
// 🔸 .inverse : -v (additive inverse)
// 🔹 .plus(...args) : (v), (x, y)
// 🔹 .minus(...args) : (v), (x, y)
// 🔹 .times(k) : u * k (scalar product)
// ------------------------------------------------------
// 🔸 .length
// 🔹 .distanceTo(...args) : (v), (x, y)
// ------------------------------------------------------
// 🔹 .toString()
// ------------------------------------------------------
//
class Vector {
// 🟧 Vector.zero
static get zero() { return new Vector(0, 0) }
// init
constructor(x, y) {
this.x = x;
this.y = y;
}
// 🔸 .coords
get coords() { return [this.x, this.y] }
// -----------
// -v
// -----------
// 🔸 .inverse
get inverse() { return new Vector(-this.x, -this.y) }
// -------------
// u + v
// -------------
// 🔹 u.plus(v) or u.plus(x, y)
plus(...args) {
// treat as u + v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x + v.x, this.y + v.y)
}
// treat as u + (x, y)
if (args.length === 2) return this.plus(vec(...args));
// throw error otherwise
throw new Error(`Vector.plus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// u - v
// -------------
// 🔹 u.minus(v) or u.minus(x, y)
minus(...args) {
// treat as u - v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x - v.x, this.y - v.y);
}
// treat as u - (x, y)
if (args.length === 2) return this.minus(vec(...args));
// throw error otherwise
throw new Error(`Vector.minus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// k * v
// -------------
// 🔹 .times(k)
times(k) { return new Vector(this.x * k, this.y * k) }
// ------------------------
// distance related
// ------------------------
// 🔸 .length
get length() {
const {x, y} = this;
return sqrt(x*x + y*y);
}
// 🔹 .distanceTo(v) or .distanceTo(x, y)
distanceTo(...args) {
// u.distanceTo(v)
if (args.length === 1) {
const v = args[0];
return this.minus(v).length;
}
// u.distanceTo(x, y)
if (args.length === 2) {
return this.distanceTo(vec(...args));
}
// throw error otherwise
throw new Error(`Vector.distanceTo() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// 🔹 .isEqualTo(v)
isEqualTo(v, {threshold = 100 * Number.EPSILON}={}) {
return areEqualNumbers(0, this.distanceTo(v), {threshold});
}
// ---------------
// debug
// ---------------
// 🔹 .toString()
toString() { return `(${this.x}, ${this.y})` }
// ⭐️ custom type name
// ❗️ note: typeof vec(1,2) === 'object'
get [Symbol.toStringTag]() { return 'Vector' }
}
// ----------------------------------------
// ⭐️ convenience factory functions
// ----------------------------------------
// vec(x, y)
function vec(x, y) { return new Vector(x, y) }
// polar(r, theta)
function polar(r, theta) {
return vec(r * cos(theta), r * sin(theta))
}
// linear combination: a1*v1 + a2*v2 + ... + an*vn
function linearCombination(scalars, vectors) {
return vectors
.map((v, i) => v.times(scalars[i] || 0))
.reduce((sum, vec) => sum.plus(vec), Vector.zero);
}
// --------------
// ⭐️ helpers
// --------------
// ⭐️ areEqualNumbers(x, y {threshold})
// - Number.EPSILON = 2^(-52) ≈ 2.2 * 10^(-16)
function areEqualNumbers(x, y, {threshold = Number.EPSILON}={}) {
return Math.abs(x - y) < threshold;
}
// 📤 export
// -----------------------------
// export { vec, polar, Vector, linearCombination }; // export ES module
module.exports = { vec, polar, Vector, linearCombination };
// 2023.01.21 - 13:18 (+) .clampInRect()
// 2023.01.15 - 19:56 (+) linearCombination()
// 2023.01.15 - 08:50 (?) refactor, first recorded.
// ------------------------------------------------------
const {sin, cos, sqrt} = Math;
// ⭐️ import
// ------------------------------------------------------
import { } from './Number+ext.js'; // 👔 Number + ext
// ⭐️ Vector
// ------------------------------------------------------
// - vec(x, y)
// - polar(r, theta)
// - linearCombination(scalars, vectors)
// ------------------------------------------------------
// 🔸 .x
// 🔸 .y
// 🔸 .coords : [x, y]
// 🔸 .inverse : -v (additive inverse)
// 🔹 .plus(...args) : (v), (x, y)
// 🔹 .minus(...args) : (v), (x, y)
// 🔹 .times(k) : u * k (scalar product)
// ------------------------------------------------------
// 🔸 .length
// 🔹 .distanceTo(...args) : (v), (x, y)
// ------------------------------------------------------
// 🔹 .toString()
// ------------------------------------------------------
//
class Vector {
// 🟧 Vector.zero
static get zero() { return new Vector(0, 0) }
// init
constructor(x, y) {
this.x = x;
this.y = y;
}
// 🔸 .coords
get coords() { return [this.x, this.y] }
// -----------
// -v
// -----------
// 🔸 .inverse
get inverse() { return new Vector(-this.x, -this.y) }
// -------------
// u + v
// -------------
// 🔹 u.plus(v) or u.plus(x, y)
plus(...args) {
// treat as u + v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x + v.x, this.y + v.y)
}
// treat as u + (x, y)
if (args.length === 2) return this.plus(vec(...args));
// throw error otherwise
throw new Error(`Vector.plus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// u - v
// -------------
// 🔹 u.minus(v) or u.minus(x, y)
minus(...args) {
// treat as u - v
if (args.length === 1) {
const v = args[0];
return new Vector(this.x - v.x, this.y - v.y);
}
// treat as u - (x, y)
if (args.length === 2) return this.minus(vec(...args));
// throw error otherwise
throw new Error(`Vector.minus() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// -------------
// k * v
// -------------
// 🔹 .times(k)
times(k) { return new Vector(this.x * k, this.y * k) }
// ------------------------
// distance related
// ------------------------
// 🔸 .length
get length() {
const {x, y} = this;
return sqrt(x*x + y*y);
}
// 🔹 .distanceTo(v) or .distanceTo(x, y)
distanceTo(...args) {
// u.distanceTo(v)
if (args.length === 1) {
const v = args[0];
return this.minus(v).length;
}
// u.distanceTo(x, y)
if (args.length === 2) {
return this.distanceTo(vec(...args));
}
// throw error otherwise
throw new Error(`Vector.distanceTo() is expecting 1 or 2 arguments, but got ${args.length}.`);
}
// 🔹 .isEqualTo(v)
isEqualTo(v, {threshold = 100 * Number.EPSILON}={}) {
return areEqualNumbers(0, this.distanceTo(v), {threshold});
}
// ---------------
// rect
// ---------------
clampInRect(rect) {
const {x: x1, y: y1} = rect.origin;
const {x: x2, y: y2} = rect.bottomRight;
return vec(this.x.clamp(x1, x2), this.y.clamp(y1, y2)); // 👔 Number + ext
}
// ---------------
// debug
// ---------------
// 🔹 .toString()
toString() { return `(${this.x}, ${this.y})` }
// ⭐️ custom type name
// ❗️ note: typeof vec(1,2) === 'object'
get [Symbol.toStringTag]() { return 'Vector' }
}
// ----------------------------------------
// ⭐️ convenience factory functions
// ----------------------------------------
// vec(x, y)
function vec(x, y) { return new Vector(x, y) }
// polar(r, theta)
function polar(r, theta) {
return vec(r * cos(theta), r * sin(theta))
}
// linear combination: a1*v1 + a2*v2 + ... + an*vn
function linearCombination(scalars, vectors) {
return vectors
.map((v, i) => v.times(scalars[i] || 0))
.reduce((sum, vec) => sum.plus(vec), Vector.zero);
}
// --------------
// ⭐️ helpers
// --------------
// ⭐️ areEqualNumbers(x, y {threshold})
// - Number.EPSILON = 2^(-52) ≈ 2.2 * 10^(-16)
function areEqualNumbers(x, y, {threshold = Number.EPSILON}={}) {
return Math.abs(x - y) < threshold;
}
// 📤 export
// -----------------------------
export { vec, polar, Vector, linearCombination }; // export ES module
// module.exports = { vec, polar, Vector, linearCombination };
replit ⟩ Vector.js , require -> Number+ext