@js-draw/math 1.21.3 → 1.23.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/build-config.json +1 -1
- package/dist/cjs/Color4.d.ts +24 -1
- package/dist/cjs/Color4.js +35 -3
- package/dist/cjs/Mat33.d.ts +21 -11
- package/dist/cjs/Mat33.js +28 -24
- package/dist/cjs/Vec3.d.ts +12 -3
- package/dist/cjs/Vec3.js +20 -9
- package/dist/cjs/lib.d.ts +3 -0
- package/dist/cjs/lib.js +3 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
- package/dist/cjs/shapes/LineSegment2.js +13 -17
- package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
- package/dist/cjs/shapes/Path.d.ts +1 -0
- package/dist/cjs/shapes/Path.js +50 -47
- package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/cjs/shapes/QuadraticBezier.js +26 -3
- package/dist/cjs/shapes/Rect2.d.ts +13 -0
- package/dist/cjs/shapes/Rect2.js +35 -16
- package/dist/cjs/shapes/Triangle.js +4 -5
- package/dist/cjs/utils/convexHull2Of.js +3 -3
- package/dist/mjs/Color4.d.ts +24 -1
- package/dist/mjs/Color4.mjs +35 -3
- package/dist/mjs/Mat33.d.ts +21 -11
- package/dist/mjs/Mat33.mjs +28 -24
- package/dist/mjs/Vec3.d.ts +12 -3
- package/dist/mjs/Vec3.mjs +20 -9
- package/dist/mjs/lib.d.ts +3 -0
- package/dist/mjs/lib.mjs +3 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
- package/dist/mjs/shapes/LineSegment2.mjs +13 -17
- package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
- package/dist/mjs/shapes/Path.d.ts +1 -0
- package/dist/mjs/shapes/Path.mjs +50 -47
- package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
- package/dist/mjs/shapes/Rect2.d.ts +13 -0
- package/dist/mjs/shapes/Rect2.mjs +35 -16
- package/dist/mjs/shapes/Triangle.mjs +4 -5
- package/dist/mjs/utils/convexHull2Of.mjs +3 -3
- package/dist-test/test_imports/test-require.cjs +1 -1
- package/package.json +3 -3
- package/src/Color4.test.ts +21 -21
- package/src/Color4.ts +61 -18
- package/src/Mat33.fromCSSMatrix.test.ts +32 -46
- package/src/Mat33.test.ts +64 -102
- package/src/Mat33.ts +81 -104
- package/src/Vec2.test.ts +3 -3
- package/src/Vec3.test.ts +2 -3
- package/src/Vec3.ts +46 -61
- package/src/lib.ts +3 -2
- package/src/polynomial/solveQuadratic.test.ts +39 -13
- package/src/polynomial/solveQuadratic.ts +5 -6
- package/src/rounding/cleanUpNumber.test.ts +1 -1
- package/src/rounding/constants.ts +1 -3
- package/src/rounding/getLenAfterDecimal.ts +1 -2
- package/src/rounding/lib.ts +1 -2
- package/src/rounding/toRoundedString.test.ts +1 -1
- package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
- package/src/rounding/toStringOfSamePrecision.ts +1 -1
- package/src/shapes/BezierJSWrapper.ts +56 -37
- package/src/shapes/CubicBezier.ts +3 -3
- package/src/shapes/LineSegment2.test.ts +24 -17
- package/src/shapes/LineSegment2.ts +26 -29
- package/src/shapes/Parameterized2DShape.ts +5 -4
- package/src/shapes/Path.fromString.test.ts +5 -5
- package/src/shapes/Path.test.ts +122 -120
- package/src/shapes/Path.toString.test.ts +7 -7
- package/src/shapes/Path.ts +379 -352
- package/src/shapes/PointShape2D.ts +3 -3
- package/src/shapes/QuadraticBezier.test.ts +27 -21
- package/src/shapes/QuadraticBezier.ts +26 -11
- package/src/shapes/Rect2.test.ts +44 -75
- package/src/shapes/Rect2.ts +47 -35
- package/src/shapes/Triangle.test.ts +31 -29
- package/src/shapes/Triangle.ts +17 -18
- package/src/utils/convexHull2Of.test.ts +54 -15
- package/src/utils/convexHull2Of.ts +9 -7
- package/tsconfig.json +1 -3
- package/typedoc.json +2 -2
package/build-config.json
CHANGED
package/dist/cjs/Color4.d.ts
CHANGED
@@ -28,9 +28,32 @@ export declare class Color4 {
|
|
28
28
|
* Each component should be in the range [0, 1].
|
29
29
|
*/
|
30
30
|
static ofRGB(red: number, green: number, blue: number): Color4;
|
31
|
+
/**
|
32
|
+
* Creates a color from red, green, blue, and transparency components. Each component should
|
33
|
+
* be in the range $[0, 1]$.
|
34
|
+
*/
|
31
35
|
static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4;
|
36
|
+
/**
|
37
|
+
* Creates a color from an RGB (or RGBA) array.
|
38
|
+
*
|
39
|
+
* This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
|
40
|
+
* that range from 0 to 255.
|
41
|
+
*
|
42
|
+
* If the array values instead range from 0-1, pass `maxValue` as `1`.
|
43
|
+
*/
|
44
|
+
static fromRGBArray(array: Uint8Array | Uint8ClampedArray | number[], maxValue?: number): Color4;
|
45
|
+
/**
|
46
|
+
* Creates a `Color4` from a three or four-component hexadecimal
|
47
|
+
* [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
|
48
|
+
*
|
49
|
+
* Example:
|
50
|
+
* ```ts,runnable,console
|
51
|
+
* import { Color4 } from '@js-draw/math';
|
52
|
+
* console.log(Color4.fromHex('#ff0'));
|
53
|
+
* ```
|
54
|
+
*/
|
32
55
|
static fromHex(hexString: string): Color4;
|
33
|
-
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
56
|
+
/** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
34
57
|
static fromString(text: string): Color4;
|
35
58
|
/** @returns true if `this` and `other` are approximately equal. */
|
36
59
|
eq(other: Color4 | null | undefined): boolean;
|
package/dist/cjs/Color4.js
CHANGED
@@ -42,6 +42,10 @@ class Color4 {
|
|
42
42
|
static ofRGB(red, green, blue) {
|
43
43
|
return Color4.ofRGBA(red, green, blue, 1.0);
|
44
44
|
}
|
45
|
+
/**
|
46
|
+
* Creates a color from red, green, blue, and transparency components. Each component should
|
47
|
+
* be in the range $[0, 1]$.
|
48
|
+
*/
|
45
49
|
static ofRGBA(red, green, blue, alpha) {
|
46
50
|
red = Math.max(0, Math.min(red, 1));
|
47
51
|
green = Math.max(0, Math.min(green, 1));
|
@@ -49,6 +53,34 @@ class Color4 {
|
|
49
53
|
alpha = Math.max(0, Math.min(alpha, 1));
|
50
54
|
return new Color4(red, green, blue, alpha);
|
51
55
|
}
|
56
|
+
/**
|
57
|
+
* Creates a color from an RGB (or RGBA) array.
|
58
|
+
*
|
59
|
+
* This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
|
60
|
+
* that range from 0 to 255.
|
61
|
+
*
|
62
|
+
* If the array values instead range from 0-1, pass `maxValue` as `1`.
|
63
|
+
*/
|
64
|
+
static fromRGBArray(array, maxValue = 255) {
|
65
|
+
const red = array[0];
|
66
|
+
const green = array[1] ?? red;
|
67
|
+
const blue = array[2] ?? red;
|
68
|
+
let alpha = 255;
|
69
|
+
if (3 < array.length) {
|
70
|
+
alpha = array[3];
|
71
|
+
}
|
72
|
+
return Color4.ofRGBA(red / maxValue, green / maxValue, blue / maxValue, alpha / maxValue);
|
73
|
+
}
|
74
|
+
/**
|
75
|
+
* Creates a `Color4` from a three or four-component hexadecimal
|
76
|
+
* [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
|
77
|
+
*
|
78
|
+
* Example:
|
79
|
+
* ```ts,runnable,console
|
80
|
+
* import { Color4 } from '@js-draw/math';
|
81
|
+
* console.log(Color4.fromHex('#ff0'));
|
82
|
+
* ```
|
83
|
+
*/
|
52
84
|
static fromHex(hexString) {
|
53
85
|
// Remove starting '#' (if present)
|
54
86
|
hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
|
@@ -61,7 +93,7 @@ class Color4 {
|
|
61
93
|
// Each character is a component
|
62
94
|
const components = hexString.split('');
|
63
95
|
// Convert to RRGGBBAA or RRGGBB format
|
64
|
-
hexString = components.map(component => `${component}0`).join('');
|
96
|
+
hexString = components.map((component) => `${component}0`).join('');
|
65
97
|
}
|
66
98
|
if (hexString.length === 6) {
|
67
99
|
// Alpha component
|
@@ -77,7 +109,7 @@ class Color4 {
|
|
77
109
|
}
|
78
110
|
return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
|
79
111
|
}
|
80
|
-
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
112
|
+
/** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
81
113
|
static fromString(text) {
|
82
114
|
if (text.startsWith('#')) {
|
83
115
|
return Color4.fromHex(text);
|
@@ -171,7 +203,7 @@ class Color4 {
|
|
171
203
|
// - https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
172
204
|
// - https://stackoverflow.com/a/9733420
|
173
205
|
// Normalize the components, as per above
|
174
|
-
const components = [this.r, this.g, this.b].map(component => {
|
206
|
+
const components = [this.r, this.g, this.b].map((component) => {
|
175
207
|
if (component < 0.03928) {
|
176
208
|
return component / 12.92;
|
177
209
|
}
|
package/dist/cjs/Mat33.d.ts
CHANGED
@@ -3,17 +3,7 @@ import Vec3 from './Vec3';
|
|
3
3
|
/**
|
4
4
|
* See {@link Mat33.toArray}.
|
5
5
|
*/
|
6
|
-
export type Mat33Array = [
|
7
|
-
number,
|
8
|
-
number,
|
9
|
-
number,
|
10
|
-
number,
|
11
|
-
number,
|
12
|
-
number,
|
13
|
-
number,
|
14
|
-
number,
|
15
|
-
number
|
16
|
-
];
|
6
|
+
export type Mat33Array = [number, number, number, number, number, number, number, number, number];
|
17
7
|
/**
|
18
8
|
* Represents a three dimensional linear transformation or
|
19
9
|
* a two-dimensional affine transformation. (An affine transformation scales/rotates/shears
|
@@ -216,6 +206,26 @@ export declare class Mat33 {
|
|
216
206
|
* $$
|
217
207
|
*/
|
218
208
|
static translation(amount: Vec2): Mat33;
|
209
|
+
/**
|
210
|
+
* Creates a matrix for rotating `Vec2`s about `center` by some number of `radians`.
|
211
|
+
*
|
212
|
+
* For this function, {@link Vec2}s are considered to be points in 2D space.
|
213
|
+
*
|
214
|
+
* For example,
|
215
|
+
* ```ts,runnable,console
|
216
|
+
* import { Mat33, Vec2 } from '@js-draw/math';
|
217
|
+
*
|
218
|
+
* const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle
|
219
|
+
* const center = Vec2.of(1, 1); // The point (1,1)
|
220
|
+
* const rotationMatrix = Mat33.zRotation(halfCircle, center);
|
221
|
+
*
|
222
|
+
* console.log(
|
223
|
+
* 'Rotating (0,0) 180deg about', center, 'results in',
|
224
|
+
* // Rotates (0,0)
|
225
|
+
* rotationMatrix.transformVec2(Vec2.zero),
|
226
|
+
* );
|
227
|
+
* ```
|
228
|
+
*/
|
219
229
|
static zRotation(radians: number, center?: Point2): Mat33;
|
220
230
|
static scaling2D(amount: number | Vec2, center?: Point2): Mat33;
|
221
231
|
/**
|
package/dist/cjs/Mat33.js
CHANGED
@@ -76,11 +76,7 @@ class Mat33 {
|
|
76
76
|
this.c2 = c2;
|
77
77
|
this.c3 = c3;
|
78
78
|
this.cachedInverse = undefined;
|
79
|
-
this.rows = [
|
80
|
-
Vec3_1.default.of(a1, a2, a3),
|
81
|
-
Vec3_1.default.of(b1, b2, b3),
|
82
|
-
Vec3_1.default.of(c1, c2, c3),
|
83
|
-
];
|
79
|
+
this.rows = [Vec3_1.default.of(a1, a2, a3), Vec3_1.default.of(b1, b2, b3), Vec3_1.default.of(c1, c2, c3)];
|
84
80
|
}
|
85
81
|
/**
|
86
82
|
* Creates a matrix from the given rows:
|
@@ -112,16 +108,8 @@ class Mat33 {
|
|
112
108
|
if (this.cachedInverse !== undefined) {
|
113
109
|
return this.cachedInverse;
|
114
110
|
}
|
115
|
-
const toIdentity = [
|
116
|
-
|
117
|
-
this.rows[1],
|
118
|
-
this.rows[2],
|
119
|
-
];
|
120
|
-
const toResult = [
|
121
|
-
Vec3_1.default.unitX,
|
122
|
-
Vec3_1.default.unitY,
|
123
|
-
Vec3_1.default.unitZ,
|
124
|
-
];
|
111
|
+
const toIdentity = [this.rows[0], this.rows[1], this.rows[2]];
|
112
|
+
const toResult = [Vec3_1.default.unitX, Vec3_1.default.unitY, Vec3_1.default.unitZ];
|
125
113
|
// Convert toIdentity to the identity matrix and
|
126
114
|
// toResult to the inverse through elementary row operations
|
127
115
|
for (let cursor = 0; cursor < 3; cursor++) {
|
@@ -317,11 +305,7 @@ class Mat33 {
|
|
317
305
|
* ```
|
318
306
|
*/
|
319
307
|
toArray() {
|
320
|
-
return [
|
321
|
-
this.a1, this.a2, this.a3,
|
322
|
-
this.b1, this.b2, this.b3,
|
323
|
-
this.c1, this.c2, this.c3,
|
324
|
-
];
|
308
|
+
return [this.a1, this.a2, this.a3, this.b1, this.b2, this.b3, this.c1, this.c2, this.c3];
|
325
309
|
}
|
326
310
|
/**
|
327
311
|
* Returns a new `Mat33` where each entry is the output of the function
|
@@ -378,6 +362,26 @@ class Mat33 {
|
|
378
362
|
// ...
|
379
363
|
return new Mat33(1, 0, amount.x, 0, 1, amount.y, 0, 0, 1);
|
380
364
|
}
|
365
|
+
/**
|
366
|
+
* Creates a matrix for rotating `Vec2`s about `center` by some number of `radians`.
|
367
|
+
*
|
368
|
+
* For this function, {@link Vec2}s are considered to be points in 2D space.
|
369
|
+
*
|
370
|
+
* For example,
|
371
|
+
* ```ts,runnable,console
|
372
|
+
* import { Mat33, Vec2 } from '@js-draw/math';
|
373
|
+
*
|
374
|
+
* const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle
|
375
|
+
* const center = Vec2.of(1, 1); // The point (1,1)
|
376
|
+
* const rotationMatrix = Mat33.zRotation(halfCircle, center);
|
377
|
+
*
|
378
|
+
* console.log(
|
379
|
+
* 'Rotating (0,0) 180deg about', center, 'results in',
|
380
|
+
* // Rotates (0,0)
|
381
|
+
* rotationMatrix.transformVec2(Vec2.zero),
|
382
|
+
* );
|
383
|
+
* ```
|
384
|
+
*/
|
381
385
|
static zRotation(radians, center = Vec2_1.Vec2.zero) {
|
382
386
|
if (radians === 0) {
|
383
387
|
return Mat33.identity;
|
@@ -427,7 +431,7 @@ class Mat33 {
|
|
427
431
|
return Mat33.identity;
|
428
432
|
}
|
429
433
|
const parseArguments = (argumentString) => {
|
430
|
-
const parsed = argumentString.split(/[, \t\n]+/g).map(argString => {
|
434
|
+
const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
|
431
435
|
// Handle trailing spaces/commands
|
432
436
|
if (argString.trim() === '') {
|
433
437
|
return null;
|
@@ -438,7 +442,7 @@ class Mat33 {
|
|
438
442
|
argString = argString.substring(0, argString.length - 1);
|
439
443
|
}
|
440
444
|
// Remove trailing px units.
|
441
|
-
argString = argString.replace(/px$/
|
445
|
+
argString = argString.replace(/px$/gi, '');
|
442
446
|
const numberExp = /^[-]?\d*(?:\.\d*)?(?:[eE][-+]?\d+)?$/i;
|
443
447
|
if (!numberExp.exec(argString)) {
|
444
448
|
throw new Error(`All arguments to transform functions must be numeric (state: ${JSON.stringify({
|
@@ -452,7 +456,7 @@ class Mat33 {
|
|
452
456
|
}
|
453
457
|
return argNumber;
|
454
458
|
});
|
455
|
-
return parsed.filter(n => n !== null);
|
459
|
+
return parsed.filter((n) => n !== null);
|
456
460
|
};
|
457
461
|
const keywordToAction = {
|
458
462
|
matrix: (matrixData) => {
|
@@ -502,7 +506,7 @@ class Mat33 {
|
|
502
506
|
};
|
503
507
|
// A command (\w+)
|
504
508
|
// followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
|
505
|
-
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/
|
509
|
+
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
|
506
510
|
let match;
|
507
511
|
let matrix = null;
|
508
512
|
while ((match = partRegex.exec(cssString)) !== null) {
|
package/dist/cjs/Vec3.d.ts
CHANGED
@@ -150,7 +150,7 @@ export interface Vec3 {
|
|
150
150
|
map(fn: (component: number, index: number) => number): Vec3;
|
151
151
|
asArray(): [number, number, number];
|
152
152
|
/**
|
153
|
-
*
|
153
|
+
* @param tolerance The maximum difference between two components for this and [other]
|
154
154
|
* to be considered equal.
|
155
155
|
*
|
156
156
|
* @example
|
@@ -200,12 +200,16 @@ declare class Vec2Impl implements Vec3 {
|
|
200
200
|
toString(): string;
|
201
201
|
}
|
202
202
|
/**
|
203
|
-
* A `Vec2` is a
|
203
|
+
* A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
|
204
204
|
* always-zero `z` component.
|
205
205
|
*
|
206
206
|
* ```ts,runnable,console
|
207
207
|
* import { Vec2 } from '@js-draw/math';
|
208
|
-
*
|
208
|
+
*
|
209
|
+
* const v = Vec2.of(1, 2);
|
210
|
+
* console.log('a Vec2:', v);
|
211
|
+
* console.log('x component:', v.x);
|
212
|
+
* console.log('z component:', v.z);
|
209
213
|
* ```
|
210
214
|
*/
|
211
215
|
export declare namespace Vec2 {
|
@@ -240,6 +244,7 @@ export declare namespace Vec2 {
|
|
240
244
|
/** The zero vector: A vector with x=0, y=0. */
|
241
245
|
const zero: Vec2Impl;
|
242
246
|
}
|
247
|
+
/** Contains static methods for constructing a {@link Vec3}. */
|
243
248
|
export declare namespace Vec3 {
|
244
249
|
/**
|
245
250
|
* Construct a vector from three components.
|
@@ -248,11 +253,15 @@ export declare namespace Vec3 {
|
|
248
253
|
* ```ts,runnable,console
|
249
254
|
* import { Vec3 } from '@js-draw/math';
|
250
255
|
* const v1 = Vec3.of(1, 2, 3);
|
256
|
+
* console.log(v1.plus(Vec3.of(0, 100, 0)));
|
251
257
|
* ```
|
252
258
|
*/
|
253
259
|
const of: (x: number, y: number, z: number) => Vec3;
|
260
|
+
/** A unit vector in the x direction (`[1, 0, 0]`). */
|
254
261
|
const unitX: Vec2Impl;
|
262
|
+
/** A unit vector in the y direction (`[0, 1, 0]`). */
|
255
263
|
const unitY: Vec2Impl;
|
264
|
+
/** The zero vector (`[0, 0, 0]`). */
|
256
265
|
const zero: Vec2Impl;
|
257
266
|
/** A vector of length 1 in the z direction. */
|
258
267
|
const unitZ: Vec3;
|
package/dist/cjs/Vec3.js
CHANGED
@@ -106,9 +106,9 @@ class Vec3Impl {
|
|
106
106
|
return [this.x, this.y, this.z];
|
107
107
|
}
|
108
108
|
eq(other, fuzz = defaultEqlTolerance) {
|
109
|
-
return (Math.abs(other.x - this.x) <= fuzz
|
110
|
-
|
111
|
-
|
109
|
+
return (Math.abs(other.x - this.x) <= fuzz &&
|
110
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
111
|
+
Math.abs(other.z - this.z) <= fuzz);
|
112
112
|
}
|
113
113
|
toString() {
|
114
114
|
return `Vec(${this.x}, ${this.y}, ${this.z})`;
|
@@ -119,7 +119,9 @@ class Vec2Impl {
|
|
119
119
|
this.x = x;
|
120
120
|
this.y = y;
|
121
121
|
}
|
122
|
-
get z() {
|
122
|
+
get z() {
|
123
|
+
return 0;
|
124
|
+
}
|
123
125
|
get xy() {
|
124
126
|
// Useful for APIs that behave differently if .z is present.
|
125
127
|
return {
|
@@ -216,21 +218,25 @@ class Vec2Impl {
|
|
216
218
|
return [this.x, this.y, 0];
|
217
219
|
}
|
218
220
|
eq(other, fuzz = defaultEqlTolerance) {
|
219
|
-
return (Math.abs(other.x - this.x) <= fuzz
|
220
|
-
|
221
|
-
|
221
|
+
return (Math.abs(other.x - this.x) <= fuzz &&
|
222
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
223
|
+
Math.abs(other.z) <= fuzz);
|
222
224
|
}
|
223
225
|
toString() {
|
224
226
|
return `Vec(${this.x}, ${this.y})`;
|
225
227
|
}
|
226
228
|
}
|
227
229
|
/**
|
228
|
-
* A `Vec2` is a
|
230
|
+
* A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
|
229
231
|
* always-zero `z` component.
|
230
232
|
*
|
231
233
|
* ```ts,runnable,console
|
232
234
|
* import { Vec2 } from '@js-draw/math';
|
233
|
-
*
|
235
|
+
*
|
236
|
+
* const v = Vec2.of(1, 2);
|
237
|
+
* console.log('a Vec2:', v);
|
238
|
+
* console.log('x component:', v.x);
|
239
|
+
* console.log('z component:', v.z);
|
234
240
|
* ```
|
235
241
|
*/
|
236
242
|
var Vec2;
|
@@ -267,6 +273,7 @@ var Vec2;
|
|
267
273
|
/** The zero vector: A vector with x=0, y=0. */
|
268
274
|
Vec2.zero = Vec2.of(0, 0);
|
269
275
|
})(Vec2 || (exports.Vec2 = Vec2 = {}));
|
276
|
+
/** Contains static methods for constructing a {@link Vec3}. */
|
270
277
|
var Vec3;
|
271
278
|
(function (Vec3) {
|
272
279
|
/**
|
@@ -276,6 +283,7 @@ var Vec3;
|
|
276
283
|
* ```ts,runnable,console
|
277
284
|
* import { Vec3 } from '@js-draw/math';
|
278
285
|
* const v1 = Vec3.of(1, 2, 3);
|
286
|
+
* console.log(v1.plus(Vec3.of(0, 100, 0)));
|
279
287
|
* ```
|
280
288
|
*/
|
281
289
|
Vec3.of = (x, y, z) => {
|
@@ -286,8 +294,11 @@ var Vec3;
|
|
286
294
|
return new Vec3Impl(x, y, z);
|
287
295
|
}
|
288
296
|
};
|
297
|
+
/** A unit vector in the x direction (`[1, 0, 0]`). */
|
289
298
|
Vec3.unitX = Vec2.unitX;
|
299
|
+
/** A unit vector in the y direction (`[0, 1, 0]`). */
|
290
300
|
Vec3.unitY = Vec2.unitY;
|
301
|
+
/** The zero vector (`[0, 0, 0]`). */
|
291
302
|
Vec3.zero = Vec2.zero;
|
292
303
|
/** A vector of length 1 in the z direction. */
|
293
304
|
Vec3.unitZ = Vec3.of(0, 0, 1);
|
package/dist/cjs/lib.d.ts
CHANGED
package/dist/cjs/lib.js
CHANGED
@@ -29,8 +29,10 @@ export declare abstract class BezierJSWrapper extends Parameterized2DShape {
|
|
29
29
|
* @returns the curve evaluated at `t`.
|
30
30
|
*/
|
31
31
|
at(t: number): Point2;
|
32
|
+
/** @returns the curve's directional derivative at `t`. */
|
32
33
|
derivativeAt(t: number): Point2;
|
33
34
|
secondDerivativeAt(t: number): Point2;
|
35
|
+
/** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
|
34
36
|
normal(t: number): Vec2;
|
35
37
|
normalAt(t: number): Vec2;
|
36
38
|
tangentAt(t: number): Vec2;
|
@@ -40,7 +40,7 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
40
40
|
}
|
41
41
|
getBezier() {
|
42
42
|
if (!__classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f")) {
|
43
|
-
__classPrivateFieldSet(this, _BezierJSWrapper_bezierJs, new bezier_js_1.Bezier(this.getPoints().map(p => p.xy)), "f");
|
43
|
+
__classPrivateFieldSet(this, _BezierJSWrapper_bezierJs, new bezier_js_1.Bezier(this.getPoints().map((p) => p.xy)), "f");
|
44
44
|
}
|
45
45
|
return __classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f");
|
46
46
|
}
|
@@ -63,12 +63,14 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
63
63
|
at(t) {
|
64
64
|
return Vec2_1.Vec2.ofXY(this.getBezier().get(t));
|
65
65
|
}
|
66
|
+
/** @returns the curve's directional derivative at `t`. */
|
66
67
|
derivativeAt(t) {
|
67
68
|
return Vec2_1.Vec2.ofXY(this.getBezier().derivative(t));
|
68
69
|
}
|
69
70
|
secondDerivativeAt(t) {
|
70
71
|
return Vec2_1.Vec2.ofXY(this.getBezier().dderivative(t));
|
71
72
|
}
|
73
|
+
/** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
|
72
74
|
normal(t) {
|
73
75
|
return Vec2_1.Vec2.ofXY(this.getBezier().normal(t));
|
74
76
|
}
|
@@ -94,10 +96,12 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
94
96
|
const asLine = LineSegment2_1.default.ofSmallestContainingPoints(this.getPoints());
|
95
97
|
if (asLine) {
|
96
98
|
const intersection = asLine.intersectsLineSegment(line);
|
97
|
-
return intersection.map(p => this.nearestPointTo(p).parameterValue);
|
99
|
+
return intersection.map((p) => this.nearestPointTo(p).parameterValue);
|
98
100
|
}
|
99
101
|
const bezier = this.getBezier();
|
100
|
-
return bezier
|
102
|
+
return bezier
|
103
|
+
.intersects(line)
|
104
|
+
.map((t) => {
|
101
105
|
// We're using the .intersects(line) function, which is documented
|
102
106
|
// to always return numbers. However, to satisfy the type checker (and
|
103
107
|
// possibly improperly-defined types),
|
@@ -106,12 +110,12 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
106
110
|
}
|
107
111
|
const point = Vec2_1.Vec2.ofXY(this.at(t));
|
108
112
|
// Ensure that the intersection is on the line segment
|
109
|
-
if (point.distanceTo(line.p1) > line.length
|
110
|
-
|| point.distanceTo(line.p2) > line.length) {
|
113
|
+
if (point.distanceTo(line.p1) > line.length || point.distanceTo(line.p2) > line.length) {
|
111
114
|
return null;
|
112
115
|
}
|
113
116
|
return t;
|
114
|
-
})
|
117
|
+
})
|
118
|
+
.filter((entry) => entry !== null);
|
115
119
|
}
|
116
120
|
splitAt(t) {
|
117
121
|
if (t <= 0 || t >= 1) {
|
@@ -120,8 +124,8 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
120
124
|
const bezier = this.getBezier();
|
121
125
|
const split = bezier.split(t);
|
122
126
|
return [
|
123
|
-
new BezierJSWrapperImpl(split.left.points.map(point => Vec2_1.Vec2.ofXY(point)), split.left),
|
124
|
-
new BezierJSWrapperImpl(split.right.points.map(point => Vec2_1.Vec2.ofXY(point)), split.right),
|
127
|
+
new BezierJSWrapperImpl(split.left.points.map((point) => Vec2_1.Vec2.ofXY(point)), split.left),
|
128
|
+
new BezierJSWrapperImpl(split.right.points.map((point) => Vec2_1.Vec2.ofXY(point)), split.right),
|
125
129
|
];
|
126
130
|
}
|
127
131
|
nearestPointTo(point) {
|
@@ -165,16 +169,19 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
165
169
|
const b = this.at(t);
|
166
170
|
const bPrime = this.derivativeAt(t);
|
167
171
|
const bPrimePrime = this.secondDerivativeAt(t);
|
168
|
-
return (2 * bPrime.x * bPrime.x +
|
169
|
-
|
172
|
+
return (2 * bPrime.x * bPrime.x +
|
173
|
+
2 * b.x * bPrimePrime.x -
|
174
|
+
2 * point.x * bPrimePrime.x +
|
175
|
+
2 * bPrime.y * bPrime.y +
|
176
|
+
2 * b.y * bPrimePrime.y -
|
177
|
+
2 * point.y * bPrimePrime.y);
|
170
178
|
};
|
171
179
|
// Because we're zeroing f'(t), we also need to be able to compute it:
|
172
180
|
const derivativeAt = (t) => {
|
173
181
|
// f'(t) = 2Bₓ(t)Bₓ'(t) - 2pₓBₓ'(t) + 2Bᵧ(t)Bᵧ'(t) - 2pᵧBᵧ'(t)
|
174
182
|
const b = this.at(t);
|
175
183
|
const bPrime = this.derivativeAt(t);
|
176
|
-
return (2 * b.x * bPrime.x - 2 * point.x * bPrime.x
|
177
|
-
+ 2 * b.y * bPrime.y - 2 * point.y * bPrime.y);
|
184
|
+
return (2 * b.x * bPrime.x - 2 * point.x * bPrime.x + 2 * b.y * bPrime.y - 2 * point.y * bPrime.y);
|
178
185
|
};
|
179
186
|
const iterate = () => {
|
180
187
|
const slope = secondDerivativeAt(t);
|
@@ -225,7 +232,9 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
225
232
|
return result;
|
226
233
|
}
|
227
234
|
toString() {
|
228
|
-
return `Bézier(${this.getPoints()
|
235
|
+
return `Bézier(${this.getPoints()
|
236
|
+
.map((point) => point.toString())
|
237
|
+
.join(', ')})`;
|
229
238
|
}
|
230
239
|
}
|
231
240
|
exports.BezierJSWrapper = BezierJSWrapper;
|
@@ -46,7 +46,7 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
46
46
|
static ofSmallestContainingPoints(points) {
|
47
47
|
if (points.length <= 1)
|
48
48
|
return null;
|
49
|
-
const sorted = [...points].sort((a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y);
|
49
|
+
const sorted = [...points].sort((a, b) => (a.x !== b.x ? a.x - b.x : a.y - b.y));
|
50
50
|
const line = new LineSegment2(sorted[0], sorted[sorted.length - 1]);
|
51
51
|
for (const point of sorted) {
|
52
52
|
if (!line.containsPoint(point)) {
|
@@ -96,10 +96,7 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
96
96
|
if (t <= 0 || t >= 1) {
|
97
97
|
return [this];
|
98
98
|
}
|
99
|
-
return [
|
100
|
-
new LineSegment2(this.point1, this.at(t)),
|
101
|
-
new LineSegment2(this.at(t), this.point2),
|
102
|
-
];
|
99
|
+
return [new LineSegment2(this.point1, this.at(t)), new LineSegment2(this.at(t), this.point2)];
|
103
100
|
}
|
104
101
|
/**
|
105
102
|
* Returns the intersection of this with another line segment.
|
@@ -149,18 +146,17 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
149
146
|
return null;
|
150
147
|
}
|
151
148
|
const xIntersect = this.point1.x;
|
152
|
-
const yIntersect = (this.point1.x - other.point1.x) * other.direction.y / other.direction.x + other.point1.y;
|
149
|
+
const yIntersect = ((this.point1.x - other.point1.x) * other.direction.y) / other.direction.x + other.point1.y;
|
153
150
|
resultPoint = Vec2_1.Vec2.of(xIntersect, yIntersect);
|
154
151
|
resultT = (yIntersect - this.point1.y) / this.direction.y;
|
155
152
|
}
|
156
153
|
else {
|
157
154
|
// From above,
|
158
155
|
// x = ((o₁ᵧ - o₂ᵧ)(d₁ₓd₂ₓ) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
|
159
|
-
const numerator = (
|
160
|
-
|
161
|
-
|
162
|
-
const denominator =
|
163
|
-
- this.direction.y * other.direction.x);
|
156
|
+
const numerator = (this.point1.y - other.point1.y) * this.direction.x * other.direction.x +
|
157
|
+
this.direction.x * other.direction.y * other.point1.x -
|
158
|
+
this.direction.y * other.direction.x * this.point1.x;
|
159
|
+
const denominator = other.direction.y * this.direction.x - this.direction.y * other.direction.x;
|
164
160
|
// Avoid dividing by zero. It means there is no intersection
|
165
161
|
if (denominator === 0) {
|
166
162
|
return null;
|
@@ -176,10 +172,10 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
176
172
|
const resultToP2 = resultPoint.distanceTo(this.point2);
|
177
173
|
const resultToP3 = resultPoint.distanceTo(other.point1);
|
178
174
|
const resultToP4 = resultPoint.distanceTo(other.point2);
|
179
|
-
if (resultToP1 > this.length
|
180
|
-
|
181
|
-
|
182
|
-
|
175
|
+
if (resultToP1 > this.length ||
|
176
|
+
resultToP2 > this.length ||
|
177
|
+
resultToP3 > other.length ||
|
178
|
+
resultToP4 > other.length) {
|
183
179
|
return null;
|
184
180
|
}
|
185
181
|
return {
|
@@ -264,8 +260,8 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
264
260
|
}
|
265
261
|
const tolerance = options?.tolerance;
|
266
262
|
const ignoreDirection = options?.ignoreDirection ?? true;
|
267
|
-
return ((other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance))
|
268
|
-
|
263
|
+
return ((other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance)) ||
|
264
|
+
(ignoreDirection && other.p1.eq(this.p2, tolerance) && other.p2.eq(this.p1, tolerance)));
|
269
265
|
}
|
270
266
|
}
|
271
267
|
exports.LineSegment2 = LineSegment2;
|
@@ -13,7 +13,7 @@ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
|
|
13
13
|
*/
|
14
14
|
class Parameterized2DShape extends Abstract2DShape_1.default {
|
15
15
|
intersectsLineSegment(line) {
|
16
|
-
return this.argIntersectsLineSegment(line).map(t => this.at(t));
|
16
|
+
return this.argIntersectsLineSegment(line).map((t) => this.at(t));
|
17
17
|
}
|
18
18
|
}
|
19
19
|
exports.Parameterized2DShape = Parameterized2DShape;
|
@@ -3,6 +3,7 @@ import Mat33 from '../Mat33';
|
|
3
3
|
import Rect2 from './Rect2';
|
4
4
|
import { Point2 } from '../Vec2';
|
5
5
|
import Parameterized2DShape from './Parameterized2DShape';
|
6
|
+
/** Identifiers for different path commands. These commands can make up a {@link Path}. */
|
6
7
|
export declare enum PathCommandType {
|
7
8
|
LineTo = 0,
|
8
9
|
MoveTo = 1,
|