@js-draw/math 1.9.0 → 1.10.0

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/dist/cjs/lib.d.ts CHANGED
@@ -20,6 +20,7 @@ export { LineSegment2 } from './shapes/LineSegment2';
20
20
  export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
21
21
  export { Rect2 } from './shapes/Rect2';
22
22
  export { QuadraticBezier } from './shapes/QuadraticBezier';
23
+ export { Abstract2DShape } from './shapes/Abstract2DShape';
23
24
  export { Mat33, Mat33Array } from './Mat33';
24
25
  export { Point2, Vec2 } from './Vec2';
25
26
  export { Vec3 } from './Vec3';
package/dist/cjs/lib.js CHANGED
@@ -18,7 +18,7 @@
18
18
  * @packageDocumentation
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.toRoundedString = exports.Color4 = exports.Vec3 = exports.Vec2 = exports.Mat33 = exports.QuadraticBezier = exports.Rect2 = exports.PathCommandType = exports.Path = exports.LineSegment2 = void 0;
21
+ exports.toRoundedString = exports.Color4 = exports.Vec3 = exports.Vec2 = exports.Mat33 = exports.Abstract2DShape = exports.QuadraticBezier = exports.Rect2 = exports.PathCommandType = exports.Path = exports.LineSegment2 = void 0;
22
22
  var LineSegment2_1 = require("./shapes/LineSegment2");
23
23
  Object.defineProperty(exports, "LineSegment2", { enumerable: true, get: function () { return LineSegment2_1.LineSegment2; } });
24
24
  var Path_1 = require("./shapes/Path");
@@ -28,6 +28,8 @@ var Rect2_1 = require("./shapes/Rect2");
28
28
  Object.defineProperty(exports, "Rect2", { enumerable: true, get: function () { return Rect2_1.Rect2; } });
29
29
  var QuadraticBezier_1 = require("./shapes/QuadraticBezier");
30
30
  Object.defineProperty(exports, "QuadraticBezier", { enumerable: true, get: function () { return QuadraticBezier_1.QuadraticBezier; } });
31
+ var Abstract2DShape_1 = require("./shapes/Abstract2DShape");
32
+ Object.defineProperty(exports, "Abstract2DShape", { enumerable: true, get: function () { return Abstract2DShape_1.Abstract2DShape; } });
31
33
  var Mat33_1 = require("./Mat33");
32
34
  Object.defineProperty(exports, "Mat33", { enumerable: true, get: function () { return Mat33_1.Mat33; } });
33
35
  var Vec2_1 = require("./Vec2");
@@ -1,7 +1,10 @@
1
1
  import LineSegment2 from './LineSegment2';
2
2
  import { Point2 } from '../Vec2';
3
3
  import Rect2 from './Rect2';
