@js-draw/math
Version:
A math library for js-draw.
307 lines (306 loc) • 8.93 kB
JavaScript
"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;