UNPKG

@js-draw/math

Version:
307 lines (306 loc) 8.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Vec3 = exports.Vec2 = void 0; const defaultEqlTolerance = 1e-10; class Vec3Impl { constructor(x, y, z) { this.x = x; this.y = y; this.z = z; } get xy() { // Useful for APIs that behave differently if .z is present. return { x: this.x, y: this.y, }; } /** Returns this' `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */ at(idx) { if (idx === 0) return this.x; if (idx === 1) return this.y; if (idx === 2) return this.z; throw new Error(`${idx} out of bounds!`); } length() { return this.magnitude(); } magnitude() { return Math.sqrt(this.magnitudeSquared()); } magnitudeSquared() { return this.x * this.x + this.y * this.y + this.z * this.z; } squareDistanceTo(p) { const dx = this.x - p.x; const dy = this.y - p.y; const dz = this.z - p.z; return dx * dx + dy * dy + dz * dz; } distanceTo(p) { return Math.sqrt(this.squareDistanceTo(p)); } maximumEntryMagnitude() { return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z))); } angle() { return Math.atan2(this.y, this.x); } normalized() { const norm = this.magnitude(); return Vec3.of(this.x / norm, this.y / norm, this.z / norm); } normalizedOrZero() { if (this.eq(Vec3.zero)) { return Vec3.zero; } return this.normalized(); } times(c) { return Vec3.of(this.x * c, this.y * c, this.z * c); } plus(v) { return Vec3.of(this.x + v.x, this.y + v.y, this.z + v.z); } minus(v) { return Vec3.of(this.x - v.x, this.y - v.y, this.z - v.z); } dot(other) { return this.x * other.x + this.y * other.y + this.z * other.z; } cross(other) { // | i j k | // | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1) // | x2 y2 z2| return Vec3.of(this.y * other.z - other.y * this.z, other.x * this.z - this.x * other.z, this.x * other.y - other.x * this.y); } scale(other) { if (typeof other === 'number') { return this.times(other); } return Vec3.of(this.x * other.x, this.y * other.y, this.z * other.z); } orthog() { // If parallel to the z-axis if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) { return this.dot(Vec3.unitX) === 0 ? Vec3.unitX : this.cross(Vec3.unitX).normalized(); } return this.cross(Vec3.unitZ.times(-1)).normalized(); } extend(distance, direction) { return this.plus(direction.normalized().times(distance)); } lerp(target, fractionTo) { return this.times(1 - fractionTo).plus(target.times(fractionTo)); } zip(other, zip) { return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, this.z)); } map(fn) { return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(this.z, 2)); } asArray() { return [this.x, this.y, this.z]; } eq(other, fuzz = defaultEqlTolerance) { return (Math.abs(other.x - this.x) <= fuzz && Math.abs(other.y - this.y) <= fuzz && Math.abs(other.z - this.z) <= fuzz); } toString() { return `Vec(${this.x}, ${this.y}, ${this.z})`; } } class Vec2Impl { constructor(x, y) { this.x = x; this.y = y; } get z() { return 0; } get xy() { // Useful for APIs that behave differently if .z is present. return { x: this.x, y: this.y, }; } at(idx) { if (idx === 0) return this.x; if (idx === 1) return this.y; if (idx === 2) return 0; throw new Error(`${idx} out of bounds!`); } length() { return this.magnitude(); } magnitude() { return Math.sqrt(this.x * this.x + this.y * this.y); } magnitudeSquared() { return this.x * this.x + this.y * this.y; } squareDistanceTo(p) { const dx = this.x - p.x; const dy = this.y - p.y; return dx * dx + dy * dy + p.z * p.z; } distanceTo(p) { return Math.sqrt(this.squareDistanceTo(p)); } maximumEntryMagnitude() { return Math.max(Math.abs(this.x), Math.abs(this.y)); } angle() { return Math.atan2(this.y, this.x); } normalized() { const norm = this.magnitude(); return Vec2.of(this.x / norm, this.y / norm); } normalizedOrZero() { if (this.eq(Vec3.zero)) { return Vec3.zero; } return this.normalized(); } times(c) { return Vec2.of(this.x * c, this.y * c); } plus(v) { return Vec3.of(this.x + v.x, this.y + v.y, v.z); } minus(v) { return Vec3.of(this.x - v.x, this.y - v.y, -v.z); } dot(other) { return this.x * other.x + this.y * other.y; } cross(other) { // | i j k | // | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1) // | x2 y2 z2| return Vec3.of(this.y * other.z, -this.x * other.z, this.x * other.y - other.x * this.y); } scale(other) { if (typeof other === 'number') { return this.times(other); } return Vec2.of(this.x * other.x, this.y * other.y); } orthog() { // If parallel to the z-axis if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) { return this.dot(Vec3.unitX) === 0 ? Vec3.unitX : this.cross(Vec3.unitX).normalized(); } return this.cross(Vec3.unitZ.times(-1)).normalized(); } extend(distance, direction) { return this.plus(direction.normalized().times(distance)); } lerp(target, fractionTo) { return this.times(1 - fractionTo).plus(target.times(fractionTo)); } zip(other, zip) { return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, 0)); } map(fn) { return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(0, 2)); } asArray() { return [this.x, this.y, 0]; } eq(other, fuzz = defaultEqlTolerance) { return (Math.abs(other.x - this.x) <= fuzz && Math.abs(other.y - this.y) <= fuzz && Math.abs(other.z) <= fuzz); } toString() { return `Vec(${this.x}, ${this.y})`; } } /** * A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an * always-zero `z` component. * * ```ts,runnable,console * import { Vec2 } from '@js-draw/math'; * * const v = Vec2.of(1, 2); * console.log('a Vec2:', v); * console.log('x component:', v.x); * console.log('z component:', v.z); * ``` */ var Vec2; (function (Vec2) { /** * Creates a `Vec2` from an x and y coordinate. * * @example * ```ts,runnable,console * import { Vec2 } from '@js-draw/math'; * const v = Vec2.of(3, 4); // x=3, y=4. * ``` */ Vec2.of = (x, y) => { return new Vec2Impl(x, y); }; /** * Creates a `Vec2` from an object containing `x` and `y` coordinates. * * @example * ```ts,runnable,console * import { Vec2 } from '@js-draw/math'; * const v1 = Vec2.ofXY({ x: 3, y: 4.5 }); * const v2 = Vec2.ofXY({ x: -123.4, y: 1 }); * ``` */ Vec2.ofXY = ({ x, y }) => { return Vec2.of(x, y); }; /** A vector of length 1 in the X direction (→). */ Vec2.unitX = Vec2.of(1, 0); /** A vector of length 1 in the Y direction (↑). */ Vec2.unitY = Vec2.of(0, 1); /** The zero vector: A vector with x=0, y=0. */ Vec2.zero = Vec2.of(0, 0); })(Vec2 || (exports.Vec2 = Vec2 = {})); /** Contains static methods for constructing a {@link Vec3}. */ var Vec3; (function (Vec3) { /** * Construct a vector from three components. * * @example * ```ts,runnable,console * import { Vec3 } from '@js-draw/math'; * const v1 = Vec3.of(1, 2, 3); * console.log(v1.plus(Vec3.of(0, 100, 0))); * ``` */ Vec3.of = (x, y, z) => { if (z === 0) { return Vec2.of(x, y); } else { return new Vec3Impl(x, y, z); } }; /** A unit vector in the x direction (`[1, 0, 0]`). */ Vec3.unitX = Vec2.unitX; /** A unit vector in the y direction (`[0, 1, 0]`). */ Vec3.unitY = Vec2.unitY; /** The zero vector (`[0, 0, 0]`). */ Vec3.zero = Vec2.zero; /** A vector of length 1 in the z direction. */ Vec3.unitZ = Vec3.of(0, 0, 1); })(Vec3 || (exports.Vec3 = Vec3 = {})); exports.default = Vec3;