@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.
Files changed (81) hide show
  1. package/build-config.json +1 -1
  2. package/dist/cjs/Color4.d.ts +24 -1
  3. package/dist/cjs/Color4.js +35 -3
  4. package/dist/cjs/Mat33.d.ts +21 -11
  5. package/dist/cjs/Mat33.js +28 -24
  6. package/dist/cjs/Vec3.d.ts +12 -3
  7. package/dist/cjs/Vec3.js +20 -9
  8. package/dist/cjs/lib.d.ts +3 -0
  9. package/dist/cjs/lib.js +3 -0
  10. package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
  11. package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
  12. package/dist/cjs/shapes/LineSegment2.js +13 -17
  13. package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
  14. package/dist/cjs/shapes/Path.d.ts +1 -0
  15. package/dist/cjs/shapes/Path.js +50 -47
  16. package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
  17. package/dist/cjs/shapes/QuadraticBezier.js +26 -3
  18. package/dist/cjs/shapes/Rect2.d.ts +13 -0
  19. package/dist/cjs/shapes/Rect2.js +35 -16
  20. package/dist/cjs/shapes/Triangle.js +4 -5
  21. package/dist/cjs/utils/convexHull2Of.js +3 -3
  22. package/dist/mjs/Color4.d.ts +24 -1
  23. package/dist/mjs/Color4.mjs +35 -3
  24. package/dist/mjs/Mat33.d.ts +21 -11
  25. package/dist/mjs/Mat33.mjs +28 -24
  26. package/dist/mjs/Vec3.d.ts +12 -3
  27. package/dist/mjs/Vec3.mjs +20 -9
  28. package/dist/mjs/lib.d.ts +3 -0
  29. package/dist/mjs/lib.mjs +3 -0
  30. package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
  31. package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
  32. package/dist/mjs/shapes/LineSegment2.mjs +13 -17
  33. package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
  34. package/dist/mjs/shapes/Path.d.ts +1 -0
  35. package/dist/mjs/shapes/Path.mjs +50 -47
  36. package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
  37. package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
  38. package/dist/mjs/shapes/Rect2.d.ts +13 -0
  39. package/dist/mjs/shapes/Rect2.mjs +35 -16
  40. package/dist/mjs/shapes/Triangle.mjs +4 -5
  41. package/dist/mjs/utils/convexHull2Of.mjs +3 -3
  42. package/dist-test/test_imports/test-require.cjs +1 -1
  43. package/package.json +3 -3
  44. package/src/Color4.test.ts +21 -21
  45. package/src/Color4.ts +61 -18
  46. package/src/Mat33.fromCSSMatrix.test.ts +32 -46
  47. package/src/Mat33.test.ts +64 -102
  48. package/src/Mat33.ts +81 -104
  49. package/src/Vec2.test.ts +3 -3
  50. package/src/Vec3.test.ts +2 -3
  51. package/src/Vec3.ts +46 -61
  52. package/src/lib.ts +3 -2
  53. package/src/polynomial/solveQuadratic.test.ts +39 -13
  54. package/src/polynomial/solveQuadratic.ts +5 -6
  55. package/src/rounding/cleanUpNumber.test.ts +1 -1
  56. package/src/rounding/constants.ts +1 -3
  57. package/src/rounding/getLenAfterDecimal.ts +1 -2
  58. package/src/rounding/lib.ts +1 -2
  59. package/src/rounding/toRoundedString.test.ts +1 -1
  60. package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
  61. package/src/rounding/toStringOfSamePrecision.ts +1 -1
  62. package/src/shapes/BezierJSWrapper.ts +56 -37
  63. package/src/shapes/CubicBezier.ts +3 -3
  64. package/src/shapes/LineSegment2.test.ts +24 -17
  65. package/src/shapes/LineSegment2.ts +26 -29
  66. package/src/shapes/Parameterized2DShape.ts +5 -4
  67. package/src/shapes/Path.fromString.test.ts +5 -5
  68. package/src/shapes/Path.test.ts +122 -120
  69. package/src/shapes/Path.toString.test.ts +7 -7
  70. package/src/shapes/Path.ts +379 -352
  71. package/src/shapes/PointShape2D.ts +3 -3
  72. package/src/shapes/QuadraticBezier.test.ts +27 -21
  73. package/src/shapes/QuadraticBezier.ts +26 -11
  74. package/src/shapes/Rect2.test.ts +44 -75
  75. package/src/shapes/Rect2.ts +47 -35
  76. package/src/shapes/Triangle.test.ts +31 -29
  77. package/src/shapes/Triangle.ts +17 -18
  78. package/src/utils/convexHull2Of.test.ts +54 -15
  79. package/src/utils/convexHull2Of.ts +9 -7
  80. package/tsconfig.json +1 -3
  81. package/typedoc.json +2 -2
package/build-config.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "inDirectory": "./src",
3
3
  "outDirectory": "./dist"
4
- }
4
+ }
@@ -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;
@@ -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
  }
@@ -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
- this.rows[0],
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$/ig, '');
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*\(([^)]*)\)/ig;
509
+ const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
506
510
  let match;
507
511
  let matrix = null;
508
512
  while ((match = partRegex.exec(cssString)) !== null) {
@@ -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
- * [fuzz] The maximum difference between two components for this and [other]
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 `Vec3` optimized for working in a plane. As such, they have an
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
- * console.log(Vec2.of(1, 2));
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
- && Math.abs(other.y - this.y) <= fuzz
111
- && Math.abs(other.z - this.z) <= fuzz);
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() { return 0; }
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
- && Math.abs(other.y - this.y) <= fuzz
221
- && Math.abs(other.z) <= fuzz);
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 `Vec3` optimized for working in a plane. As such, they have an
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
- * console.log(Vec2.of(1, 2));
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
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * This package contains general math utilities used by `js-draw`.
3
+ * These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
4
+ *
2
5
  * ```ts,runnable,console
3
6
  * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
4
7
  *
package/dist/cjs/lib.js CHANGED
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  /**
3
+ * This package contains general math utilities used by `js-draw`.
4
+ * These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
5
+ *
3
6
  * ```ts,runnable,console
4
7
  * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
5
8
  *
@@ -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.intersects(line).map(t => {
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
- }).filter(entry => entry !== null);
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 + 2 * b.x * bPrimePrime.x - 2 * point.x * bPrimePrime.x
169
- + 2 * bPrime.y * bPrime.y + 2 * b.y * bPrimePrime.y - 2 * point.y * bPrimePrime.y);
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().map(point => point.toString()).join(', ')})`;
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 = ((this.point1.y - other.point1.y) * this.direction.x * other.direction.x
160
- + this.direction.x * other.direction.y * other.point1.x
161
- - this.direction.y * other.direction.x * this.point1.x);
162
- const denominator = (other.direction.y * this.direction.x
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
- || resultToP2 > this.length
181
- || resultToP3 > other.length
182
- || resultToP4 > other.length) {
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
- || (ignoreDirection && other.p1.eq(this.p2, tolerance) && other.p2.eq(this.p1, tolerance)));
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,