@js-draw/math 1.21.3 → 1.23.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,
|