@js-draw/math 1.16.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
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;