4
- declare abstract class Abstract2DShape {
4
+ /**
5
+ * An abstract base class for 2D shapes.
6
+ */
7
+ export declare abstract class Abstract2DShape {
5
8
  protected static readonly smallValue = 1e-12;
6
9
  /**
7
10
  * @returns the distance from `point` to this shape. If `point` is within this shape,
@@ -1,5 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Abstract2DShape = void 0;
4
+ /**
5
+ * An abstract base class for 2D shapes.
6
+ */
3
7
  class Abstract2DShape {
4
8
  /**
5
9
  * @returns the distance from `point` to this shape. If `point` is within this shape,
@@ -34,5 +38,7 @@ class Abstract2DShape {
34
38
  return this.getTightBoundingBox();
35
39
  }
36
40
  }
41
+ exports.Abstract2DShape = Abstract2DShape;
42
+ // @internal
37
43
  Abstract2DShape.smallValue = 1e-12;
38
44
  exports.default = Abstract2DShape;
@@ -35,24 +35,29 @@ interface IntersectionResult {
35
35
  parameterValue?: number;
36
36
  point: Point2;
37
37
  }
38
- type GeometryType = Abstract2DShape;
39
- type GeometryArrayType = Array<GeometryType>;
40
38
  /**
41
39
  * Represents a union of lines and curves.
42
40
  */
43
41
  export declare class Path {
44
42
  readonly startPoint: Point2;
45
- readonly parts: PathCommand[];
46
43
  /**
47
44
  * A rough estimate of the bounding box of the path.
48
45
  * A slight overestimate.
49
46
  * See {@link getExactBBox}
50
47
  */
51
48
  readonly bbox: Rect2;
52
- constructor(startPoint: Point2, parts: PathCommand[]);
49
+ /** The individual shapes that make up this path. */
50
+ readonly parts: Readonly<PathCommand>[];
51
+ /**
52
+ * Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
53
+ * `parts`.
54
+ *
55
+ * See also {@link fromString}
56
+ */
57
+ constructor(startPoint: Point2, parts: Readonly<PathCommand>[]);
53
58
  getExactBBox(): Rect2;
54
59
  private cachedGeometry;
55
- get geometry(): GeometryArrayType;
60
+ get geometry(): Abstract2DShape[];
56
61
  /**
57
62
  * Iterates through the start/end points of each component in this path.
58
63
  *
@@ -77,6 +82,8 @@ export declare class Path {
77
82
  * intersections are approximated with the surface `strokeRadius` away from this.
78
83
  *
79
84
  * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
85
+ *
86
+ * **Note**: `strokeRadius` is half of a stroke's width.
80
87
  */
81
88
  intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
82
89
  private static mapPathCommand;
@@ -84,6 +91,16 @@ export declare class Path {
84
91
  transformedBy(affineTransfm: Mat33): Path;
85
92
  union(other: Path | null): Path;
86
93
  private getEndPoint;
94
+ /**
95
+ * Like {@link closedRoughlyIntersects} except takes stroke width into account.
96
+ *
97
+ * This is intended to be a very fast and rough approximation. Use {@link intersection}
98
+ * and {@link signedDistance} for more accurate (but much slower) intersection calculations.
99
+ *
100
+ * **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
101
+ *
102
+ * `strokeRadius` is half of `strokeWidth`.
103
+ */
87
104
  roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
88
105
  closedRoughlyIntersects(rect: Rect2): boolean;
89
106
  /**
@@ -95,16 +112,32 @@ export declare class Path {
95
112
  */
96
113
  static fromRect(rect: Rect2, lineWidth?: number | null): Path;
97
114
  private cachedStringVersion;
98
- toString(useNonAbsCommands?: boolean): string;
115
+ /**
116
+ * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
117
+ *
118
+ * If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
119
+ * absolute commands (e.g. `L10,0`).
120
+ *
121
+ * See also {@link fromString}.
122
+ */
123
+ toString(useNonAbsCommands?: boolean, ignoreCache?: boolean): string;
99
124
  serialize(): string;
100
125
  static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string;
101
126
  /**
102
- * Create a Path from a SVG path specification.
127
+ * Create a `Path` from a subset of the SVG path specification.
103
128
  *
104
129
  * ## To-do
105
130
  * - TODO: Support a larger subset of SVG paths
106
131
  * - Elliptical arcs are currently unsupported.
107
132
  * - TODO: Support `s`,`t` commands shorthands.
133
+ *
134
+ * @example
135
+ * ```ts,runnable,console
136
+ * import { Path } from '@js-draw/math';
137
+ *
138
+ * const path = Path.fromString('m0,0l100,100');
139
+ * console.log(path.toString(true)); // true: Prefer relative to absolute path commands
140
+ * ```
108
141
  */
109
142
  static fromString(pathString: string): Path;
110
143
  static empty: Path;
@@ -22,17 +22,23 @@ var PathCommandType;
22
22
  * Represents a union of lines and curves.
23
23
  */
24
24
  class Path {
25
+ /**
26
+ * Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
27
+ * `parts`.
28
+ *
29
+ * See also {@link fromString}
30
+ */
25
31
  constructor(startPoint, parts) {
26
32
  this.startPoint = startPoint;
27
- this.parts = parts;
28
33
  this.cachedGeometry = null;
29
34
  this.cachedPolylineApproximation = null;
30
35
  this.cachedStringVersion = null;
36
+ this.parts = parts;
31
37
  // Initial bounding box contains one point: the start point.
32
38
  this.bbox = Rect2_1.default.bboxOf([startPoint]);
33
39
  // Convert into a representation of the geometry (cache for faster intersection
34
40
  // calculation)
35
- for (const part of parts) {
41
+ for (const part of this.parts) {
36
42
  this.bbox = this.bbox.union(Path.computeBBoxForSegment(startPoint, part));
37
43
  }
38
44
  }
@@ -337,6 +343,8 @@ class Path {
337
343
  * intersections are approximated with the surface `strokeRadius` away from this.
338
344
  *
339
345
  * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
346
+ *
347
+ * **Note**: `strokeRadius` is half of a stroke's width.
340
348
  */
341
349
  intersection(line, strokeRadius) {
342
350
  let result = [];
@@ -432,6 +440,16 @@ class Path {
432
440
  return lastPart.point;
433
441
  }
434
442
  }
443
+ /**
444
+ * Like {@link closedRoughlyIntersects} except takes stroke width into account.
445
+ *
446
+ * This is intended to be a very fast and rough approximation. Use {@link intersection}
447
+ * and {@link signedDistance} for more accurate (but much slower) intersection calculations.
448
+ *
449
+ * **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
450
+ *
451
+ * `strokeRadius` is half of `strokeWidth`.
452
+ */
435
453
  roughlyIntersects(rect, strokeWidth = 0) {
436
454
  if (this.parts.length === 0) {
437
455
  return rect.containsPoint(this.startPoint);
@@ -459,7 +477,7 @@ class Path {
459
477
  }
460
478
  return false;
461
479
  }
462
- // Treats this as a closed path and returns true if part of `rect` is roughly within
480
+ // Treats this as a closed path and returns true if part of `rect` is *roughly* within
463
481
  // this path's interior.
464
482
  //
465
483
  // Note: Assumes that this is a closed, non-self-intersecting path.
@@ -541,8 +559,16 @@ class Path {
541
559
  });
542
560
  return new Path(startPoint, commands);
543
561
  }
544
- toString(useNonAbsCommands) {
545
- if (this.cachedStringVersion) {
562
+ /**
563
+ * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
564
+ *
565
+ * If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
566
+ * absolute commands (e.g. `L10,0`).
567
+ *
568
+ * See also {@link fromString}.
569
+ */
570
+ toString(useNonAbsCommands, ignoreCache = false) {
571
+ if (this.cachedStringVersion && !ignoreCache) {
546
572
  return this.cachedStringVersion;
547
573
  }
548
574
  if (useNonAbsCommands === undefined) {
@@ -632,12 +658,20 @@ class Path {
632
658
  return result.join('');
633
659
  }
634
660
  /**
635
- * Create a Path from a SVG path specification.
661
+ * Create a `Path` from a subset of the SVG path specification.
636
662
  *
637
663
  * ## To-do
638
664
  * - TODO: Support a larger subset of SVG paths
639
665
  * - Elliptical arcs are currently unsupported.
640
666
  * - TODO: Support `s`,`t` commands shorthands.
667
+ *
668
+ * @example
669
+ * ```ts,runnable,console
670
+ * import { Path } from '@js-draw/math';
671
+ *
672
+ * const path = Path.fromString('m0,0l100,100');
673
+ * console.log(path.toString(true)); // true: Prefer relative to absolute path commands
674
+ * ```
641
675
  */
642
676
  static fromString(pathString) {
643
677
  // See the MDN reference:
package/dist/mjs/lib.d.ts CHANGED
@@ -20,6 +20,7 @@ export { LineSegment2 } from './shapes/LineSegment2';
20
20
  export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
21
21
  export { Rect2 } from './shapes/Rect2';
22
22
  export { QuadraticBezier } from './shapes/QuadraticBezier';
23
+ export { Abstract2DShape } from './shapes/Abstract2DShape';
23
24
  export { Mat33, Mat33Array } from './Mat33';
24
25
  export { Point2, Vec2 } from './Vec2';
25
26
  export { Vec3 } from './Vec3';
package/dist/mjs/lib.mjs CHANGED
@@ -20,6 +20,7 @@ export { LineSegment2 } from './shapes/LineSegment2.mjs';
20
20
  export { Path, PathCommandType, } from './shapes/Path.mjs';
21
21
  export { Rect2 } from './shapes/Rect2.mjs';
22
22
  export { QuadraticBezier } from './shapes/QuadraticBezier.mjs';
23
+ export { Abstract2DShape } from './shapes/Abstract2DShape.mjs';
23
24
  export { Mat33 } from './Mat33.mjs';
24
25
  export { Vec2 } from './Vec2.mjs';
25
26
  export { Vec3 } from './Vec3.mjs';
@@ -1,7 +1,10 @@
1
1
  import LineSegment2 from './LineSegment2';
2
2
  import { Point2 } from '../Vec2';
3
3
  import Rect2 from './Rect2';
4
- declare abstract class Abstract2DShape {
4
+ /**
5
+ * An abstract base class for 2D shapes.
6
+ */
7
+ export declare abstract class Abstract2DShape {
5
8
  protected static readonly smallValue = 1e-12;
6
9
  /**
7
10
  * @returns the distance from `point` to this shape. If `point` is within this shape,
@@ -1,4 +1,7 @@
1
- class Abstract2DShape {
1
+ /**
2
+ * An abstract base class for 2D shapes.
3
+ */
4
+ export class Abstract2DShape {
2
5
  /**
3
6
  * @returns the distance from `point` to this shape. If `point` is within this shape,
4
7
  * this returns the distance from `point` to the edge of this shape.
@@ -32,5 +35,6 @@ class Abstract2DShape {
32
35
  return this.getTightBoundingBox();
33
36
  }
34
37
  }
38
+ // @internal
35
39
  Abstract2DShape.smallValue = 1e-12;
36
40
  export default Abstract2DShape;
@@ -35,24 +35,29 @@ interface IntersectionResult {
35
35
  parameterValue?: number;
36
36
  point: Point2;
37
37
  }
38
- type GeometryType = Abstract2DShape;
39
- type GeometryArrayType = Array<GeometryType>;
40
38
  /**
41
39
  * Represents a union of lines and curves.
42
40
  */
43
41
  export declare class Path {
44
42
  readonly startPoint: Point2;
45
- readonly parts: PathCommand[];
46
43
  /**
47
44
  * A rough estimate of the bounding box of the path.
48
45
  * A slight overestimate.
49
46
  * See {@link getExactBBox}
50
47
  */
51
48
  readonly bbox: Rect2;
52
- constructor(startPoint: Point2, parts: PathCommand[]);
49
+ /** The individual shapes that make up this path. */
50
+ readonly parts: Readonly<PathCommand>[];
51
+ /**
52
+ * Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
53
+ * `parts`.
54
+ *
55
+ * See also {@link fromString}
56
+ */
57
+ constructor(startPoint: Point2, parts: Readonly<PathCommand>[]);
53
58
  getExactBBox(): Rect2;
54
59
  private cachedGeometry;
55
- get geometry(): GeometryArrayType;
60
+ get geometry(): Abstract2DShape[];
56
61
  /**
57
62
  * Iterates through the start/end points of each component in this path.
58
63
  *
@@ -77,6 +82,8 @@ export declare class Path {
77
82
  * intersections are approximated with the surface `strokeRadius` away from this.
78
83
  *
79
84
  * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
85
+ *
86
+ * **Note**: `strokeRadius` is half of a stroke's width.
80
87
  */
81
88
  intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
82
89
  private static mapPathCommand;
@@ -84,6 +91,16 @@ export declare class Path {
84
91
  transformedBy(affineTransfm: Mat33): Path;
85
92
  union(other: Path | null): Path;
86
93
  private getEndPoint;
94
+ /**
95
+ * Like {@link closedRoughlyIntersects} except takes stroke width into account.
96
+ *
97
+ * This is intended to be a very fast and rough approximation. Use {@link intersection}
98
+ * and {@link signedDistance} for more accurate (but much slower) intersection calculations.
99
+ *
100
+ * **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
101
+ *
102
+ * `strokeRadius` is half of `strokeWidth`.
103
+ */
87
104
  roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
88
105
  closedRoughlyIntersects(rect: Rect2): boolean;
89
106
  /**
@@ -95,16 +112,32 @@ export declare class Path {
95
112
  */
96
113
  static fromRect(rect: Rect2, lineWidth?: number | null): Path;
97
114
  private cachedStringVersion;
98
- toString(useNonAbsCommands?: boolean): string;
115
+ /**
116
+ * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
117
+ *
118
+ * If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
119
+ * absolute commands (e.g. `L10,0`).
120
+ *
121
+ * See also {@link fromString}.
122
+ */
123
+ toString(useNonAbsCommands?: boolean, ignoreCache?: boolean): string;
99
124
  serialize(): string;
100
125
  static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string;
101
126
  /**
102
- * Create a Path from a SVG path specification.
127
+ * Create a `Path` from a subset of the SVG path specification.
103
128
  *
104
129
  * ## To-do
105
130
  * - TODO: Support a larger subset of SVG paths
106
131
  * - Elliptical arcs are currently unsupported.
107
132
  * - TODO: Support `s`,`t` commands shorthands.
133
+ *
134
+ * @example
135
+ * ```ts,runnable,console
136
+ * import { Path } from '@js-draw/math';
137
+ *
138
+ * const path = Path.fromString('m0,0l100,100');
139
+ * console.log(path.toString(true)); // true: Prefer relative to absolute path commands
140
+ * ```
108
141
  */
109
142
  static fromString(pathString: string): Path;
110
143
  static empty: Path;
@@ -16,17 +16,23 @@ export var PathCommandType;
16
16
  * Represents a union of lines and curves.
17
17
  */
18
18
  export class Path {
19
+ /**
20
+ * Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
21
+ * `parts`.
22
+ *
23
+ * See also {@link fromString}
24
+ */
19
25
  constructor(startPoint, parts) {
20
26
  this.startPoint = startPoint;
21
- this.parts = parts;
22
27
  this.cachedGeometry = null;
23
28
  this.cachedPolylineApproximation = null;
24
29
  this.cachedStringVersion = null;
30
+ this.parts = parts;
25
31
  // Initial bounding box contains one point: the start point.
26
32
  this.bbox = Rect2.bboxOf([startPoint]);
27
33
  // Convert into a representation of the geometry (cache for faster intersection
28
34
  // calculation)
29
- for (const part of parts) {
35
+ for (const part of this.parts) {
30
36
  this.bbox = this.bbox.union(Path.computeBBoxForSegment(startPoint, part));
31
37
  }
32
38
  }
@@ -331,6 +337,8 @@ export class Path {
331
337
  * intersections are approximated with the surface `strokeRadius` away from this.
332
338
  *
333
339
  * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
340
+ *
341
+ * **Note**: `strokeRadius` is half of a stroke's width.
334
342
  */
335
343
  intersection(line, strokeRadius) {
336
344
  let result = [];
@@ -426,6 +434,16 @@ export class Path {
426
434
  return lastPart.point;
427
435
  }
428
436
  }
437
+ /**
438
+ * Like {@link closedRoughlyIntersects} except takes stroke width into account.
439
+ *
440
+ * This is intended to be a very fast and rough approximation. Use {@link intersection}
441
+ * and {@link signedDistance} for more accurate (but much slower) intersection calculations.
442
+ *
443
+ * **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
444
+ *
445
+ * `strokeRadius` is half of `strokeWidth`.
446
+ */
429
447
  roughlyIntersects(rect, strokeWidth = 0) {
430
448
  if (this.parts.length === 0) {
431
449
  return rect.containsPoint(this.startPoint);
@@ -453,7 +471,7 @@ export class Path {
453
471
  }
454
472
  return false;
455
473
  }
456
- // Treats this as a closed path and returns true if part of `rect` is roughly within
474
+ // Treats this as a closed path and returns true if part of `rect` is *roughly* within
457
475
  // this path's interior.
458
476
  //
459
477
  // Note: Assumes that this is a closed, non-self-intersecting path.
@@ -535,8 +553,16 @@ export class Path {
535
553
  });
536
554
  return new Path(startPoint, commands);
537
555
  }
538
- toString(useNonAbsCommands) {
539
- if (this.cachedStringVersion) {
556
+ /**
557
+ * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
558
+ *
559
+ * If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
560
+ * absolute commands (e.g. `L10,0`).
561
+ *
562
+ * See also {@link fromString}.
563
+ */
564
+ toString(useNonAbsCommands, ignoreCache = false) {
565
+ if (this.cachedStringVersion && !ignoreCache) {
540
566
  return this.cachedStringVersion;
541
567
  }
542
568
  if (useNonAbsCommands === undefined) {
@@ -626,12 +652,20 @@ export class Path {
626
652
  return result.join('');
627
653
  }
628
654
  /**
629
- * Create a Path from a SVG path specification.
655
+ * Create a `Path` from a subset of the SVG path specification.
630
656
  *
631
657
  * ## To-do
632
658
  * - TODO: Support a larger subset of SVG paths
633
659
  * - Elliptical arcs are currently unsupported.
634
660
  * - TODO: Support `s`,`t` commands shorthands.
661
+ *
662
+ * @example
663
+ * ```ts,runnable,console
664
+ * import { Path } from '@js-draw/math';
665
+ *
666
+ * const path = Path.fromString('m0,0l100,100');
667
+ * console.log(path.toString(true)); // true: Prefer relative to absolute path commands
668
+ * ```
635
669
  */
636
670
  static fromString(pathString) {
637
671
  // See the MDN reference:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-draw/math",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "A math library for js-draw. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -45,5 +45,5 @@
45
45
  "svg",
46
46
  "math"
47
47
  ],
48
- "gitHead": "e824c37e9f216852cf096976e3a74fb4f177ead3"
48
+ "gitHead": "ccf1d0634e902c731fcd794df11cd001c3a30585"
49
49
  }
package/src/lib.ts CHANGED
@@ -30,6 +30,7 @@ export {
30
30
  } from './shapes/Path';
31
31
  export { Rect2 } from './shapes/Rect2';
32
32
  export { QuadraticBezier } from './shapes/QuadraticBezier';
33
+ export { Abstract2DShape } from './shapes/Abstract2DShape';
33
34
 
34
35
  export { Mat33, Mat33Array } from './Mat33';
35
36
  export { Point2, Vec2 } from './Vec2';
@@ -2,7 +2,11 @@ import LineSegment2 from './LineSegment2';
2
2
  import { Point2 } from '../Vec2';
3
3
  import Rect2 from './Rect2';
4
4
 
5
- abstract class Abstract2DShape {
5
+ /**
6
+ * An abstract base class for 2D shapes.
7
+ */
8
+ export abstract class Abstract2DShape {
9
+ // @internal
6
10
  protected static readonly smallValue = 1e-12;
7
11
 
8
12
  /**
@@ -47,9 +47,9 @@ describe('Path.toString', () => {
47
47
 
48
48
  it('deserialized path should serialize to the same/similar path, but with rounded components', () => {
49
49
  const path1 = Path.fromString('M100,100 L101,101 Q102,102 90.000000001,89.99999999 Z');
50
- path1['cachedStringVersion'] = null; // Clear the cache.
50
+ const ignoreCache = true;
51
51
 
52
- expect(path1.toString()).toBe([
52
+ expect(path1.toString(undefined, ignoreCache)).toBe([
53
53
  'M100,100', 'l1,1', 'q1,1 -11-11', 'l10,10'
54
54
  ].join(''));
55
55
  });
@@ -58,9 +58,9 @@ describe('Path.toString', () => {
58
58
  const pathStr = 'M184.2,52.3l-.2-.2q-2.7,2.4 -3.2,3.5q-2.8,7 -.9,6.1q4.3-2.6 4.8-6.1q1.2-8.8 .4-8.3q-4.2,5.2 -3.9,3.9q.2-1.6 .3-2.1q.2-1.3 -.2-1q-3.8,6.5 -3.2,3.3q.6-4.1 1.1-5.3q4.1-10 3.3-8.3q-5.3,13.1 -6.6,14.1q-3.3,2.8 -1.8-1.5q2.8-9.7 2.7-8.4q0,.3 0,.4q-1.4,7.1 -2.7,8.5q-2.6,3.2 -2.5,2.9q-.3-1.9 -.7-1.9q-4.1,4.4 -2.9,1.9q1.1-3 .3-2.6q-1.8,2 -2.5,2.4q-4.5,2.8 -4.2,1.9q.3-1.6 .2-1.4q1.5,2.2 1.3,2.9q-.8,3.9 -.5,3.3q.8-7.6 2.5-13.3q2.6-9.2 2.9-6.9q.3,1.4 .3,1.2q-.7-.4 -.9,0q-2.2,11.6 -7.6,13.6q-3.9,1.6 -2.1-1.3q3-5.5 2.6-3.4q-.2,1.8 -.5,1.8q-3.2,.5 -4.1,1.2q-2.6,2.6 -1.9,2.5q4.7-4.4 3.7-5.5q-1.1-.9 -1.6-.6q-7.2,7.5 -3.9,6.5q.3-.1 .4-.4q.6-5.3 -.2-4.9q-2.8,2.3 -3.1,2.4q-3.7,1.5 -3.5,.5q.3-3.6 1.4-3.3q3.5,.7 1.9,2.4q-1.7,2.3 -1.6,.8q0-3.5 -.9-3.1q-5.1,3.3 -4.9,2.8q.1-4 -.8-3.5q-4.3,3.4 -4.6,2.5q-1-2.1 .5-8.7l-.2,0q-1.6,6.6 -.7,8.9q.7,1.2 5.2-2.3q.4-.5 .2,3.1q.1,1 5.5-2.4q.4-.4 .3,2.7q.1,2 2.4-.4q1.7-2.3 -2.1-3.2q-1.7-.3 -2,3.7q0,1.4 4.1-.1q.3-.1 3.1-2.4q.3-.5 -.4,4.5q0-.1 -.2,0q-2.6,1.2 4.5-5.7q0-.2 .8,.6q.9,.6 -3.7,4.7q-.5,1 2.7-1.7q.6-.7 3.7-1.2q.7-.2 .9-2.2q.1-2.7 -3.4,3.2q-1.8,3.4 2.7,1.9q5.6-2.1 7.8-14q-.1,.1 .3,.4q.6,.1 .3-1.6q-.7-2.8 -3.7,6.7q-1.8,5.8 -2.5,13.5q.1,1.1 1.3-3.1q.2-1 -1.3-3.3q-.5-.5 -1,1.6q-.1,1.3 4.8-1.5q1-1 3-2q.1-.4 -1.1,2q-1.1,3.1 3.7-1.3q-.4,0 -.1,1.5q.3,.8 3.3-2.5q1.3-1.6 2.7-8.9q0-.1 0-.4q-.3-1.9 -3.5,8.2q-1.3,4.9 2.4,2.1q1.4-1.2 6.6-14.3q.8-2.4 -3.9,7.9q-.6,1.3 -1.1,5.5q-.3,3.7 4-3.1q-.2,0 -.6,.6q-.2,.6 -.3,2.3q0,1.8 4.7-3.5q.1-.5 -1.2,7.9q-.5,3.2 -4.6,5.7q-1.3,1 1.5-5.5q.4-1.1 3.01-3.5';
59
59
 
60
60
  const path1 = Path.fromString(pathStr);
61
- path1['cachedStringVersion'] = null; // Clear the cache.
62
- const path = Path.fromString(path1.toString(true));
63
- path1['cachedStringVersion'] = null; // Clear the cache.
61
+ const ignoreCache = true;
62
+ const path = Path.fromString(path1.toString(true, ignoreCache));
63
+ path1['cachedStringVersion'] = null; // Clear the cache manually
64
64
 
65
65
  expect(path.toString(true)).toBe(path1.toString(true));
66
66
  });
@@ -51,9 +51,6 @@ interface IntersectionResult {
51
51
  point: Point2;
52
52
  }
53
53
 
54
- type GeometryType = Abstract2DShape;
55
- type GeometryArrayType = Array<GeometryType>;
56
-
57
54
  /**
58
55
  * Represents a union of lines and curves.
59
56
  */
@@ -65,13 +62,27 @@ export class Path {
65
62
  */
66
63
  public readonly bbox: Rect2;
67
64
 
68
- public constructor(public readonly startPoint: Point2, public readonly parts: PathCommand[]) {
65
+ /** The individual shapes that make up this path. */
66
+ public readonly parts: Readonly<PathCommand>[];
67
+
68
+ /**
69
+ * Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
70
+ * `parts`.
71
+ *
72
+ * See also {@link fromString}
73
+ */
74
+ public constructor(
75
+ public readonly startPoint: Point2,
76
+ parts: Readonly<PathCommand>[],
77
+ ) {
78
+ this.parts = parts;
79
+
69
80
  // Initial bounding box contains one point: the start point.
70
81
  this.bbox = Rect2.bboxOf([startPoint]);
71
82
 
72
83
  // Convert into a representation of the geometry (cache for faster intersection
73
84
  // calculation)
74
- for (const part of parts) {
85
+ for (const part of this.parts) {
75
86
  this.bbox = this.bbox.union(Path.computeBBoxForSegment(startPoint, part));
76
87
  }
77
88
  }
@@ -85,16 +96,16 @@ export class Path {
85
96
  return Rect2.union(...bboxes);
86
97
  }
87
98
 
88
- private cachedGeometry: GeometryArrayType|null = null;
99
+ private cachedGeometry: Abstract2DShape[]|null = null;
89
100
 
90
101
  // Lazy-loads and returns this path's geometry
91
- public get geometry(): GeometryArrayType {
102
+ public get geometry(): Abstract2DShape[] {
92
103
  if (this.cachedGeometry) {
93
104
  return this.cachedGeometry;
94
105
  }
95
106
 
96
107
  let startPoint = this.startPoint;
97
- const geometry: GeometryArrayType = [];
108
+ const geometry: Abstract2DShape[] = [];
98
109
 
99
110
  for (const part of this.parts) {
100
111
  let exhaustivenessCheck: never;
@@ -258,7 +269,7 @@ export class Path {
258
269
 
259
270
  type DistanceFunction = (point: Point2) => number;
260
271
  type DistanceFunctionRecord = {
261
- part: GeometryType,
272
+ part: Abstract2DShape,
262
273
  bbox: Rect2,
263
274
  distFn: DistanceFunction,
264
275
  };
@@ -297,7 +308,7 @@ export class Path {
297
308
 
298
309
  // Returns the minimum distance to a part in this stroke, where only parts that the given
299
310
  // line could intersect are considered.
300
- const sdf = (point: Point2): [GeometryType|null, number] => {
311
+ const sdf = (point: Point2): [Abstract2DShape|null, number] => {
301
312
  let minDist = Infinity;
302
313
  let minDistPart: Abstract2DShape|null = null;
303
314
 
@@ -466,6 +477,8 @@ export class Path {
466
477
  * intersections are approximated with the surface `strokeRadius` away from this.
467
478
  *
468
479
  * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
480
+ *
481
+ * **Note**: `strokeRadius` is half of a stroke's width.
469
482
  */
470
483
  public intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[] {
471
484
  let result: IntersectionResult[] = [];
@@ -576,6 +589,16 @@ export class Path {
576
589
  }
577
590
  }
578
591
 
592
+ /**
593
+ * Like {@link closedRoughlyIntersects} except takes stroke width into account.
594
+ *
595
+ * This is intended to be a very fast and rough approximation. Use {@link intersection}
596
+ * and {@link signedDistance} for more accurate (but much slower) intersection calculations.
597
+ *
598
+ * **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
599
+ *
600
+ * `strokeRadius` is half of `strokeWidth`.
601
+ */
579
602
  public roughlyIntersects(rect: Rect2, strokeWidth: number = 0) {
580
603
  if (this.parts.length === 0) {
581
604
  return rect.containsPoint(this.startPoint);
@@ -609,7 +632,7 @@ export class Path {
609
632
  return false;
610
633
  }
611
634
 
612
- // Treats this as a closed path and returns true if part of `rect` is roughly within
635
+ // Treats this as a closed path and returns true if part of `rect` is *roughly* within
613
636
  // this path's interior.
614
637
  //
615
638
  // Note: Assumes that this is a closed, non-self-intersecting path.
@@ -713,8 +736,16 @@ export class Path {
713
736
 
714
737
  private cachedStringVersion: string|null = null;
715
738
 
716
- public toString(useNonAbsCommands?: boolean): string {
717
- if (this.cachedStringVersion) {
739
+ /**
740
+ * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
741
+ *
742
+ * If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
743
+ * absolute commands (e.g. `L10,0`).
744
+ *
745
+ * See also {@link fromString}.
746
+ */
747
+ public toString(useNonAbsCommands?: boolean, ignoreCache: boolean = false): string {
748
+ if (this.cachedStringVersion && !ignoreCache) {
718
749
  return this.cachedStringVersion;
719
750
  }
720
751
 
@@ -817,12 +848,20 @@ export class Path {
817
848
  }
818
849
 
819
850
  /**
820
- * Create a Path from a SVG path specification.
851
+ * Create a `Path` from a subset of the SVG path specification.
821
852
  *
822
853
  * ## To-do
823
854
  * - TODO: Support a larger subset of SVG paths
824
855
  * - Elliptical arcs are currently unsupported.
825
856
  * - TODO: Support `s`,`t` commands shorthands.
857
+ *
858
+ * @example
859
+ * ```ts,runnable,console
860
+ * import { Path } from '@js-draw/math';
861
+ *
862
+ * const path = Path.fromString('m0,0l100,100');
863
+ * console.log(path.toString(true)); // true: Prefer relative to absolute path commands
864
+ * ```
826
865
  */
827
866
  public static fromString(pathString: string): Path {
828
867
  // See the MDN reference: