@js-draw/math 1.16.0 → 1.17.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.
Files changed (51) hide show
  1. package/dist/cjs/Vec3.d.ts +21 -0
  2. package/dist/cjs/Vec3.js +28 -0
  3. package/dist/cjs/lib.d.ts +1 -1
  4. package/dist/cjs/shapes/Abstract2DShape.d.ts +3 -0
  5. package/dist/cjs/shapes/BezierJSWrapper.d.ts +15 -5
  6. package/dist/cjs/shapes/BezierJSWrapper.js +135 -18
  7. package/dist/cjs/shapes/LineSegment2.d.ts +34 -5
  8. package/dist/cjs/shapes/LineSegment2.js +63 -10
  9. package/dist/cjs/shapes/Parameterized2DShape.d.ts +31 -0
  10. package/dist/cjs/shapes/Parameterized2DShape.js +15 -0
  11. package/dist/cjs/shapes/Path.d.ts +40 -6
  12. package/dist/cjs/shapes/Path.js +173 -15
  13. package/dist/cjs/shapes/PointShape2D.d.ts +14 -3
  14. package/dist/cjs/shapes/PointShape2D.js +28 -5
  15. package/dist/cjs/shapes/QuadraticBezier.d.ts +4 -0
  16. package/dist/cjs/shapes/QuadraticBezier.js +19 -4
  17. package/dist/cjs/shapes/Rect2.d.ts +3 -0
  18. package/dist/cjs/shapes/Rect2.js +4 -1
  19. package/dist/mjs/Vec3.d.ts +21 -0
  20. package/dist/mjs/Vec3.mjs +28 -0
  21. package/dist/mjs/lib.d.ts +1 -1
  22. package/dist/mjs/shapes/Abstract2DShape.d.ts +3 -0
  23. package/dist/mjs/shapes/BezierJSWrapper.d.ts +15 -5
  24. package/dist/mjs/shapes/BezierJSWrapper.mjs +133 -18
  25. package/dist/mjs/shapes/LineSegment2.d.ts +34 -5
  26. package/dist/mjs/shapes/LineSegment2.mjs +63 -10
  27. package/dist/mjs/shapes/Parameterized2DShape.d.ts +31 -0
  28. package/dist/mjs/shapes/Parameterized2DShape.mjs +8 -0
  29. package/dist/mjs/shapes/Path.d.ts +40 -6
  30. package/dist/mjs/shapes/Path.mjs +173 -15
  31. package/dist/mjs/shapes/PointShape2D.d.ts +14 -3
  32. package/dist/mjs/shapes/PointShape2D.mjs +28 -5
  33. package/dist/mjs/shapes/QuadraticBezier.d.ts +4 -0
  34. package/dist/mjs/shapes/QuadraticBezier.mjs +19 -4
  35. package/dist/mjs/shapes/Rect2.d.ts +3 -0
  36. package/dist/mjs/shapes/Rect2.mjs +4 -1
  37. package/package.json +5 -5
  38. package/src/Vec3.test.ts +26 -7
  39. package/src/Vec3.ts +30 -0
  40. package/src/lib.ts +2 -0
  41. package/src/shapes/Abstract2DShape.ts +3 -0
  42. package/src/shapes/BezierJSWrapper.ts +154 -14
  43. package/src/shapes/LineSegment2.test.ts +35 -1
  44. package/src/shapes/LineSegment2.ts +79 -11
  45. package/src/shapes/Parameterized2DShape.ts +39 -0
  46. package/src/shapes/Path.test.ts +63 -3
  47. package/src/shapes/Path.ts +209 -25
  48. package/src/shapes/PointShape2D.ts +33 -6
  49. package/src/shapes/QuadraticBezier.test.ts +48 -12
  50. package/src/shapes/QuadraticBezier.ts +23 -5
  51. package/src/shapes/Rect2.ts +4 -1
@@ -2,7 +2,7 @@ import LineSegment2 from './LineSegment2';
2
2
  import Mat33 from '../Mat33';
3
3
  import Rect2 from './Rect2';
4
4
  import { Point2 } from '../Vec2';
5
- import Abstract2DShape from './Abstract2DShape';
5
+ import Parameterized2DShape from './Parameterized2DShape';
6
6
  export declare enum PathCommandType {
7
7
  LineTo = 0,
8
8
  MoveTo = 1,
@@ -29,12 +29,23 @@ export interface MoveToPathCommand {
29
29
  point: Point2;
30
30
  }
31
31
  export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
32
- interface IntersectionResult {
33
- curve: Abstract2DShape;
34
- /** @internal @deprecated */
32
+ export interface IntersectionResult {
33
+ curve: Parameterized2DShape;
34
+ curveIndex: number;
35
+ /** Parameter value for the closest point **on** the path to the intersection. @internal @deprecated */
35
36
  parameterValue?: number;
37
+ /** Point at which the intersection occured. */
36
38
  point: Point2;
37
39
  }
40
+ /**
41
+ * Allows indexing a particular part of a path.
42
+ *
43
+ * @see {@link Path.at} {@link Path.tangentAt}
44
+ */
45
+ export interface CurveIndexRecord {
46
+ curveIndex: number;
47
+ parameterValue: number;
48
+ }
38
49
  /**
39
50
  * Represents a union of lines and curves.
40
51
  */
@@ -57,7 +68,7 @@ export declare class Path {
57
68
  constructor(startPoint: Point2, parts: Readonly<PathCommand>[]);
58
69
  getExactBBox(): Rect2;
59
70
  private cachedGeometry;
60
- get geometry(): Abstract2DShape[];
71
+ get geometry(): Parameterized2DShape[];
61
72
  /**
62
73
  * Iterates through the start/end points of each component in this path.
63
74
  *
@@ -86,10 +97,31 @@ export declare class Path {
86
97
  * **Note**: `strokeRadius` is half of a stroke's width.
87
98
  */
88
99
  intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
100
+ /**
101
+ * @returns the nearest point on this path to the given `point`.
102
+ *
103
+ * @internal
104
+ * @beta
105
+ */
106
+ nearestPointTo(point: Point2): IntersectionResult;
107
+ at(index: CurveIndexRecord): import("../Vec3").Vec3;
108
+ tangentAt(index: CurveIndexRecord): import("../Vec3").Vec3;
89
109
  private static mapPathCommand;
90
110
  mapPoints(mapping: (point: Point2) => Point2): Path;
91
111
  transformedBy(affineTransfm: Mat33): Path;
92
- union(other: Path | null): Path;
112
+ union(other: Path | null, options?: {
113
+ allowReverse?: boolean;
114
+ }): Path;
115
+ /**
116
+ * @returns a version of this path with the direction reversed.
117
+ *
118
+ * Example:
119
+ * ```ts,runnable,console
120
+ * import {Path} from '@js-draw/math';
121
+ * console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
122
+ * ```
123
+ */
124
+ reversed(): Path;
93
125
  private getEndPoint;
94
126
  /**
95
127
  * Like {@link closedRoughlyIntersects} except takes stroke width into account.
@@ -103,6 +135,8 @@ export declare class Path {
103
135
  */
104
136
  roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
105
137
  closedRoughlyIntersects(rect: Rect2): boolean;
138
+ /** @returns true if all points on this are equivalent to the points on `other` */
139
+ eq(other: Path, tolerance?: number): boolean;
106
140
  /**
107
141
  * Returns a path that outlines `rect`.
108
142
  *
@@ -236,7 +236,7 @@ class Path {
236
236
  for (const { part, distFn, bbox } of uncheckedDistFunctions) {
237
237
  // Skip if impossible for the distance to the target to be lesser than
238
238
  // the current minimum.
239
- if (!bbox.grownBy(minDist).containsPoint(point)) {
239
+ if (isFinite(minDist) && !bbox.grownBy(minDist).containsPoint(point)) {
240
240
  continue;
241
241
  }
242
242
  const currentDist = distFn(point);
@@ -274,7 +274,7 @@ class Path {
274
274
  });
275
275
  const result = [];
276
276
  const stoppingThreshold = strokeRadius / 1000;
277
- // Returns the maximum x value explored
277
+ // Returns the maximum parameter value explored
278
278
  const raymarchFrom = (startPoint,
279
279
  // Direction to march in (multiplies line.direction)
280
280
  directionMultiplier,
@@ -318,9 +318,14 @@ class Path {
318
318
  if (lastPart && isOnLineSegment && Math.abs(lastDist) < stoppingThreshold) {
319
319
  result.push({
320
320
  point: currentPoint,
321
- parameterValue: NaN,
321
+ parameterValue: NaN, // lastPart.nearestPointTo(currentPoint).parameterValue,
322
322
  curve: lastPart,
323
+ curveIndex: this.geometry.indexOf(lastPart),
323
324
  });
325
+ // Slightly increase the parameter value to prevent the same point from being
326
+ // added to the results twice.
327
+ const parameterIncrease = strokeRadius / 20 / line.length;
328
+ lastParameter += isFinite(parameterIncrease) ? parameterIncrease : 0;
324
329
  }
325
330
  return lastParameter;
326
331
  };
@@ -353,14 +358,18 @@ class Path {
353
358
  if (!line.bbox.intersects(this.bbox.grownBy(strokeRadius ?? 0))) {
354
359
  return [];
355
360
  }
361
+ let index = 0;
356
362
  for (const part of this.geometry) {
357
- const intersection = part.intersectsLineSegment(line);
358
- if (intersection.length > 0) {
363
+ const intersections = part.argIntersectsLineSegment(line);
364
+ for (const intersection of intersections) {
359
365
  result.push({
360
366
  curve: part,
361
- point: intersection[0],
367
+ curveIndex: index,
368
+ point: part.at(intersection),
369
+ parameterValue: intersection,
362
370
  });
363
371
  }
372
+ index++;
364
373
  }
365
374
  // If given a non-zero strokeWidth, attempt to raymarch.
366
375
  // Even if raymarching, we need to collect starting points.
@@ -373,6 +382,42 @@ class Path {
373
382
  }
374
383
  return result;
375
384
  }
385
+ /**
386
+ * @returns the nearest point on this path to the given `point`.
387
+ *
388
+ * @internal
389
+ * @beta
390
+ */
391
+ nearestPointTo(point) {
392
+ // Find the closest point on this
393
+ let closestSquareDist = Infinity;
394
+ let closestPartIndex = 0;
395
+ let closestParameterValue = 0;
396
+ let closestPoint = this.startPoint;
397
+ for (let i = 0; i < this.geometry.length; i++) {
398
+ const current = this.geometry[i];
399
+ const nearestPoint = current.nearestPointTo(point);
400
+ const sqareDist = nearestPoint.point.squareDistanceTo(point);
401
+ if (i === 0 || sqareDist < closestSquareDist) {
402
+ closestPartIndex = i;
403
+ closestSquareDist = sqareDist;
404
+ closestParameterValue = nearestPoint.parameterValue;
405
+ closestPoint = nearestPoint.point;
406
+ }
407
+ }
408
+ return {
409
+ curve: this.geometry[closestPartIndex],
410
+ curveIndex: closestPartIndex,
411
+ parameterValue: closestParameterValue,
412
+ point: closestPoint,
413
+ };
414
+ }
415
+ at(index) {
416
+ return this.geometry[index.curveIndex].at(index.parameterValue);
417
+ }
418
+ tangentAt(index) {
419
+ return this.geometry[index.curveIndex].tangentAt(index.parameterValue);
420
+ }
376
421
  static mapPathCommand(part, mapping) {
377
422
  switch (part.kind) {
378
423
  case PathCommandType.MoveTo:
@@ -416,18 +461,85 @@ class Path {
416
461
  return this.mapPoints(point => affineTransfm.transformVec2(point));
417
462
  }
418
463
  // Creates a new path by joining [other] to the end of this path
419
- union(other) {
464
+ union(other,
465
+ // allowReverse: true iff reversing other or this is permitted if it means
466
+ // no moveTo command is necessary when unioning the paths.
467
+ options = { allowReverse: true }) {
420
468
  if (!other) {
421
469
  return this;
422
470
  }
423
- return new Path(this.startPoint, [
424
- ...this.parts,
425
- {
426
- kind: PathCommandType.MoveTo,
427
- point: other.startPoint,
428
- },
429
- ...other.parts,
430
- ]);
471
+ const thisEnd = this.getEndPoint();
472
+ let newParts = [];
473
+ if (thisEnd.eq(other.startPoint)) {
474
+ newParts = this.parts.concat(other.parts);
475
+ }
476
+ else if (options.allowReverse && this.startPoint.eq(other.getEndPoint())) {
477
+ return other.union(this, { allowReverse: false });
478
+ }
479
+ else if (options.allowReverse && this.startPoint.eq(other.startPoint)) {
480
+ return this.union(other.reversed(), { allowReverse: false });
481
+ }
482
+ else {
483
+ newParts = [
484
+ ...this.parts,
485
+ {
486
+ kind: PathCommandType.MoveTo,
487
+ point: other.startPoint,
488
+ },
489
+ ...other.parts,
490
+ ];
491
+ }
492
+ return new Path(this.startPoint, newParts);
493
+ }
494
+ /**
495
+ * @returns a version of this path with the direction reversed.
496
+ *
497
+ * Example:
498
+ * ```ts,runnable,console
499
+ * import {Path} from '@js-draw/math';
500
+ * console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
501
+ * ```
502
+ */
503
+ reversed() {
504
+ const newStart = this.getEndPoint();
505
+ const newParts = [];
506
+ let lastPoint = this.startPoint;
507
+ for (const part of this.parts) {
508
+ switch (part.kind) {
509
+ case PathCommandType.LineTo:
510
+ case PathCommandType.MoveTo:
511
+ newParts.push({
512
+ kind: part.kind,
513
+ point: lastPoint,
514
+ });
515
+ lastPoint = part.point;
516
+ break;
517
+ case PathCommandType.CubicBezierTo:
518
+ newParts.push({
519
+ kind: part.kind,
520
+ controlPoint1: part.controlPoint2,
521
+ controlPoint2: part.controlPoint1,
522
+ endPoint: lastPoint,
523
+ });
524
+ lastPoint = part.endPoint;
525
+ break;
526
+ case PathCommandType.QuadraticBezierTo:
527
+ newParts.push({
528
+ kind: part.kind,
529
+ controlPoint: part.controlPoint,
530
+ endPoint: lastPoint,
531
+ });
532
+ lastPoint = part.endPoint;
533
+ break;
534
+ default:
535
+ {
536
+ const exhaustivenessCheck = part;
537
+ return exhaustivenessCheck;
538
+ }
539
+ }
540
+ }
541
+ newParts.reverse();
542
+ return new Path(newStart, newParts);
431
543
  }
432
544
  getEndPoint() {
433
545
  if (this.parts.length === 0) {
@@ -519,6 +631,52 @@ class Path {
519
631
  // Even? Probably no intersection.
520
632
  return false;
521
633
  }
634
+ /** @returns true if all points on this are equivalent to the points on `other` */
635
+ eq(other, tolerance) {
636
+ if (other.parts.length !== this.parts.length) {
637
+ return false;
638
+ }
639
+ for (let i = 0; i < this.parts.length; i++) {
640
+ const part1 = this.parts[i];
641
+ const part2 = other.parts[i];
642
+ switch (part1.kind) {
643
+ case PathCommandType.LineTo:
644
+ case PathCommandType.MoveTo:
645
+ if (part1.kind !== part2.kind) {
646
+ return false;
647
+ }
648
+ else if (!part1.point.eq(part2.point, tolerance)) {
649
+ return false;
650
+ }
651
+ break;
652
+ case PathCommandType.CubicBezierTo:
653
+ if (part1.kind !== part2.kind) {
654
+ return false;
655
+ }
656
+ else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance)
657
+ || !part1.controlPoint2.eq(part2.controlPoint2, tolerance)
658
+ || !part1.endPoint.eq(part2.endPoint, tolerance)) {
659
+ return false;
660
+ }
661
+ break;
662
+ case PathCommandType.QuadraticBezierTo:
663
+ if (part1.kind !== part2.kind) {
664
+ return false;
665
+ }
666
+ else if (!part1.controlPoint.eq(part2.controlPoint, tolerance)
667
+ || !part1.endPoint.eq(part2.endPoint, tolerance)) {
668
+ return false;
669
+ }
670
+ break;
671
+ default:
672
+ {
673
+ const exhaustivenessCheck = part1;
674
+ return exhaustivenessCheck;
675
+ }
676
+ }
677
+ }
678
+ return true;
679
+ }
522
680
  /**
523
681
  * Returns a path that outlines `rect`.
524
682
  *
@@ -1,18 +1,29 @@
1
1
  import { Point2 } from '../Vec2';
2
2
  import Vec3 from '../Vec3';
3
- import Abstract2DShape from './Abstract2DShape';
4
3
  import LineSegment2 from './LineSegment2';
4
+ import Parameterized2DShape from './Parameterized2DShape';
5
5
  import Rect2 from './Rect2';
6
6
  /**
7
7
  * Like a {@link Point2}, but with additional functionality (e.g. SDF).
8
8
  *
9
9
  * Access the internal `Point2` using the `p` property.
10
10
  */
11
- declare class PointShape2D extends Abstract2DShape {
11
+ declare class PointShape2D extends Parameterized2DShape {
12
12
  readonly p: Point2;
13
13
  constructor(p: Point2);
14
14
  signedDistance(point: Vec3): number;
15
- intersectsLineSegment(lineSegment: LineSegment2, epsilon?: number): Vec3[];
15
+ argIntersectsLineSegment(lineSegment: LineSegment2, epsilon?: number): number[];
16
16
  getTightBoundingBox(): Rect2;
17
+ at(_t: number): Vec3;
18
+ /**
19
+ * Returns an arbitrary unit-length vector.
20
+ */
21
+ normalAt(_t: number): Vec3;
22
+ tangentAt(_t: number): Vec3;
23
+ splitAt(_t: number): [PointShape2D];
24
+ nearestPointTo(_point: Point2): {
25
+ point: Vec3;
26
+ parameterValue: number;
27
+ };
17
28
  }
18
29
  export default PointShape2D;
@@ -3,29 +3,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
6
+ const Vec2_1 = require("../Vec2");
7
+ const Parameterized2DShape_1 = __importDefault(require("./Parameterized2DShape"));
7
8
  const Rect2_1 = __importDefault(require("./Rect2"));
8
9
  /**
9
10
  * Like a {@link Point2}, but with additional functionality (e.g. SDF).
10
11
  *
11
12
  * Access the internal `Point2` using the `p` property.
12
13
  */
13
- class PointShape2D extends Abstract2DShape_1.default {
14
+ class PointShape2D extends Parameterized2DShape_1.default {
14
15
  constructor(p) {
15
16
  super();
16
17
  this.p = p;
17
18
  }
18
19
  signedDistance(point) {
19
- return this.p.minus(point).magnitude();
20
+ return this.p.distanceTo(point);
20
21
  }
21
- intersectsLineSegment(lineSegment, epsilon) {
22
+ argIntersectsLineSegment(lineSegment, epsilon) {
22
23
  if (lineSegment.containsPoint(this.p, epsilon)) {
23
- return [this.p];
24
+ return [0];
24
25
  }
25
26
  return [];
26
27
  }
27
28
  getTightBoundingBox() {
28
29
  return new Rect2_1.default(this.p.x, this.p.y, 0, 0);
29
30
  }
31
+ at(_t) {
32
+ return this.p;
33
+ }
34
+ /**
35
+ * Returns an arbitrary unit-length vector.
36
+ */
37
+ normalAt(_t) {
38
+ // Return a vector that makes sense.
39
+ return Vec2_1.Vec2.unitY;
40
+ }
41
+ tangentAt(_t) {
42
+ return Vec2_1.Vec2.unitX;
43
+ }
44
+ splitAt(_t) {
45
+ return [this];
46
+ }
47
+ nearestPointTo(_point) {
48
+ return {
49
+ point: this.p,
50
+ parameterValue: 0,
51
+ };
52
+ }
30
53
  }
31
54
  exports.default = PointShape2D;
@@ -18,11 +18,15 @@ export declare class QuadraticBezier extends BezierJSWrapper {
18
18
  */
19
19
  private static componentAt;
20
20
  private static derivativeComponentAt;
21
+ private static secondDerivativeComponentAt;
21
22
  /**
22
23
  * @returns the curve evaluated at `t`.
24
+ *
25
+ * `t` should be a number in `[0, 1]`.
23
26
  */
24
27
  at(t: number): Point2;
25
28
  derivativeAt(t: number): Point2;
29
+ secondDerivativeAt(t: number): Point2;
26
30
  normal(t: number): Vec2;
27
31
  /** @returns an overestimate of this shape's bounding box. */
28
32
  getLooseBoundingBox(): Rect2;
@@ -31,10 +31,19 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
31
31
  static derivativeComponentAt(t, p0, p1, p2) {
32
32
  return -2 * p0 + 2 * p1 + 2 * t * (p0 - 2 * p1 + p2);
33
33
  }
34
+ static secondDerivativeComponentAt(t, p0, p1, p2) {
35
+ return 2 * (p0 - 2 * p1 + p2);
36
+ }
34
37
  /**
35
38
  * @returns the curve evaluated at `t`.
39
+ *
40
+ * `t` should be a number in `[0, 1]`.
36
41
  */
37
42
  at(t) {
43
+ if (t === 0)
44
+ return this.p0;
45
+ if (t === 1)
46
+ return this.p2;
38
47
  const p0 = this.p0;
39
48
  const p1 = this.p1;
40
49
  const p2 = this.p2;
@@ -46,6 +55,12 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
46
55
  const p2 = this.p2;
47
56
  return Vec2_1.Vec2.of(QuadraticBezier.derivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.derivativeComponentAt(t, p0.y, p1.y, p2.y));
48
57
  }
58
+ secondDerivativeAt(t) {
59
+ const p0 = this.p0;
60
+ const p1 = this.p1;
61
+ const p2 = this.p2;
62
+ return Vec2_1.Vec2.of(QuadraticBezier.secondDerivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.secondDerivativeComponentAt(t, p0.y, p1.y, p2.y));
63
+ }
49
64
  normal(t) {
50
65
  const tangent = this.derivativeAt(t);
51
66
  return tangent.orthog().normalized();
@@ -106,10 +121,10 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
106
121
  }
107
122
  const at1 = this.at(min1);
108
123
  const at2 = this.at(min2);
109
- const sqrDist1 = at1.minus(point).magnitudeSquared();
110
- const sqrDist2 = at2.minus(point).magnitudeSquared();
111
- const sqrDist3 = this.at(0).minus(point).magnitudeSquared();
112
- const sqrDist4 = this.at(1).minus(point).magnitudeSquared();
124
+ const sqrDist1 = at1.squareDistanceTo(point);
125
+ const sqrDist2 = at2.squareDistanceTo(point);
126
+ const sqrDist3 = this.at(0).squareDistanceTo(point);
127
+ const sqrDist4 = this.at(1).squareDistanceTo(point);
113
128
  return Math.sqrt(Math.min(sqrDist1, sqrDist2, sqrDist3, sqrDist4));
114
129
  }
115
130
  getPoints() {
@@ -25,6 +25,9 @@ export declare class Rect2 extends Abstract2DShape {
25
25
  resizedTo(size: Vec2): Rect2;
26
26
  containsPoint(other: Point2): boolean;
27
27
  containsRect(other: Rect2): boolean;
28
+ /**
29
+ * @returns true iff this and `other` overlap
30
+ */
28
31
  intersects(other: Rect2): boolean;
29
32
  intersection(other: Rect2): Rect2 | null;
30
33
  union(other: Rect2): Rect2;
@@ -44,6 +44,9 @@ class Rect2 extends Abstract2DShape_1.default {
44
44
  && this.x + this.w >= other.x + other.w
45
45
  && this.y + this.h >= other.y + other.h;
46
46
  }
47
+ /**
48
+ * @returns true iff this and `other` overlap
49
+ */
47
50
  intersects(other) {
48
51
  // Project along x/y axes.
49
52
  const thisMinX = this.x;
@@ -130,7 +133,7 @@ class Rect2 extends Abstract2DShape_1.default {
130
133
  let closest = null;
131
134
  let closestDist = null;
132
135
  for (const point of closestEdgePoints) {
133
- const dist = point.minus(target).length();
136
+ const dist = point.distanceTo(target);
134
137
  if (closestDist === null || dist < closestDist) {
135
138
  closest = point;
136
139
  closestDist = dist;
@@ -35,11 +35,31 @@ export declare class Vec3 {
35
35
  length(): number;
36
36
  magnitude(): number;
37
37
  magnitudeSquared(): number;
38
+ /**
39
+ * Interpreting this vector as a point in ℝ^3, computes the square distance
40
+ * to another point, `p`.
41
+ *
42
+ * Equivalent to `.minus(p).magnitudeSquared()`.
43
+ */
44
+ squareDistanceTo(p: Vec3): number;
45
+ /**
46
+ * Interpreting this vector as a point in ℝ³, returns the distance to the point
47
+ * `p`.
48
+ *
49
+ * Equivalent to `.minus(p).magnitude()`.
50
+ */
51
+ distanceTo(p: Vec3): number;
38
52
  /**
39
53
  * Returns the entry of this with the greatest magnitude.
40
54
  *
41
55
  * In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
42
56
  * all entries of this vector.
57
+ *
58
+ * **Example**:
59
+ * ```ts,runnable,console
60
+ * import { Vec3 } from '@js-draw/math';
61
+ * console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
62
+ * ```
43
63
  */
44
64
  maximumEntryMagnitude(): number;
45
65
  /**
@@ -50,6 +70,7 @@ export declare class Vec3 {
50
70
  * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
51
71
  * the resultant angle is in the range $[-\pi, pi]$.
52
72
  *
73
+ * **Example**:
53
74
  * ```ts,runnable,console
54
75
  * import { Vec2 } from '@js-draw/math';
55
76
  * console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
package/dist/mjs/Vec3.mjs CHANGED
@@ -55,11 +55,38 @@ export class Vec3 {
55
55
  magnitudeSquared() {
56
56
  return this.dot(this);
57
57
  }
58
+ /**
59
+ * Interpreting this vector as a point in ℝ^3, computes the square distance
60
+ * to another point, `p`.
61
+ *
62
+ * Equivalent to `.minus(p).magnitudeSquared()`.
63
+ */
64
+ squareDistanceTo(p) {
65
+ const dx = this.x - p.x;
66
+ const dy = this.y - p.y;
67
+ const dz = this.z - p.z;
68
+ return dx * dx + dy * dy + dz * dz;
69
+ }
70
+ /**
71
+ * Interpreting this vector as a point in ℝ³, returns the distance to the point
72
+ * `p`.
73
+ *
74
+ * Equivalent to `.minus(p).magnitude()`.
75
+ */
76
+ distanceTo(p) {
77
+ return Math.sqrt(this.squareDistanceTo(p));
78
+ }
58
79
  /**
59
80
  * Returns the entry of this with the greatest magnitude.
60
81
  *
61
82
  * In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
62
83
  * all entries of this vector.
84
+ *
85
+ * **Example**:
86
+ * ```ts,runnable,console
87
+ * import { Vec3 } from '@js-draw/math';
88
+ * console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
89
+ * ```
63
90
  */
64
91
  maximumEntryMagnitude() {
65
92
  return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z)));
@@ -72,6 +99,7 @@ export class Vec3 {
72
99
  * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
73
100
  * the resultant angle is in the range $[-\pi, pi]$.
74
101
  *
102
+ * **Example**:
75
103
  * ```ts,runnable,console
76
104
  * import { Vec2 } from '@js-draw/math';
77
105
  * console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
package/dist/mjs/lib.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * @packageDocumentation
18
18
  */
19
19
  export { LineSegment2 } from './shapes/LineSegment2';
20
- export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
20
+ export { Path, IntersectionResult as PathIntersectionResult, CurveIndexRecord as PathCurveIndex, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
21
21
  export { Rect2 } from './shapes/Rect2';
22
22
  export { QuadraticBezier } from './shapes/QuadraticBezier';
23
23
  export { Abstract2DShape } from './shapes/Abstract2DShape';
@@ -38,6 +38,9 @@ export declare abstract class Abstract2DShape {
38
38
  containsPoint(point: Point2, epsilon?: number): boolean;
39
39
  /**
40
40
  * Returns a bounding box that precisely fits the content of this shape.
41
+ *
42
+ * **Note**: This bounding box should aligned with the x/y axes. (Thus, it may be
43
+ * possible to find a tighter bounding box not axes-aligned).
41
44
  */
42
45
  abstract getTightBoundingBox(): Rect2;
43
46
  /**
@@ -1,21 +1,22 @@
1
1
  import { Bezier } from 'bezier-js';
2
2
  import { Point2, Vec2 } from '../Vec2';
3
- import Abstract2DShape from './Abstract2DShape';
4
3
  import LineSegment2 from './LineSegment2';
5
4
  import Rect2 from './Rect2';
5
+ import Parameterized2DShape from './Parameterized2DShape';
6
6
  /**
7
7
  * A lazy-initializing wrapper around Bezier-js.
8
8
  *
9
9
  * Subclasses may override `at`, `derivativeAt`, and `normal` with functions
10
10
  * that do not initialize a `bezier-js` `Bezier`.
11
11
  *
12
- * Do not use this class directly. It may be removed/replaced in a future release.
12
+ * **Do not use this class directly.** It may be removed/replaced in a future release.
13
13
  * @internal
14
14
  */
15
- declare abstract class BezierJSWrapper extends Abstract2DShape {
15
+ export declare abstract class BezierJSWrapper extends Parameterized2DShape {
16
16
  #private;
17
+ protected constructor(bezierJsBezier?: Bezier);
17
18
  /** Returns the start, control points, and end point of this Bézier. */
18
- abstract getPoints(): Point2[];
19
+ abstract getPoints(): readonly Point2[];
19
20
  protected getBezier(): Bezier;
20
21
  signedDistance(point: Point2): number;
21
22
  /**
@@ -29,8 +30,17 @@ declare abstract class BezierJSWrapper extends Abstract2DShape {
29
30
  */
30
31
  at(t: number): Point2;
31
32
  derivativeAt(t: number): Point2;
33
+ secondDerivativeAt(t: number): Point2;
32
34
  normal(t: number): Vec2;
35
+ normalAt(t: number): Vec2;
36
+ tangentAt(t: number): Vec2;
33
37
  getTightBoundingBox(): Rect2;
34
- intersectsLineSegment(line: LineSegment2): Point2[];
38
+ argIntersectsLineSegment(line: LineSegment2): number[];
39
+ splitAt(t: number): [BezierJSWrapper] | [BezierJSWrapper, BezierJSWrapper];
40
+ nearestPointTo(point: Point2): {
41
+ parameterValue: number;
42
+ point: import("../Vec3").Vec3;
43
+ };
44
+ toString(): string;
35
45
  }
36
46
  export default BezierJSWrapper;