@js-draw/math 1.18.0 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/cjs/Mat33.d.ts +91 -0
- package/dist/cjs/Mat33.js +88 -0
- package/dist/cjs/Vec2.d.ts +3 -40
- package/dist/cjs/Vec2.js +8 -46
- package/dist/cjs/Vec3.d.ts +112 -16
- package/dist/cjs/Vec3.js +184 -136
- package/dist/cjs/shapes/LineSegment2.d.ts +13 -2
- package/dist/cjs/shapes/LineSegment2.js +13 -2
- package/dist/cjs/shapes/PointShape2D.d.ts +33 -1
- package/dist/cjs/shapes/Rect2.d.ts +35 -3
- package/dist/cjs/shapes/Rect2.js +3 -3
- package/dist/mjs/Mat33.d.ts +91 -0
- package/dist/mjs/Mat33.mjs +88 -0
- package/dist/mjs/Vec2.d.ts +3 -40
- package/dist/mjs/Vec2.mjs +6 -42
- package/dist/mjs/Vec3.d.ts +112 -16
- package/dist/mjs/Vec3.mjs +183 -134
- package/dist/mjs/shapes/LineSegment2.d.ts +13 -2
- package/dist/mjs/shapes/LineSegment2.mjs +13 -2
- package/dist/mjs/shapes/PointShape2D.d.ts +33 -1
- package/dist/mjs/shapes/Rect2.d.ts +35 -3
- package/dist/mjs/shapes/Rect2.mjs +3 -3
- package/dist-test/test_imports/yarn.lock +12 -0
- package/package.json +6 -7
- package/src/Mat33.ts +92 -1
- package/src/Vec2.test.ts +5 -3
- package/src/Vec2.ts +7 -47
- package/src/Vec3.ts +408 -121
- package/src/shapes/BezierJSWrapper.ts +1 -1
- package/src/shapes/LineSegment2.ts +13 -2
- package/src/shapes/Rect2.ts +3 -3
- package/dist-test/test_imports/package-lock.json +0 -13
    
        package/src/Vec3.ts
    CHANGED
    
    | @@ -19,49 +19,25 @@ | |
| 19 19 | 
             
             * console.log('As an array:', Vec3.unitZ.asArray());
         | 
| 20 20 | 
             
             * ```
         | 
| 21 21 | 
             
             */
         | 
| 22 | 
            -
            export  | 
| 23 | 
            -
            	 | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
            		public readonly z: number
         | 
| 27 | 
            -
            	) {
         | 
| 28 | 
            -
            	}
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            	/** Returns the x, y components of this. */
         | 
| 31 | 
            -
            	public get xy(): { x: number; y: number } {
         | 
| 32 | 
            -
            		// Useful for APIs that behave differently if .z is present.
         | 
| 33 | 
            -
            		return {
         | 
| 34 | 
            -
            			x: this.x,
         | 
| 35 | 
            -
            			y: this.y,
         | 
| 36 | 
            -
            		};
         | 
| 37 | 
            -
            	}
         | 
| 22 | 
            +
            export interface Vec3 {
         | 
| 23 | 
            +
            	readonly x: number;
         | 
| 24 | 
            +
            	readonly y: number;
         | 
| 25 | 
            +
            	readonly z: number;
         | 
| 38 26 |  | 
| 39 | 
            -
            	/** | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
            	/** Returns this' `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
         | 
| 45 | 
            -
            	public at(idx: number): number {
         | 
| 46 | 
            -
            		if (idx === 0) return this.x;
         | 
| 47 | 
            -
            		if (idx === 1) return this.y;
         | 
| 48 | 
            -
            		if (idx === 2) return this.z;
         | 
| 49 | 
            -
             | 
| 50 | 
            -
            		throw new Error(`${idx} out of bounds!`);
         | 
| 51 | 
            -
            	}
         | 
| 52 | 
            -
             | 
| 53 | 
            -
            	/** Alias for this.magnitude. */
         | 
| 54 | 
            -
            	public length(): number {
         | 
| 55 | 
            -
            		return this.magnitude();
         | 
| 56 | 
            -
            	}
         | 
| 27 | 
            +
            	/**
         | 
| 28 | 
            +
            	 * Returns the x, y components of this.
         | 
| 29 | 
            +
            	 * May be implemented as a getter method.
         | 
| 30 | 
            +
            	 */
         | 
| 31 | 
            +
            	readonly xy: { x: number, y: number };
         | 
| 57 32 |  | 
| 58 | 
            -
            	 | 
| 59 | 
            -
             | 
| 60 | 
            -
            	}
         | 
| 33 | 
            +
            	/** Returns the vector's `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
         | 
| 34 | 
            +
            	at(i: number): number;
         | 
| 61 35 |  | 
| 62 | 
            -
            	 | 
| 63 | 
            -
             | 
| 64 | 
            -
            	 | 
| 36 | 
            +
            	/** Alias for `.magnitude`. */
         | 
| 37 | 
            +
            	length(): number;
         | 
| 38 | 
            +
            	/** Returns the length of this vector in ℝ^3. */
         | 
| 39 | 
            +
            	magnitude(): number;
         | 
| 40 | 
            +
            	magnitudeSquared(): number;
         | 
| 65 41 |  | 
| 66 42 | 
             
            	/**
         | 
| 67 43 | 
             
            	 * Interpreting this vector as a point in ℝ^3, computes the square distance
         | 
| @@ -69,12 +45,7 @@ export class Vec3 { | |
| 69 45 | 
             
            	 *
         | 
| 70 46 | 
             
            	 * Equivalent to `.minus(p).magnitudeSquared()`.
         | 
| 71 47 | 
             
            	 */
         | 
| 72 | 
            -
            	 | 
| 73 | 
            -
            		const dx = this.x - p.x;
         | 
| 74 | 
            -
            		const dy = this.y - p.y;
         | 
| 75 | 
            -
            		const dz = this.z - p.z;
         | 
| 76 | 
            -
            		return dx * dx + dy * dy + dz * dz;
         | 
| 77 | 
            -
            	}
         | 
| 48 | 
            +
            	squareDistanceTo(other: Vec3): number;
         | 
| 78 49 |  | 
| 79 50 | 
             
            	/**
         | 
| 80 51 | 
             
            	 * Interpreting this vector as a point in ℝ³, returns the distance to the point
         | 
| @@ -82,9 +53,7 @@ export class Vec3 { | |
| 82 53 | 
             
            	 *
         | 
| 83 54 | 
             
            	 * Equivalent to `.minus(p).magnitude()`.
         | 
| 84 55 | 
             
            	 */
         | 
| 85 | 
            -
            	 | 
| 86 | 
            -
            		return Math.sqrt(this.squareDistanceTo(p));
         | 
| 87 | 
            -
            	}
         | 
| 56 | 
            +
            	distanceTo(p: Vec3): number;
         | 
| 88 57 |  | 
| 89 58 | 
             
            	/**
         | 
| 90 59 | 
             
            	 * Returns the entry of this with the greatest magnitude.
         | 
| @@ -98,9 +67,7 @@ export class Vec3 { | |
| 98 67 | 
             
            	 * console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
         | 
| 99 68 | 
             
            	 * ```
         | 
| 100 69 | 
             
            	 */
         | 
| 101 | 
            -
            	 | 
| 102 | 
            -
            		return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z)));
         | 
| 103 | 
            -
            	}
         | 
| 70 | 
            +
            	maximumEntryMagnitude(): number;
         | 
| 104 71 |  | 
| 105 72 | 
             
            	/**
         | 
| 106 73 | 
             
            	 * Return this' angle in the XY plane (treats this as a Vec2).
         | 
| @@ -117,23 +84,175 @@ export class Vec3 { | |
| 117 84 | 
             
            	 * console.log(Vec2.of(-1, 0).angle());  // atan2(0, -1)
         | 
| 118 85 | 
             
            	 * ```
         | 
| 119 86 | 
             
            	 */
         | 
| 120 | 
            -
            	 | 
| 121 | 
            -
            		return Math.atan2(this.y, this.x);
         | 
| 122 | 
            -
            	}
         | 
| 87 | 
            +
            	angle(): number;
         | 
| 123 88 |  | 
| 124 89 | 
             
            	/**
         | 
| 125 90 | 
             
            	 * Returns a unit vector in the same direction as this.
         | 
| 126 91 | 
             
            	 *
         | 
| 127 92 | 
             
            	 * If `this` has zero length, the resultant vector has `NaN` components.
         | 
| 128 93 | 
             
            	 */
         | 
| 94 | 
            +
            	normalized(): Vec3;
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            	/**
         | 
| 97 | 
            +
            	 * Like {@link normalized}, except returns zero if this has zero magnitude.
         | 
| 98 | 
            +
            	 */
         | 
| 99 | 
            +
            	normalizedOrZero(): Vec3;
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            	/** @returns A copy of `this` multiplied by a scalar. */
         | 
| 102 | 
            +
            	times(c: number): Vec3;
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            	/** Performs vector addition. */
         | 
| 105 | 
            +
            	plus(v: Vec3): Vec3;
         | 
| 106 | 
            +
            	minus(v: Vec3): Vec3;
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            	/**
         | 
| 109 | 
            +
            	 * Computes the scalar product between this and `v`.
         | 
| 110 | 
            +
            	 *
         | 
| 111 | 
            +
            	 * In particular, `a.dot(b)` is equivalent to `a.x * b.x + a.y * b.y + a.z * b.z`.
         | 
| 112 | 
            +
            	 */
         | 
| 113 | 
            +
            	dot(v: Vec3): number;
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            	/** Computes the cross product between this and `v` */
         | 
| 116 | 
            +
            	cross(v: Vec3): Vec3;
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            	/**
         | 
| 119 | 
            +
            	 * If `other` is a `Vec3`, multiplies `this` component-wise by `other`. Otherwise,
         | 
| 120 | 
            +
            	 * if `other is a `number`, returns the result of scalar multiplication.
         | 
| 121 | 
            +
            	 *
         | 
| 122 | 
            +
            	 * @example
         | 
| 123 | 
            +
            	 * ```
         | 
| 124 | 
            +
            	 * Vec3.of(1, 2, 3).scale(Vec3.of(2, 4, 6)); // → Vec3(2, 8, 18)
         | 
| 125 | 
            +
            	 * ```
         | 
| 126 | 
            +
            	 */
         | 
| 127 | 
            +
            	scale(other: Vec3|number): Vec3;
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            	/**
         | 
| 130 | 
            +
            	 * Returns a vector orthogonal to this. If this is a Vec2, returns `this` rotated
         | 
| 131 | 
            +
            	 * 90 degrees counter-clockwise.
         | 
| 132 | 
            +
            	 */
         | 
| 133 | 
            +
            	orthog(): Vec3;
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            	/** Returns this plus a vector of length `distance` in `direction`. */
         | 
| 136 | 
            +
            	extend(distance: number, direction: Vec3): Vec3;
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            	/** Returns a vector `fractionTo` of the way to target from this. */
         | 
| 139 | 
            +
            	lerp(target: Vec3, fractionTo: number): Vec3;
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            	/**
         | 
| 142 | 
            +
            	 * `zip` Maps a component of this and a corresponding component of
         | 
| 143 | 
            +
            	 * `other` to a component of the output vector.
         | 
| 144 | 
            +
            	 *
         | 
| 145 | 
            +
            	 * @example
         | 
| 146 | 
            +
            	 * ```
         | 
| 147 | 
            +
            	 * const a = Vec3.of(1, 2, 3);
         | 
| 148 | 
            +
            	 * const b = Vec3.of(0.5, 2.1, 2.9);
         | 
| 149 | 
            +
            	 *
         | 
| 150 | 
            +
            	 * const zipped = a.zip(b, (aComponent, bComponent) => {
         | 
| 151 | 
            +
            	 *   return Math.min(aComponent, bComponent);
         | 
| 152 | 
            +
            	 * });
         | 
| 153 | 
            +
            	 *
         | 
| 154 | 
            +
            	 * console.log(zipped.toString()); // → Vec(0.5, 2, 2.9)
         | 
| 155 | 
            +
            	 * ```
         | 
| 156 | 
            +
            	 */
         | 
| 157 | 
            +
            	zip(
         | 
| 158 | 
            +
            		other: Vec3, zip: (componentInThis: number, componentInOther: number)=> number
         | 
| 159 | 
            +
            	): Vec3;
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            	/**
         | 
| 162 | 
            +
            	 * Returns a vector with each component acted on by `fn`.
         | 
| 163 | 
            +
            	 *
         | 
| 164 | 
            +
            	 * @example
         | 
| 165 | 
            +
            	 * ```ts,runnable,console
         | 
| 166 | 
            +
            	 * import { Vec3 } from '@js-draw/math';
         | 
| 167 | 
            +
            	 * console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
         | 
| 168 | 
            +
            	 * ```
         | 
| 169 | 
            +
            	 */
         | 
| 170 | 
            +
            	map(fn: (component: number, index: number)=> number): Vec3;
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            	asArray(): [ number, number, number ];
         | 
| 173 | 
            +
             | 
| 174 | 
            +
             | 
| 175 | 
            +
            	/**
         | 
| 176 | 
            +
            	 * [fuzz] The maximum difference between two components for this and [other]
         | 
| 177 | 
            +
            	 * to be considered equal.
         | 
| 178 | 
            +
            	 *
         | 
| 179 | 
            +
            	 * @example
         | 
| 180 | 
            +
            	 * ```
         | 
| 181 | 
            +
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 100);  // → true
         | 
| 182 | 
            +
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 0.1);  // → false
         | 
| 183 | 
            +
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3);    // → true
         | 
| 184 | 
            +
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3.01); // → true
         | 
| 185 | 
            +
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 2.99); // → false
         | 
| 186 | 
            +
            	 * ```
         | 
| 187 | 
            +
            	 */
         | 
| 188 | 
            +
            	eq(other: Vec3, tolerance?: number): boolean;
         | 
| 189 | 
            +
             | 
| 190 | 
            +
            	toString(): string;
         | 
| 191 | 
            +
            }
         | 
| 192 | 
            +
             | 
| 193 | 
            +
            const defaultEqlTolerance = 1e-10;
         | 
| 194 | 
            +
             | 
| 195 | 
            +
            class Vec3Impl implements Vec3 {
         | 
| 196 | 
            +
            	public constructor(
         | 
| 197 | 
            +
            		public readonly x: number,
         | 
| 198 | 
            +
            		public readonly y: number,
         | 
| 199 | 
            +
            		public readonly z: number
         | 
| 200 | 
            +
            	) {
         | 
| 201 | 
            +
            	}
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            	public get xy(): { x: number; y: number } {
         | 
| 204 | 
            +
            		// Useful for APIs that behave differently if .z is present.
         | 
| 205 | 
            +
            		return {
         | 
| 206 | 
            +
            			x: this.x,
         | 
| 207 | 
            +
            			y: this.y,
         | 
| 208 | 
            +
            		};
         | 
| 209 | 
            +
            	}
         | 
| 210 | 
            +
             | 
| 211 | 
            +
            	/** Returns this' `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
         | 
| 212 | 
            +
            	public at(idx: number): number {
         | 
| 213 | 
            +
            		if (idx === 0) return this.x;
         | 
| 214 | 
            +
            		if (idx === 1) return this.y;
         | 
| 215 | 
            +
            		if (idx === 2) return this.z;
         | 
| 216 | 
            +
             | 
| 217 | 
            +
            		throw new Error(`${idx} out of bounds!`);
         | 
| 218 | 
            +
            	}
         | 
| 219 | 
            +
             | 
| 220 | 
            +
            	public length(): number {
         | 
| 221 | 
            +
            		return this.magnitude();
         | 
| 222 | 
            +
            	}
         | 
| 223 | 
            +
             | 
| 224 | 
            +
            	public magnitude(): number {
         | 
| 225 | 
            +
            		return Math.sqrt(this.magnitudeSquared());
         | 
| 226 | 
            +
            	}
         | 
| 227 | 
            +
             | 
| 228 | 
            +
            	public magnitudeSquared(): number {
         | 
| 229 | 
            +
            		return this.x * this.x + this.y * this.y + this.z * this.z;
         | 
| 230 | 
            +
            	}
         | 
| 231 | 
            +
             | 
| 232 | 
            +
            	public squareDistanceTo(p: Vec3) {
         | 
| 233 | 
            +
            		const dx = this.x - p.x;
         | 
| 234 | 
            +
            		const dy = this.y - p.y;
         | 
| 235 | 
            +
            		const dz = this.z - p.z;
         | 
| 236 | 
            +
            		return dx * dx + dy * dy + dz * dz;
         | 
| 237 | 
            +
            	}
         | 
| 238 | 
            +
             | 
| 239 | 
            +
            	public distanceTo(p: Vec3) {
         | 
| 240 | 
            +
            		return Math.sqrt(this.squareDistanceTo(p));
         | 
| 241 | 
            +
            	}
         | 
| 242 | 
            +
             | 
| 243 | 
            +
            	public maximumEntryMagnitude(): number {
         | 
| 244 | 
            +
            		return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z)));
         | 
| 245 | 
            +
            	}
         | 
| 246 | 
            +
             | 
| 247 | 
            +
            	public angle(): number {
         | 
| 248 | 
            +
            		return Math.atan2(this.y, this.x);
         | 
| 249 | 
            +
            	}
         | 
| 250 | 
            +
             | 
| 129 251 | 
             
            	public normalized(): Vec3 {
         | 
| 130 252 | 
             
            		const norm = this.magnitude();
         | 
| 131 253 | 
             
            		return Vec3.of(this.x / norm, this.y / norm, this.z / norm);
         | 
| 132 254 | 
             
            	}
         | 
| 133 255 |  | 
| 134 | 
            -
            	/**
         | 
| 135 | 
            -
            	 * Like {@link normalized}, except returns zero if this has zero magnitude.
         | 
| 136 | 
            -
            	 */
         | 
| 137 256 | 
             
            	public normalizedOrZero(): Vec3 {
         | 
| 138 257 | 
             
            		if (this.eq(Vec3.zero)) {
         | 
| 139 258 | 
             
            			return Vec3.zero;
         | 
| @@ -142,7 +261,6 @@ export class Vec3 { | |
| 142 261 | 
             
            		return this.normalized();
         | 
| 143 262 | 
             
            	}
         | 
| 144 263 |  | 
| 145 | 
            -
            	/** @returns A copy of `this` multiplied by a scalar. */
         | 
| 146 264 | 
             
            	public times(c: number): Vec3 {
         | 
| 147 265 | 
             
            		return Vec3.of(this.x * c, this.y * c, this.z * c);
         | 
| 148 266 | 
             
            	}
         | 
| @@ -166,19 +284,10 @@ export class Vec3 { | |
| 166 284 | 
             
            		return Vec3.of(
         | 
| 167 285 | 
             
            			this.y * other.z - other.y * this.z,
         | 
| 168 286 | 
             
            			other.x * this.z - this.x * other.z,
         | 
| 169 | 
            -
            			this.x * other.y - other.x * this.y
         | 
| 287 | 
            +
            			this.x * other.y - other.x * this.y,
         | 
| 170 288 | 
             
            		);
         | 
| 171 289 | 
             
            	}
         | 
| 172 290 |  | 
| 173 | 
            -
            	/**
         | 
| 174 | 
            -
            	 * If `other` is a `Vec3`, multiplies `this` component-wise by `other`. Otherwise,
         | 
| 175 | 
            -
            	 * if `other is a `number`, returns the result of scalar multiplication.
         | 
| 176 | 
            -
            	 *
         | 
| 177 | 
            -
            	 * @example
         | 
| 178 | 
            -
            	 * ```
         | 
| 179 | 
            -
            	 * Vec3.of(1, 2, 3).scale(Vec3.of(2, 4, 6)); // → Vec3(2, 8, 18)
         | 
| 180 | 
            -
            	 * ```
         | 
| 181 | 
            -
            	 */
         | 
| 182 291 | 
             
            	public scale(other: Vec3|number): Vec3 {
         | 
| 183 292 | 
             
            		if (typeof other === 'number') {
         | 
| 184 293 | 
             
            			return this.times(other);
         | 
| @@ -191,10 +300,6 @@ export class Vec3 { | |
| 191 300 | 
             
            		);
         | 
| 192 301 | 
             
            	}
         | 
| 193 302 |  | 
| 194 | 
            -
            	/**
         | 
| 195 | 
            -
            	 * Returns a vector orthogonal to this. If this is a Vec2, returns `this` rotated
         | 
| 196 | 
            -
            	 * 90 degrees counter-clockwise.
         | 
| 197 | 
            -
            	 */
         | 
| 198 303 | 
             
            	public orthog(): Vec3 {
         | 
| 199 304 | 
             
            		// If parallel to the z-axis
         | 
| 200 305 | 
             
            		if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) {
         | 
| @@ -204,32 +309,14 @@ export class Vec3 { | |
| 204 309 | 
             
            		return this.cross(Vec3.unitZ.times(-1)).normalized();
         | 
| 205 310 | 
             
            	}
         | 
| 206 311 |  | 
| 207 | 
            -
            	/** Returns this plus a vector of length `distance` in `direction`. */
         | 
| 208 312 | 
             
            	public extend(distance: number, direction: Vec3): Vec3 {
         | 
| 209 313 | 
             
            		return this.plus(direction.normalized().times(distance));
         | 
| 210 314 | 
             
            	}
         | 
| 211 315 |  | 
| 212 | 
            -
            	/** Returns a vector `fractionTo` of the way to target from this. */
         | 
| 213 316 | 
             
            	public lerp(target: Vec3, fractionTo: number): Vec3 {
         | 
| 214 317 | 
             
            		return this.times(1 - fractionTo).plus(target.times(fractionTo));
         | 
| 215 318 | 
             
            	}
         | 
| 216 319 |  | 
| 217 | 
            -
            	/**
         | 
| 218 | 
            -
            	 * `zip` Maps a component of this and a corresponding component of
         | 
| 219 | 
            -
            	 * `other` to a component of the output vector.
         | 
| 220 | 
            -
            	 *
         | 
| 221 | 
            -
            	 * @example
         | 
| 222 | 
            -
            	 * ```
         | 
| 223 | 
            -
            	 * const a = Vec3.of(1, 2, 3);
         | 
| 224 | 
            -
            	 * const b = Vec3.of(0.5, 2.1, 2.9);
         | 
| 225 | 
            -
            	 *
         | 
| 226 | 
            -
            	 * const zipped = a.zip(b, (aComponent, bComponent) => {
         | 
| 227 | 
            -
            	 *   return Math.min(aComponent, bComponent);
         | 
| 228 | 
            -
            	 * });
         | 
| 229 | 
            -
            	 *
         | 
| 230 | 
            -
            	 * console.log(zipped.toString()); // → Vec(0.5, 2, 2.9)
         | 
| 231 | 
            -
            	 * ```
         | 
| 232 | 
            -
            	 */
         | 
| 233 320 | 
             
            	public zip(
         | 
| 234 321 | 
             
            		other: Vec3, zip: (componentInThis: number, componentInOther: number)=> number
         | 
| 235 322 | 
             
            	): Vec3 {
         | 
| @@ -240,39 +327,15 @@ export class Vec3 { | |
| 240 327 | 
             
            		);
         | 
| 241 328 | 
             
            	}
         | 
| 242 329 |  | 
| 243 | 
            -
            	/**
         | 
| 244 | 
            -
            	 * Returns a vector with each component acted on by `fn`.
         | 
| 245 | 
            -
            	 *
         | 
| 246 | 
            -
            	 * @example
         | 
| 247 | 
            -
            	 * ```ts,runnable,console
         | 
| 248 | 
            -
            	 * import { Vec3 } from '@js-draw/math';
         | 
| 249 | 
            -
            	 * console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
         | 
| 250 | 
            -
            	 * ```
         | 
| 251 | 
            -
            	 */
         | 
| 252 330 | 
             
            	public map(fn: (component: number, index: number)=> number): Vec3 {
         | 
| 253 | 
            -
            		return Vec3.of(
         | 
| 254 | 
            -
            			fn(this.x, 0), fn(this.y, 1), fn(this.z, 2)
         | 
| 255 | 
            -
            		);
         | 
| 331 | 
            +
            		return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(this.z, 2));
         | 
| 256 332 | 
             
            	}
         | 
| 257 333 |  | 
| 258 334 | 
             
            	public asArray(): [ number, number, number ] {
         | 
| 259 335 | 
             
            		return [this.x, this.y, this.z];
         | 
| 260 336 | 
             
            	}
         | 
| 261 337 |  | 
| 262 | 
            -
            	 | 
| 263 | 
            -
            	 * [fuzz] The maximum difference between two components for this and [other]
         | 
| 264 | 
            -
            	 * to be considered equal.
         | 
| 265 | 
            -
            	 *
         | 
| 266 | 
            -
            	 * @example
         | 
| 267 | 
            -
            	 * ```
         | 
| 268 | 
            -
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 100);  // → true
         | 
| 269 | 
            -
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 0.1);  // → false
         | 
| 270 | 
            -
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3);    // → true
         | 
| 271 | 
            -
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3.01); // → true
         | 
| 272 | 
            -
            	 * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 2.99); // → false
         | 
| 273 | 
            -
            	 * ```
         | 
| 274 | 
            -
            	 */
         | 
| 275 | 
            -
            	public eq(other: Vec3, fuzz: number = 1e-10): boolean {
         | 
| 338 | 
            +
            	public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
         | 
| 276 339 | 
             
            		return (
         | 
| 277 340 | 
             
            			Math.abs(other.x - this.x) <= fuzz
         | 
| 278 341 | 
             
            			&& Math.abs(other.y - this.y) <= fuzz
         | 
| @@ -283,11 +346,235 @@ export class Vec3 { | |
| 283 346 | 
             
            	public toString(): string {
         | 
| 284 347 | 
             
            		return `Vec(${this.x}, ${this.y}, ${this.z})`;
         | 
| 285 348 | 
             
            	}
         | 
| 349 | 
            +
            }
         | 
| 350 | 
            +
             | 
| 351 | 
            +
            class Vec2Impl implements Vec3 {
         | 
| 352 | 
            +
            	public constructor(
         | 
| 353 | 
            +
            		public readonly x: number,
         | 
| 354 | 
            +
            		public readonly y: number,
         | 
| 355 | 
            +
            	) {
         | 
| 356 | 
            +
            	}
         | 
| 357 | 
            +
             | 
| 358 | 
            +
            	public get z() { return 0; }
         | 
| 359 | 
            +
             | 
| 360 | 
            +
            	public get xy(): { x: number; y: number } {
         | 
| 361 | 
            +
            		// Useful for APIs that behave differently if .z is present.
         | 
| 362 | 
            +
            		return {
         | 
| 363 | 
            +
            			x: this.x,
         | 
| 364 | 
            +
            			y: this.y,
         | 
| 365 | 
            +
            		};
         | 
| 366 | 
            +
            	}
         | 
| 367 | 
            +
             | 
| 368 | 
            +
            	public at(idx: number): number {
         | 
| 369 | 
            +
            		if (idx === 0) return this.x;
         | 
| 370 | 
            +
            		if (idx === 1) return this.y;
         | 
| 371 | 
            +
            		if (idx === 2) return 0;
         | 
| 372 | 
            +
             | 
| 373 | 
            +
            		throw new Error(`${idx} out of bounds!`);
         | 
| 374 | 
            +
            	}
         | 
| 375 | 
            +
             | 
| 376 | 
            +
            	public length(): number {
         | 
| 377 | 
            +
            		return this.magnitude();
         | 
| 378 | 
            +
            	}
         | 
| 379 | 
            +
             | 
| 380 | 
            +
            	public magnitude(): number {
         | 
| 381 | 
            +
            		return Math.sqrt(this.x * this.x + this.y * this.y);
         | 
| 382 | 
            +
            	}
         | 
| 383 | 
            +
             | 
| 384 | 
            +
            	public magnitudeSquared(): number {
         | 
| 385 | 
            +
            		return this.x * this.x + this.y * this.y;
         | 
| 386 | 
            +
            	}
         | 
| 387 | 
            +
             | 
| 388 | 
            +
            	public squareDistanceTo(p: Vec3) {
         | 
| 389 | 
            +
            		const dx = this.x - p.x;
         | 
| 390 | 
            +
            		const dy = this.y - p.y;
         | 
| 391 | 
            +
            		return dx * dx + dy * dy + p.z * p.z;
         | 
| 392 | 
            +
            	}
         | 
| 286 393 |  | 
| 394 | 
            +
            	public distanceTo(p: Vec3) {
         | 
| 395 | 
            +
            		return Math.sqrt(this.squareDistanceTo(p));
         | 
| 396 | 
            +
            	}
         | 
| 397 | 
            +
             | 
| 398 | 
            +
            	public maximumEntryMagnitude(): number {
         | 
| 399 | 
            +
            		return Math.max(Math.abs(this.x), Math.abs(this.y));
         | 
| 400 | 
            +
            	}
         | 
| 401 | 
            +
             | 
| 402 | 
            +
            	public angle(): number {
         | 
| 403 | 
            +
            		return Math.atan2(this.y, this.x);
         | 
| 404 | 
            +
            	}
         | 
| 405 | 
            +
             | 
| 406 | 
            +
            	public normalized(): Vec3 {
         | 
| 407 | 
            +
            		const norm = this.magnitude();
         | 
| 408 | 
            +
            		return Vec2.of(this.x / norm, this.y / norm);
         | 
| 409 | 
            +
            	}
         | 
| 410 | 
            +
             | 
| 411 | 
            +
            	public normalizedOrZero(): Vec3 {
         | 
| 412 | 
            +
            		if (this.eq(Vec3.zero)) {
         | 
| 413 | 
            +
            			return Vec3.zero;
         | 
| 414 | 
            +
            		}
         | 
| 415 | 
            +
             | 
| 416 | 
            +
            		return this.normalized();
         | 
| 417 | 
            +
            	}
         | 
| 418 | 
            +
             | 
| 419 | 
            +
            	public times(c: number): Vec3 {
         | 
| 420 | 
            +
            		return Vec2.of(this.x * c, this.y * c);
         | 
| 421 | 
            +
            	}
         | 
| 422 | 
            +
             | 
| 423 | 
            +
            	public plus(v: Vec3): Vec3 {
         | 
| 424 | 
            +
            		return Vec3.of(this.x + v.x, this.y + v.y, v.z);
         | 
| 425 | 
            +
            	}
         | 
| 426 | 
            +
             | 
| 427 | 
            +
            	public minus(v: Vec3): Vec3 {
         | 
| 428 | 
            +
            		return Vec3.of(this.x - v.x, this.y - v.y, -v.z);
         | 
| 429 | 
            +
            	}
         | 
| 287 430 |  | 
| 288 | 
            -
            	public  | 
| 289 | 
            -
             | 
| 290 | 
            -
            	 | 
| 291 | 
            -
             | 
| 431 | 
            +
            	public dot(other: Vec3): number {
         | 
| 432 | 
            +
            		return this.x * other.x + this.y * other.y;
         | 
| 433 | 
            +
            	}
         | 
| 434 | 
            +
             | 
| 435 | 
            +
            	public cross(other: Vec3): Vec3 {
         | 
| 436 | 
            +
            		// | i  j  k |
         | 
| 437 | 
            +
            		// | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1)
         | 
| 438 | 
            +
            		// | x2 y2 z2|
         | 
| 439 | 
            +
            		return Vec3.of(
         | 
| 440 | 
            +
            			this.y * other.z,
         | 
| 441 | 
            +
            			-this.x * other.z,
         | 
| 442 | 
            +
            			this.x * other.y - other.x * this.y,
         | 
| 443 | 
            +
            		);
         | 
| 444 | 
            +
            	}
         | 
| 445 | 
            +
             | 
| 446 | 
            +
            	public scale(other: Vec3|number): Vec3 {
         | 
| 447 | 
            +
            		if (typeof other === 'number') {
         | 
| 448 | 
            +
            			return this.times(other);
         | 
| 449 | 
            +
            		}
         | 
| 450 | 
            +
             | 
| 451 | 
            +
            		return Vec2.of(
         | 
| 452 | 
            +
            			this.x * other.x,
         | 
| 453 | 
            +
            			this.y * other.y,
         | 
| 454 | 
            +
            		);
         | 
| 455 | 
            +
            	}
         | 
| 456 | 
            +
             | 
| 457 | 
            +
            	public orthog(): Vec3 {
         | 
| 458 | 
            +
            		// If parallel to the z-axis
         | 
| 459 | 
            +
            		if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) {
         | 
| 460 | 
            +
            			return this.dot(Vec3.unitX) === 0 ? Vec3.unitX : this.cross(Vec3.unitX).normalized();
         | 
| 461 | 
            +
            		}
         | 
| 462 | 
            +
             | 
| 463 | 
            +
            		return this.cross(Vec3.unitZ.times(-1)).normalized();
         | 
| 464 | 
            +
            	}
         | 
| 465 | 
            +
             | 
| 466 | 
            +
            	public extend(distance: number, direction: Vec3): Vec3 {
         | 
| 467 | 
            +
            		return this.plus(direction.normalized().times(distance));
         | 
| 468 | 
            +
            	}
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            	public lerp(target: Vec3, fractionTo: number): Vec3 {
         | 
| 471 | 
            +
            		return this.times(1 - fractionTo).plus(target.times(fractionTo));
         | 
| 472 | 
            +
            	}
         | 
| 473 | 
            +
             | 
| 474 | 
            +
            	public zip(
         | 
| 475 | 
            +
            		other: Vec3, zip: (componentInThis: number, componentInOther: number)=> number
         | 
| 476 | 
            +
            	): Vec3 {
         | 
| 477 | 
            +
            		return Vec3.of(
         | 
| 478 | 
            +
            			zip(other.x, this.x),
         | 
| 479 | 
            +
            			zip(other.y, this.y),
         | 
| 480 | 
            +
            			zip(other.z, 0),
         | 
| 481 | 
            +
            		);
         | 
| 482 | 
            +
            	}
         | 
| 483 | 
            +
             | 
| 484 | 
            +
            	public map(fn: (component: number, index: number)=> number): Vec3 {
         | 
| 485 | 
            +
            		return Vec3.of(
         | 
| 486 | 
            +
            			fn(this.x, 0), fn(this.y, 1), fn(0, 2)
         | 
| 487 | 
            +
            		);
         | 
| 488 | 
            +
            	}
         | 
| 489 | 
            +
             | 
| 490 | 
            +
            	public asArray(): [ number, number, number ] {
         | 
| 491 | 
            +
            		return [this.x, this.y, 0];
         | 
| 492 | 
            +
            	}
         | 
| 493 | 
            +
             | 
| 494 | 
            +
            	public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
         | 
| 495 | 
            +
            		return (
         | 
| 496 | 
            +
            			Math.abs(other.x - this.x) <= fuzz
         | 
| 497 | 
            +
            			&& Math.abs(other.y - this.y) <= fuzz
         | 
| 498 | 
            +
            			&& Math.abs(other.z) <= fuzz
         | 
| 499 | 
            +
            		);
         | 
| 500 | 
            +
            	}
         | 
| 501 | 
            +
             | 
| 502 | 
            +
            	public toString(): string {
         | 
| 503 | 
            +
            		return `Vec(${this.x}, ${this.y})`;
         | 
| 504 | 
            +
            	}
         | 
| 505 | 
            +
            }
         | 
| 506 | 
            +
             | 
| 507 | 
            +
            /**
         | 
| 508 | 
            +
             * A `Vec2` is a `Vec3` optimized for working in a plane. As such, they have an
         | 
| 509 | 
            +
             * always-zero `z` component.
         | 
| 510 | 
            +
             *
         | 
| 511 | 
            +
             * ```ts,runnable,console
         | 
| 512 | 
            +
             * import { Vec2 } from '@js-draw/math';
         | 
| 513 | 
            +
             * console.log(Vec2.of(1, 2));
         | 
| 514 | 
            +
             * ```
         | 
| 515 | 
            +
             */
         | 
| 516 | 
            +
            export namespace Vec2 {
         | 
| 517 | 
            +
            	/**
         | 
| 518 | 
            +
            	 * Creates a `Vec2` from an x and y coordinate.
         | 
| 519 | 
            +
            	 *
         | 
| 520 | 
            +
            	 * @example
         | 
| 521 | 
            +
            	 * ```ts,runnable,console
         | 
| 522 | 
            +
            	 * import { Vec2 } from '@js-draw/math';
         | 
| 523 | 
            +
            	 * const v = Vec2.of(3, 4); // x=3, y=4.
         | 
| 524 | 
            +
            	 * ```
         | 
| 525 | 
            +
            	 */
         | 
| 526 | 
            +
            	export const of = (x: number, y: number) => {
         | 
| 527 | 
            +
            		return new Vec2Impl(x, y);
         | 
| 528 | 
            +
            	};
         | 
| 529 | 
            +
             | 
| 530 | 
            +
            	/**
         | 
| 531 | 
            +
            	 * Creates a `Vec2` from an object containing `x` and `y` coordinates.
         | 
| 532 | 
            +
            	 *
         | 
| 533 | 
            +
            	 * @example
         | 
| 534 | 
            +
            	 * ```ts,runnable,console
         | 
| 535 | 
            +
            	 * import { Vec2 } from '@js-draw/math';
         | 
| 536 | 
            +
            	 * const v1 = Vec2.ofXY({ x: 3, y: 4.5 });
         | 
| 537 | 
            +
            	 * const v2 = Vec2.ofXY({ x: -123.4, y: 1 });
         | 
| 538 | 
            +
            	 * ```
         | 
| 539 | 
            +
            	 */
         | 
| 540 | 
            +
            	export const ofXY = ({x, y}: {x: number, y: number}) => {
         | 
| 541 | 
            +
            		return Vec2.of(x, y);
         | 
| 542 | 
            +
            	};
         | 
| 543 | 
            +
             | 
| 544 | 
            +
            	/** A vector of length 1 in the X direction (→). */
         | 
| 545 | 
            +
            	export const unitX = Vec2.of(1, 0);
         | 
| 546 | 
            +
             | 
| 547 | 
            +
            	/** A vector of length 1 in the Y direction (↑). */
         | 
| 548 | 
            +
            	export const unitY = Vec2.of(0, 1);
         | 
| 549 | 
            +
             | 
| 550 | 
            +
            	/** The zero vector: A vector with x=0, y=0. */
         | 
| 551 | 
            +
            	export const zero = Vec2.of(0, 0);
         | 
| 292 552 | 
             
            }
         | 
| 553 | 
            +
             | 
| 554 | 
            +
            export namespace Vec3 {
         | 
| 555 | 
            +
            	/**
         | 
| 556 | 
            +
            	 * Construct a vector from three components.
         | 
| 557 | 
            +
            	 *
         | 
| 558 | 
            +
            	 * @example
         | 
| 559 | 
            +
            	 * ```ts,runnable,console
         | 
| 560 | 
            +
            	 * import { Vec3 } from '@js-draw/math';
         | 
| 561 | 
            +
            	 * const v1 = Vec3.of(1, 2, 3);
         | 
| 562 | 
            +
            	 * ```
         | 
| 563 | 
            +
            	 */
         | 
| 564 | 
            +
            	export const of = (x: number, y: number, z: number): Vec3 => {
         | 
| 565 | 
            +
            		if (z === 0) {
         | 
| 566 | 
            +
            			return Vec2.of(x, y);
         | 
| 567 | 
            +
            		} else {
         | 
| 568 | 
            +
            			return new Vec3Impl(x, y, z);
         | 
| 569 | 
            +
            		}
         | 
| 570 | 
            +
            	};
         | 
| 571 | 
            +
             | 
| 572 | 
            +
            	export const unitX = Vec2.unitX;
         | 
| 573 | 
            +
            	export const unitY = Vec2.unitY;
         | 
| 574 | 
            +
            	export const zero = Vec2.zero;
         | 
| 575 | 
            +
             | 
| 576 | 
            +
            	/** A vector of length 1 in the z direction. */
         | 
| 577 | 
            +
            	export const unitZ = Vec3.of(0, 0, 1);
         | 
| 578 | 
            +
            }
         | 
| 579 | 
            +
             | 
| 293 580 | 
             
            export default Vec3;
         | 
| @@ -118,7 +118,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape { | |
| 118 118 | 
             
            			}
         | 
| 119 119 |  | 
| 120 120 | 
             
            			return t;
         | 
| 121 | 
            -
            		}).filter(entry => entry !== null) | 
| 121 | 
            +
            		}).filter(entry => entry !== null);
         | 
| 122 122 | 
             
            	}
         | 
| 123 123 |  | 
| 124 124 | 
             
            	public override splitAt(t: number): [BezierJSWrapper] | [BezierJSWrapper, BezierJSWrapper] {
         | 
| @@ -9,7 +9,18 @@ interface IntersectionResult { | |
| 9 9 | 
             
            	t: number;
         | 
| 10 10 | 
             
            }
         | 
| 11 11 |  | 
| 12 | 
            -
            /** | 
| 12 | 
            +
            /**
         | 
| 13 | 
            +
             * Represents a line segment. A `LineSegment2` is immutable.
         | 
| 14 | 
            +
             *
         | 
| 15 | 
            +
             * @example
         | 
| 16 | 
            +
             * ```ts,runnable,console
         | 
| 17 | 
            +
             * import {LineSegment2, Vec2} from '@js-draw/math';
         | 
| 18 | 
            +
             * const l = new LineSegment2(Vec2.of(1, 1), Vec2.of(2, 2));
         | 
| 19 | 
            +
             * console.log('length: ', l.length);
         | 
| 20 | 
            +
             * console.log('direction: ', l.direction);
         | 
| 21 | 
            +
             * console.log('bounding box: ', l.bbox);
         | 
| 22 | 
            +
             * ```
         | 
| 23 | 
            +
             */
         | 
| 13 24 | 
             
            export class LineSegment2 extends Parameterized2DShape {
         | 
| 14 25 | 
             
            	// invariant: ||direction|| = 1
         | 
| 15 26 |  | 
| @@ -51,7 +62,7 @@ export class LineSegment2 extends Parameterized2DShape { | |
| 51 62 | 
             
            	 * if no such line segment exists.
         | 
| 52 63 | 
             
            	 *
         | 
| 53 64 | 
             
            	 * @example
         | 
| 54 | 
            -
            	 * ```ts,runnable
         | 
| 65 | 
            +
            	 * ```ts,runnable,console
         | 
| 55 66 | 
             
            	 * import {LineSegment2, Vec2} from '@js-draw/math';
         | 
| 56 67 | 
             
            	 * console.log(LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 0), Vec2.of(0, 1)]));
         | 
| 57 68 | 
             
            	 * ```
         | 
    
        package/src/shapes/Rect2.ts
    CHANGED
    
    | @@ -298,9 +298,9 @@ export class Rect2 extends Abstract2DShape { | |
| 298 298 | 
             
            		return Rect2.bboxOf(this.corners.map(corner => affineTransform.transformVec2(corner)));
         | 
| 299 299 | 
             
            	}
         | 
| 300 300 |  | 
| 301 | 
            -
            	/** @return true iff this is equal to  | 
| 302 | 
            -
            	public eq(other: Rect2,  | 
| 303 | 
            -
            		return this.topLeft.eq(other.topLeft,  | 
| 301 | 
            +
            	/** @return true iff this is equal to `other ± tolerance` */
         | 
| 302 | 
            +
            	public eq(other: Rect2, tolerance: number = 0): boolean {
         | 
| 303 | 
            +
            		return this.topLeft.eq(other.topLeft, tolerance) && this.size.eq(other.size, tolerance);
         | 
| 304 304 | 
             
            	}
         | 
| 305 305 |  | 
| 306 306 | 
             
            	public override toString(): string {
         |