@js-draw/math 1.21.3 → 1.23.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -11,6 +11,7 @@ import Parameterized2DShape from './Parameterized2DShape';
11
11
  import BezierJSWrapper from './BezierJSWrapper';
12
12
  import convexHull2Of from '../utils/convexHull2Of';
13
13
 
14
+ /** Identifiers for different path commands. These commands can make up a {@link Path}. */
14
15
  export enum PathCommandType {
15
16
  LineTo,
16
17
  MoveTo,
@@ -41,7 +42,11 @@ export interface MoveToPathCommand {
41
42
  point: Point2;
42
43
  }
43
44
 
44
- export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
45
+ export type PathCommand =
46
+ | CubicBezierPathCommand
47
+ | QuadraticBezierPathCommand
48
+ | MoveToPathCommand
49
+ | LinePathCommand;
45
50
 
46
51
  export interface IntersectionResult {
47
52
  // @internal
@@ -62,7 +67,7 @@ export interface PathSplitOptions {
62
67
  * Allows mapping points on newly added segments. This is useful, for example,
63
68
  * to round points to prevent long decimals when later saving.
64
69
  */
65
- mapNewPoint?: (point: Point2)=>Point2;
70
+ mapNewPoint?: (point: Point2) => Point2;
66
71
  }
67
72
 
68
73
  /**
@@ -182,7 +187,7 @@ export class Path {
182
187
  return Rect2.union(...bboxes);
183
188
  }
184
189
 
185
- private cachedGeometry: Parameterized2DShape[]|null = null;
190
+ private cachedGeometry: Parameterized2DShape[] | null = null;
186
191
 
187
192
  // Lazy-loads and returns this path's geometry
188
193
  public get geometry(): Parameterized2DShape[] {
@@ -197,35 +202,27 @@ export class Path {
197
202
  let exhaustivenessCheck: never;
198
203
 
199
204
  switch (part.kind) {
200
- case PathCommandType.CubicBezierTo:
201
- geometry.push(
202
- new CubicBezier(
203
- startPoint, part.controlPoint1, part.controlPoint2, part.endPoint
204
- )
205
- );
206
- startPoint = part.endPoint;
207
- break;
208
- case PathCommandType.QuadraticBezierTo:
209
- geometry.push(
210
- new QuadraticBezier(
211
- startPoint, part.controlPoint, part.endPoint
212
- )
213
- );
214
- startPoint = part.endPoint;
215
- break;
216
- case PathCommandType.LineTo:
217
- geometry.push(
218
- new LineSegment2(startPoint, part.point)
219
- );
220
- startPoint = part.point;
221
- break;
222
- case PathCommandType.MoveTo:
223
- geometry.push(new PointShape2D(part.point));
224
- startPoint = part.point;
225
- break;
226
- default:
227
- exhaustivenessCheck = part;
228
- return exhaustivenessCheck;
205
+ case PathCommandType.CubicBezierTo:
206
+ geometry.push(
207
+ new CubicBezier(startPoint, part.controlPoint1, part.controlPoint2, part.endPoint),
208
+ );
209
+ startPoint = part.endPoint;
210
+ break;
211
+ case PathCommandType.QuadraticBezierTo:
212
+ geometry.push(new QuadraticBezier(startPoint, part.controlPoint, part.endPoint));
213
+ startPoint = part.endPoint;
214
+ break;
215
+ case PathCommandType.LineTo:
216
+ geometry.push(new LineSegment2(startPoint, part.point));
217
+ startPoint = part.point;
218
+ break;
219
+ case PathCommandType.MoveTo:
220
+ geometry.push(new PointShape2D(part.point));
221
+ startPoint = part.point;
222
+ break;
223
+ default:
224
+ exhaustivenessCheck = part;
225
+ return exhaustivenessCheck;
229
226
  }
230
227
  }
231
228
 
@@ -246,26 +243,26 @@ export class Path {
246
243
  let exhaustivenessCheck: never;
247
244
 
248
245
  switch (part.kind) {
249
- case PathCommandType.CubicBezierTo:
250
- yield part.endPoint;
251
- break;
252
- case PathCommandType.QuadraticBezierTo:
253
- yield part.endPoint;
254
- break;
255
- case PathCommandType.LineTo:
256
- yield part.point;
257
- break;
258
- case PathCommandType.MoveTo:
259
- yield part.point;
260
- break;
261
- default:
262
- exhaustivenessCheck = part;
263
- return exhaustivenessCheck;
246
+ case PathCommandType.CubicBezierTo:
247
+ yield part.endPoint;
248
+ break;
249
+ case PathCommandType.QuadraticBezierTo:
250
+ yield part.endPoint;
251
+ break;
252
+ case PathCommandType.LineTo:
253
+ yield part.point;
254
+ break;
255
+ case PathCommandType.MoveTo:
256
+ yield part.point;
257
+ break;
258
+ default:
259
+ exhaustivenessCheck = part;
260
+ return exhaustivenessCheck;
264
261
  }
265
262
  }
266
263
  }
267
264
 
268
- private cachedPolylineApproximation: LineSegment2[]|null = null;
265
+ private cachedPolylineApproximation: LineSegment2[] | null = null;
269
266
 
270
267
  // Approximates this path with a group of line segments.
271
268
  public polylineApproximation(): LineSegment2[] {
@@ -277,16 +274,16 @@ export class Path {
277
274
 
278
275
  for (const part of this.parts) {
279
276
  switch (part.kind) {
280
- case PathCommandType.CubicBezierTo:
281
- points.push(part.controlPoint1, part.controlPoint2, part.endPoint);
282
- break;
283
- case PathCommandType.QuadraticBezierTo:
284
- points.push(part.controlPoint, part.endPoint);
285
- break;
286
- case PathCommandType.MoveTo:
287
- case PathCommandType.LineTo:
288
- points.push(part.point);
289
- break;
277
+ case PathCommandType.CubicBezierTo:
278
+ points.push(part.controlPoint1, part.controlPoint2, part.endPoint);
279
+ break;
280
+ case PathCommandType.QuadraticBezierTo:
281
+ points.push(part.controlPoint, part.endPoint);
282
+ break;
283
+ case PathCommandType.MoveTo:
284
+ case PathCommandType.LineTo:
285
+ points.push(part.point);
286
+ break;
290
287
  }
291
288
  }
292
289
 
@@ -304,19 +301,19 @@ export class Path {
304
301
  const points = [startPoint];
305
302
  let exhaustivenessCheck: never;
306
303
  switch (part.kind) {
307
- case PathCommandType.MoveTo:
308
- case PathCommandType.LineTo:
309
- points.push(part.point);
310
- break;
311
- case PathCommandType.CubicBezierTo:
312
- points.push(part.controlPoint1, part.controlPoint2, part.endPoint);
313
- break;
314
- case PathCommandType.QuadraticBezierTo:
315
- points.push(part.controlPoint, part.endPoint);
316
- break;
317
- default:
318
- exhaustivenessCheck = part;
319
- return exhaustivenessCheck;
304
+ case PathCommandType.MoveTo:
305
+ case PathCommandType.LineTo:
306
+ points.push(part.point);
307
+ break;
308
+ case PathCommandType.CubicBezierTo:
309
+ points.push(part.controlPoint1, part.controlPoint2, part.endPoint);
310
+ break;
311
+ case PathCommandType.QuadraticBezierTo:
312
+ points.push(part.controlPoint, part.endPoint);
313
+ break;
314
+ default:
315
+ exhaustivenessCheck = part;
316
+ return exhaustivenessCheck;
320
317
  }
321
318
 
322
319
  return Rect2.bboxOf(points);
@@ -356,7 +353,9 @@ export class Path {
356
353
  * both end points of `line` and each point in `additionalRaymarchStartPoints`.
357
354
  */
358
355
  private raymarchIntersectionWith(
359
- line: LineSegment2, strokeRadius: number, additionalRaymarchStartPoints: Point2[] = []
356
+ line: LineSegment2,
357
+ strokeRadius: number,
358
+ additionalRaymarchStartPoints: Point2[] = [],
360
359
  ): IntersectionResult[] {
361
360
  // No intersection between bounding boxes: No possible intersection
362
361
  // of the interior.
@@ -368,9 +367,9 @@ export class Path {
368
367
 
369
368
  type DistanceFunction = (point: Point2) => number;
370
369
  type DistanceFunctionRecord = {
371
- part: Parameterized2DShape,
372
- bbox: Rect2,
373
- distFn: DistanceFunction,
370
+ part: Parameterized2DShape;
371
+ bbox: Rect2;
372
+ distFn: DistanceFunction;
374
373
  };
375
374
  const partDistFunctionRecords: DistanceFunctionRecord[] = [];
376
375
 
@@ -407,9 +406,9 @@ export class Path {
407
406
 
408
407
  // Returns the minimum distance to a part in this stroke, where only parts that the given
409
408
  // line could intersect are considered.
410
- const sdf = (point: Point2): [Parameterized2DShape|null, number] => {
409
+ const sdf = (point: Point2): [Parameterized2DShape | null, number] => {
411
410
  let minDist = Infinity;
412
- let minDistPart: Parameterized2DShape|null = null;
411
+ let minDistPart: Parameterized2DShape | null = null;
413
412
 
414
413
  const uncheckedDistFunctions: DistanceFunctionRecord[] = [];
415
414
 
@@ -448,18 +447,15 @@ export class Path {
448
447
  }
449
448
  }
450
449
 
451
- return [ minDistPart, minDist - strokeRadius ];
450
+ return [minDistPart, minDist - strokeRadius];
452
451
  };
453
452
 
454
-
455
453
  // Raymarch:
456
454
  const maxRaymarchSteps = 8;
457
455
 
458
456
  // Start raymarching from each of these points. This allows detection of multiple
459
457
  // intersections.
460
- const startPoints = [
461
- line.p1, ...additionalRaymarchStartPoints, line.p2
462
- ];
458
+ const startPoints = [line.p1, ...additionalRaymarchStartPoints, line.p2];
463
459
 
464
460
  // Converts a point ON THE LINE to a parameter
465
461
  const pointToParameter = (point: Point2) => {
@@ -491,12 +487,12 @@ export class Path {
491
487
  startPoint: Point2,
492
488
 
493
489
  // Direction to march in (multiplies line.direction)
494
- directionMultiplier: -1|1,
490
+ directionMultiplier: -1 | 1,
495
491
 
496
492
  // Terminate if the current point corresponds to a parameter
497
493
  // below this.
498
494
  minimumLineParameter: number,
499
- ): number|null => {
495
+ ): number | null => {
500
496
  let currentPoint = startPoint;
501
497
  let [lastPart, lastDist] = sdf(currentPoint);
502
498
  let lastParameter = pointToParameter(currentPoint);
@@ -594,7 +590,9 @@ export class Path {
594
590
  }
595
591
 
596
592
  if (this.parts.length === 0) {
597
- return new Path(this.startPoint, [{ kind: PathCommandType.MoveTo, point: this.startPoint }]).intersection(line, strokeRadius);
593
+ return new Path(this.startPoint, [
594
+ { kind: PathCommandType.MoveTo, point: this.startPoint },
595
+ ]).intersection(line, strokeRadius);
598
596
  }
599
597
 
600
598
  let index = 0;
@@ -610,7 +608,7 @@ export class Path {
610
608
  });
611
609
  }
612
610
 
613
- index ++;
611
+ index++;
614
612
  }
615
613
 
616
614
  // If given a non-zero strokeWidth, attempt to raymarch.
@@ -619,7 +617,7 @@ export class Path {
619
617
  const doRaymarching = strokeRadius && strokeRadius > 1e-8;
620
618
  if (doRaymarching) {
621
619
  // Starting points for raymarching (in addition to the end points of the line).
622
- const startPoints = result.map(intersection => intersection.point);
620
+ const startPoints = result.map((intersection) => intersection.point);
623
621
  result = this.raymarchIntersectionWith(line, strokeRadius, startPoints);
624
622
  }
625
623
 
@@ -678,9 +676,17 @@ export class Path {
678
676
  *
679
677
  * This method is analogous to {@link Array.toSpliced}.
680
678
  */
681
- public spliced(deleteFrom: CurveIndexRecord, deleteTo: CurveIndexRecord, insert: Path|undefined, options?: PathSplitOptions): Path {
679
+ public spliced(
680
+ deleteFrom: CurveIndexRecord,
681
+ deleteTo: CurveIndexRecord,
682
+ insert: Path | undefined,
683
+ options?: PathSplitOptions,
684
+ ): Path {
682
685
  const isBeforeOrEqual = (a: CurveIndexRecord, b: CurveIndexRecord) => {
683
- return a.curveIndex < b.curveIndex || (a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue);
686
+ return (
687
+ a.curveIndex < b.curveIndex ||
688
+ (a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue)
689
+ );
684
690
  };
685
691
 
686
692
  if (isBeforeOrEqual(deleteFrom, deleteTo)) {
@@ -711,11 +717,14 @@ export class Path {
711
717
  }
712
718
  }
713
719
 
714
- public splitAt(at: CurveIndexRecord, options?: PathSplitOptions): [Path]|[Path, Path];
720
+ public splitAt(at: CurveIndexRecord, options?: PathSplitOptions): [Path] | [Path, Path];
715
721
  public splitAt(at: CurveIndexRecord[], options?: PathSplitOptions): Path[];
716
722
 
717
723
  // @internal
718
- public splitAt(splitAt: CurveIndexRecord[]|CurveIndexRecord, options?: PathSplitOptions): Path[] {
724
+ public splitAt(
725
+ splitAt: CurveIndexRecord[] | CurveIndexRecord,
726
+ options?: PathSplitOptions,
727
+ ): Path[] {
719
728
  if (!Array.isArray(splitAt)) {
720
729
  splitAt = [splitAt];
721
730
  }
@@ -728,9 +737,9 @@ export class Path {
728
737
  //
729
738
 
730
739
  while (
731
- splitAt.length > 0
732
- && splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1
733
- && splitAt[splitAt.length - 1].parameterValue >= 1
740
+ splitAt.length > 0 &&
741
+ splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1 &&
742
+ splitAt[splitAt.length - 1].parameterValue >= 1
734
743
  ) {
735
744
  splitAt.pop();
736
745
  }
@@ -738,9 +747,9 @@ export class Path {
738
747
  splitAt.reverse(); // .reverse() <-- We're `.pop`ing from the end
739
748
 
740
749
  while (
741
- splitAt.length > 0
742
- && splitAt[splitAt.length - 1].curveIndex <= 0
743
- && splitAt[splitAt.length - 1].parameterValue <= 0
750
+ splitAt.length > 0 &&
751
+ splitAt[splitAt.length - 1].curveIndex <= 0 &&
752
+ splitAt[splitAt.length - 1].parameterValue <= 0
744
753
  ) {
745
754
  splitAt.pop();
746
755
  }
@@ -750,7 +759,7 @@ export class Path {
750
759
  }
751
760
 
752
761
  const expectedSplitCount = splitAt.length + 1;
753
- const mapNewPoint = options?.mapNewPoint ?? ((p: Point2)=>p);
762
+ const mapNewPoint = options?.mapNewPoint ?? ((p: Point2) => p);
754
763
 
755
764
  const result: Path[] = [];
756
765
  let currentStartPoint = this.startPoint;
@@ -762,7 +771,7 @@ export class Path {
762
771
 
763
772
  let { curveIndex, parameterValue } = splitAt.pop()!;
764
773
 
765
- for (let i = 0; i < this.parts.length; i ++) {
774
+ for (let i = 0; i < this.parts.length; i++) {
766
775
  if (i !== curveIndex) {
767
776
  currentPath.push(this.parts[i]);
768
777
  } else {
@@ -773,71 +782,71 @@ export class Path {
773
782
  const newPath: PathCommand[] = [];
774
783
 
775
784
  switch (part.kind) {
776
- case PathCommandType.MoveTo:
777
- currentPath.push({
778
- kind: part.kind,
779
- point: part.point,
780
- });
781
- newPathStart = part.point;
782
- break;
783
- case PathCommandType.LineTo:
784
- {
785
- const split = (geom as LineSegment2).splitAt(parameterValue);
785
+ case PathCommandType.MoveTo:
786
786
  currentPath.push({
787
787
  kind: part.kind,
788
- point: mapNewPoint(split[0].p2),
788
+ point: part.point,
789
789
  });
790
- newPathStart = split[0].p2;
791
- if (split.length > 1) {
792
- console.assert(split.length === 2);
793
- newPath.push({
790
+ newPathStart = part.point;
791
+ break;
792
+ case PathCommandType.LineTo:
793
+ {
794
+ const split = (geom as LineSegment2).splitAt(parameterValue);
795
+ currentPath.push({
794
796
  kind: part.kind,
795
-
796
- // Don't map: For lines, the end point of the split is
797
- // the same as the end point of the original:
798
- point: split[1]!.p2,
797
+ point: mapNewPoint(split[0].p2),
799
798
  });
800
- geom = split[1]!;
801
- }
802
- }
803
- break;
804
- case PathCommandType.QuadraticBezierTo:
805
- case PathCommandType.CubicBezierTo:
806
- {
807
- const split = (geom as BezierJSWrapper).splitAt(parameterValue);
808
- let isFirstPart = split.length === 2;
809
- for (const segment of split) {
810
- geom = segment;
811
- const targetArray = isFirstPart ? currentPath : newPath;
812
- const controlPoints = segment.getPoints();
813
- if (part.kind === PathCommandType.CubicBezierTo) {
814
- targetArray.push({
815
- kind: part.kind,
816
- controlPoint1: mapNewPoint(controlPoints[1]),
817
- controlPoint2: mapNewPoint(controlPoints[2]),
818
- endPoint: mapNewPoint(controlPoints[3]),
819
- });
820
- } else {
821
- targetArray.push({
799
+ newPathStart = split[0].p2;
800
+ if (split.length > 1) {
801
+ console.assert(split.length === 2);
802
+ newPath.push({
822
803
  kind: part.kind,
823
- controlPoint: mapNewPoint(controlPoints[1]),
824
- endPoint: mapNewPoint(controlPoints[2]),
804
+
805
+ // Don't map: For lines, the end point of the split is
806
+ // the same as the end point of the original:
807
+ point: split[1]!.p2,
825
808
  });
809
+ geom = split[1]!;
826
810
  }
827
-
828
- // We want the start of the new path to match the start of the
829
- // FIRST Bézier in the NEW path.
830
- if (!isFirstPart) {
831
- newPathStart = controlPoints[0];
811
+ }
812
+ break;
813
+ case PathCommandType.QuadraticBezierTo:
814
+ case PathCommandType.CubicBezierTo:
815
+ {
816
+ const split = (geom as BezierJSWrapper).splitAt(parameterValue);
817
+ let isFirstPart = split.length === 2;
818
+ for (const segment of split) {
819
+ geom = segment;
820
+ const targetArray = isFirstPart ? currentPath : newPath;
821
+ const controlPoints = segment.getPoints();
822
+ if (part.kind === PathCommandType.CubicBezierTo) {
823
+ targetArray.push({
824
+ kind: part.kind,
825
+ controlPoint1: mapNewPoint(controlPoints[1]),
826
+ controlPoint2: mapNewPoint(controlPoints[2]),
827
+ endPoint: mapNewPoint(controlPoints[3]),
828
+ });
829
+ } else {
830
+ targetArray.push({
831
+ kind: part.kind,
832
+ controlPoint: mapNewPoint(controlPoints[1]),
833
+ endPoint: mapNewPoint(controlPoints[2]),
834
+ });
835
+ }
836
+
837
+ // We want the start of the new path to match the start of the
838
+ // FIRST Bézier in the NEW path.
839
+ if (!isFirstPart) {
840
+ newPathStart = controlPoints[0];
841
+ }
842
+ isFirstPart = false;
832
843
  }
833
- isFirstPart = false;
834
844
  }
845
+ break;
846
+ default: {
847
+ const exhaustivenessCheck: never = part;
848
+ return exhaustivenessCheck;
835
849
  }
836
- break;
837
- default: {
838
- const exhaustivenessCheck: never = part;
839
- return exhaustivenessCheck;
840
- }
841
850
  }
842
851
 
843
852
  result.push(new Path(currentStartPoint, [...currentPath]));
@@ -867,7 +876,7 @@ export class Path {
867
876
 
868
877
  console.assert(
869
878
  result.length === expectedSplitCount,
870
- `should split into splitAt.length + 1 splits (was ${result.length}, expected ${expectedSplitCount})`
879
+ `should split into splitAt.length + 1 splits (was ${result.length}, expected ${expectedSplitCount})`,
871
880
  );
872
881
  return result;
873
882
  }
@@ -907,37 +916,40 @@ export class Path {
907
916
  return result;
908
917
  }
909
918
 
910
- private static mapPathCommand(part: PathCommand, mapping: (point: Point2)=> Point2): PathCommand {
919
+ private static mapPathCommand(
920
+ part: PathCommand,
921
+ mapping: (point: Point2) => Point2,
922
+ ): PathCommand {
911
923
  switch (part.kind) {
912
- case PathCommandType.MoveTo:
913
- case PathCommandType.LineTo:
914
- return {
915
- kind: part.kind,
916
- point: mapping(part.point),
917
- };
918
- break;
919
- case PathCommandType.CubicBezierTo:
920
- return {
921
- kind: part.kind,
922
- controlPoint1: mapping(part.controlPoint1),
923
- controlPoint2: mapping(part.controlPoint2),
924
- endPoint: mapping(part.endPoint),
925
- };
926
- break;
927
- case PathCommandType.QuadraticBezierTo:
928
- return {
929
- kind: part.kind,
930
- controlPoint: mapping(part.controlPoint),
931
- endPoint: mapping(part.endPoint),
932
- };
933
- break;
924
+ case PathCommandType.MoveTo:
925
+ case PathCommandType.LineTo:
926
+ return {
927
+ kind: part.kind,
928
+ point: mapping(part.point),
929
+ };
930
+ break;
931
+ case PathCommandType.CubicBezierTo:
932
+ return {
933
+ kind: part.kind,
934
+ controlPoint1: mapping(part.controlPoint1),
935
+ controlPoint2: mapping(part.controlPoint2),
936
+ endPoint: mapping(part.endPoint),
937
+ };
938
+ break;
939
+ case PathCommandType.QuadraticBezierTo:
940
+ return {
941
+ kind: part.kind,
942
+ controlPoint: mapping(part.controlPoint),
943
+ endPoint: mapping(part.endPoint),
944
+ };
945
+ break;
934
946
  }
935
947
 
936
948
  const exhaustivenessCheck: never = part;
937
949
  return exhaustivenessCheck;
938
950
  }
939
951
 
940
- public mapPoints(mapping: (point: Point2)=>Point2): Path {
952
+ public mapPoints(mapping: (point: Point2) => Point2): Path {
941
953
  const startPoint = mapping(this.startPoint);
942
954
  const newParts: PathCommand[] = [];
943
955
 
@@ -953,7 +965,7 @@ export class Path {
953
965
  return this;
954
966
  }
955
967
 
956
- return this.mapPoints(point => affineTransfm.transformVec2(point));
968
+ return this.mapPoints((point) => affineTransfm.transformVec2(point));
957
969
  }
958
970
 
959
971
  /**
@@ -974,7 +986,7 @@ export class Path {
974
986
 
975
987
  // Creates a new path by joining [other] to the end of this path
976
988
  public union(
977
- other: Path|PathCommand[]|null,
989
+ other: Path | PathCommand[] | null,
978
990
 
979
991
  // allowReverse: true iff reversing other or this is permitted if it means
980
992
  // no moveTo command is necessary when unioning the paths.
@@ -1024,36 +1036,35 @@ export class Path {
1024
1036
  let lastPoint: Point2 = this.startPoint;
1025
1037
  for (const part of this.parts) {
1026
1038
  switch (part.kind) {
1027
- case PathCommandType.LineTo:
1028
- case PathCommandType.MoveTo:
1029
- newParts.push({
1030
- kind: part.kind,
1031
- point: lastPoint,
1032
- });
1033
- lastPoint = part.point;
1034
- break;
1035
- case PathCommandType.CubicBezierTo:
1036
- newParts.push({
1037
- kind: part.kind,
1038
- controlPoint1: part.controlPoint2,
1039
- controlPoint2: part.controlPoint1,
1040
- endPoint: lastPoint,
1041
- });
1042
- lastPoint = part.endPoint;
1043
- break;
1044
- case PathCommandType.QuadraticBezierTo:
1045
- newParts.push({
1046
- kind: part.kind,
1047
- controlPoint: part.controlPoint,
1048
- endPoint: lastPoint,
1049
- });
1050
- lastPoint = part.endPoint;
1051
- break;
1052
- default:
1053
- {
1054
- const exhaustivenessCheck: never = part;
1055
- return exhaustivenessCheck;
1056
- }
1039
+ case PathCommandType.LineTo:
1040
+ case PathCommandType.MoveTo:
1041
+ newParts.push({
1042
+ kind: part.kind,
1043
+ point: lastPoint,
1044
+ });
1045
+ lastPoint = part.point;
1046
+ break;
1047
+ case PathCommandType.CubicBezierTo:
1048
+ newParts.push({
1049
+ kind: part.kind,
1050
+ controlPoint1: part.controlPoint2,
1051
+ controlPoint2: part.controlPoint1,
1052
+ endPoint: lastPoint,
1053
+ });
1054
+ lastPoint = part.endPoint;
1055
+ break;
1056
+ case PathCommandType.QuadraticBezierTo:
1057
+ newParts.push({
1058
+ kind: part.kind,
1059
+ controlPoint: part.controlPoint,
1060
+ endPoint: lastPoint,
1061
+ });
1062
+ lastPoint = part.endPoint;
1063
+ break;
1064
+ default: {
1065
+ const exhaustivenessCheck: never = part;
1066
+ return exhaustivenessCheck;
1067
+ }
1057
1068
  }
1058
1069
  }
1059
1070
  newParts.reverse();
@@ -1066,7 +1077,10 @@ export class Path {
1066
1077
  return this.startPoint;
1067
1078
  }
1068
1079
  const lastPart = this.parts[this.parts.length - 1];
1069
- if (lastPart.kind === PathCommandType.QuadraticBezierTo || lastPart.kind === PathCommandType.CubicBezierTo) {
1080
+ if (
1081
+ lastPart.kind === PathCommandType.QuadraticBezierTo ||
1082
+ lastPart.kind === PathCommandType.CubicBezierTo
1083
+ ) {
1070
1084
  return lastPart.endPoint;
1071
1085
  } else {
1072
1086
  return lastPart.point;
@@ -1138,7 +1152,7 @@ export class Path {
1138
1152
  let intersectionCount = 0;
1139
1153
  for (const line of polygon) {
1140
1154
  if (line.intersects(testLine)) {
1141
- intersectionCount ++;
1155
+ intersectionCount++;
1142
1156
  }
1143
1157
  }
1144
1158
 
@@ -1178,40 +1192,39 @@ export class Path {
1178
1192
  const part2 = other.parts[i];
1179
1193
 
1180
1194
  switch (part1.kind) {
1181
- case PathCommandType.LineTo:
1182
- case PathCommandType.MoveTo:
1183
- if (part1.kind !== part2.kind) {
1184
- return false;
1185
- } else if(!part1.point.eq(part2.point, tolerance)) {
1186
- return false;
1187
- }
1188
- break;
1189
- case PathCommandType.CubicBezierTo:
1190
- if (part1.kind !== part2.kind) {
1191
- return false;
1192
- } else if (
1193
- !part1.controlPoint1.eq(part2.controlPoint1, tolerance)
1194
- || !part1.controlPoint2.eq(part2.controlPoint2, tolerance)
1195
- || !part1.endPoint.eq(part2.endPoint, tolerance)
1196
- ) {
1197
- return false;
1198
- }
1199
- break;
1200
- case PathCommandType.QuadraticBezierTo:
1201
- if (part1.kind !== part2.kind) {
1202
- return false;
1203
- } else if (
1204
- !part1.controlPoint.eq(part2.controlPoint, tolerance)
1205
- || !part1.endPoint.eq(part2.endPoint, tolerance)
1206
- ) {
1207
- return false;
1195
+ case PathCommandType.LineTo:
1196
+ case PathCommandType.MoveTo:
1197
+ if (part1.kind !== part2.kind) {
1198
+ return false;
1199
+ } else if (!part1.point.eq(part2.point, tolerance)) {
1200
+ return false;
1201
+ }
1202
+ break;
1203
+ case PathCommandType.CubicBezierTo:
1204
+ if (part1.kind !== part2.kind) {
1205
+ return false;
1206
+ } else if (
1207
+ !part1.controlPoint1.eq(part2.controlPoint1, tolerance) ||
1208
+ !part1.controlPoint2.eq(part2.controlPoint2, tolerance) ||
1209
+ !part1.endPoint.eq(part2.endPoint, tolerance)
1210
+ ) {
1211
+ return false;
1212
+ }
1213
+ break;
1214
+ case PathCommandType.QuadraticBezierTo:
1215
+ if (part1.kind !== part2.kind) {
1216
+ return false;
1217
+ } else if (
1218
+ !part1.controlPoint.eq(part2.controlPoint, tolerance) ||
1219
+ !part1.endPoint.eq(part2.endPoint, tolerance)
1220
+ ) {
1221
+ return false;
1222
+ }
1223
+ break;
1224
+ default: {
1225
+ const exhaustivenessCheck: never = part1;
1226
+ return exhaustivenessCheck;
1208
1227
  }
1209
- break;
1210
- default:
1211
- {
1212
- const exhaustivenessCheck: never = part1;
1213
- return exhaustivenessCheck;
1214
- }
1215
1228
  }
1216
1229
  }
1217
1230
 
@@ -1225,7 +1238,7 @@ export class Path {
1225
1238
  * border around `rect`. Otherwise, the resultant path is just the border
1226
1239
  * of `rect`.
1227
1240
  */
1228
- public static fromRect(rect: Rect2, lineWidth: number|null = null): Path {
1241
+ public static fromRect(rect: Rect2, lineWidth: number | null = null): Path {
1229
1242
  const commands: PathCommand[] = [];
1230
1243
 
1231
1244
  let corners;
@@ -1237,18 +1250,14 @@ export class Path {
1237
1250
  const cornerToEdge = Vec2.of(lineWidth, lineWidth).times(0.5);
1238
1251
  const innerRect = Rect2.fromCorners(
1239
1252
  rect.topLeft.plus(cornerToEdge),
1240
- rect.bottomRight.minus(cornerToEdge)
1253
+ rect.bottomRight.minus(cornerToEdge),
1241
1254
  );
1242
1255
  const outerRect = Rect2.fromCorners(
1243
1256
  rect.topLeft.minus(cornerToEdge),
1244
- rect.bottomRight.plus(cornerToEdge)
1257
+ rect.bottomRight.plus(cornerToEdge),
1245
1258
  );
1246
1259
 
1247
- corners = [
1248
- innerRect.corners[3],
1249
- ...innerRect.corners,
1250
- ...outerRect.corners.reverse(),
1251
- ];
1260
+ corners = [innerRect.corners[3], ...innerRect.corners, ...outerRect.corners.reverse()];
1252
1261
  startPoint = outerRect.corners[3];
1253
1262
  } else {
1254
1263
  corners = rect.corners.slice(1);
@@ -1271,7 +1280,7 @@ export class Path {
1271
1280
  return new Path(startPoint, commands);
1272
1281
  }
1273
1282
 
1274
- private cachedStringVersion: string|null = null;
1283
+ private cachedStringVersion: string | null = null;
1275
1284
 
1276
1285
  /**
1277
1286
  * Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
@@ -1302,10 +1311,14 @@ export class Path {
1302
1311
 
1303
1312
  // @param onlyAbsCommands - True if we should avoid converting absolute coordinates to relative offsets -- such
1304
1313
  // conversions can lead to smaller output strings, but also take time.
1305
- public static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string {
1314
+ public static toString(
1315
+ startPoint: Point2,
1316
+ parts: PathCommand[],
1317
+ onlyAbsCommands?: boolean,
1318
+ ): string {
1306
1319
  const result: string[] = [];
1307
1320
 
1308
- let prevPoint: Point2|undefined;
1321
+ let prevPoint: Point2 | undefined;
1309
1322
  const addCommand = (command: string, ...points: Point2[]) => {
1310
1323
  const absoluteCommandParts: string[] = [];
1311
1324
  const relativeCommandParts: string[] = [];
@@ -1319,8 +1332,18 @@ export class Path {
1319
1332
 
1320
1333
  // Relative commands are often shorter as strings than absolute commands.
1321
1334
  if (!makeAbsCommand) {
1322
- const xComponentRelative = toStringOfSamePrecision(point.x - prevPoint!.x, xComponent, roundedPrevX, roundedPrevY);
1323
- const yComponentRelative = toStringOfSamePrecision(point.y - prevPoint!.y, yComponent, roundedPrevX, roundedPrevY);
1335
+ const xComponentRelative = toStringOfSamePrecision(
1336
+ point.x - prevPoint!.x,
1337
+ xComponent,
1338
+ roundedPrevX,
1339
+ roundedPrevY,
1340
+ );
1341
+ const yComponentRelative = toStringOfSamePrecision(
1342
+ point.y - prevPoint!.y,
1343
+ yComponent,
1344
+ roundedPrevX,
1345
+ roundedPrevY,
1346
+ );
1324
1347
 
1325
1348
  // No need for an additional separator if it starts with a '-'
1326
1349
  if (yComponentRelative.charAt(0) === '-') {
@@ -1363,21 +1386,21 @@ export class Path {
1363
1386
  const part = parts[i];
1364
1387
 
1365
1388
  switch (part.kind) {
1366
- case PathCommandType.MoveTo:
1367
- addCommand('M', part.point);
1368
- break;
1369
- case PathCommandType.LineTo:
1370
- addCommand('L', part.point);
1371
- break;
1372
- case PathCommandType.CubicBezierTo:
1373
- addCommand('C', part.controlPoint1, part.controlPoint2, part.endPoint);
1374
- break;
1375
- case PathCommandType.QuadraticBezierTo:
1376
- addCommand('Q', part.controlPoint, part.endPoint);
1377
- break;
1378
- default:
1379
- exhaustivenessCheck = part;
1380
- return exhaustivenessCheck;
1389
+ case PathCommandType.MoveTo:
1390
+ addCommand('M', part.point);
1391
+ break;
1392
+ case PathCommandType.LineTo:
1393
+ addCommand('L', part.point);
1394
+ break;
1395
+ case PathCommandType.CubicBezierTo:
1396
+ addCommand('C', part.controlPoint1, part.controlPoint2, part.endPoint);
1397
+ break;
1398
+ case PathCommandType.QuadraticBezierTo:
1399
+ addCommand('Q', part.controlPoint, part.endPoint);
1400
+ break;
1401
+ default:
1402
+ exhaustivenessCheck = part;
1403
+ return exhaustivenessCheck;
1381
1404
  }
1382
1405
  }
1383
1406
 
@@ -1410,12 +1433,11 @@ export class Path {
1410
1433
  pathString = pathString.split('\n').join(' ');
1411
1434
 
1412
1435
  let lastPos: Point2 = Vec2.zero;
1413
- let firstPos: Point2|null = null;
1414
- let startPos: Point2|null = null;
1436
+ let firstPos: Point2 | null = null;
1437
+ let startPos: Point2 | null = null;
1415
1438
  let isFirstCommand: boolean = true;
1416
1439
  const commands: PathCommand[] = [];
1417
1440
 
1418
-
1419
1441
  const moveTo = (point: Point2) => {
1420
1442
  // The first moveTo/lineTo is already handled by the [startPoint] parameter of the Path constructor.
1421
1443
  if (isFirstCommand) {
@@ -1455,36 +1477,38 @@ export class Path {
1455
1477
  });
1456
1478
  };
1457
1479
  const commandArgCounts: Record<string, number> = {
1458
- 'm': 1,
1459
- 'l': 1,
1460
- 'c': 3,
1461
- 'q': 2,
1462
- 'z': 0,
1463
- 'h': 1,
1464
- 'v': 1,
1480
+ m: 1,
1481
+ l: 1,
1482
+ c: 3,
1483
+ q: 2,
1484
+ z: 0,
1485
+ h: 1,
1486
+ v: 1,
1465
1487
  };
1466
1488
 
1467
1489
  // Each command: Command character followed by anything that isn't a command character
1468
- const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/ig;
1490
+ const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/gi;
1469
1491
  let current;
1470
1492
  while ((current = commandExp.exec(pathString)) !== null) {
1471
- const argParts = current[2].trim().split(/[^0-9Ee.-]/).filter(
1472
- part => part.length > 0
1473
- ).reduce((accumualtor: string[], current: string): string[] => {
1474
- // As of 09/2022, iOS Safari doesn't support support lookbehind in regular
1475
- // expressions. As such, we need an alternative.
1476
- // Because '-' can be used as a path separator, unless preceeded by an 'e' (as in 1e-5),
1477
- // we need special cases:
1478
- current = current.replace(/([^eE])[-]/g, '$1 -');
1479
- const parts = current.split(' -');
1480
- if (parts[0] !== '') {
1481
- accumualtor.push(parts[0]);
1482
- }
1483
- accumualtor.push(...parts.slice(1).map(part => `-${part}`));
1484
- return accumualtor;
1485
- }, []);
1493
+ const argParts = current[2]
1494
+ .trim()
1495
+ .split(/[^0-9Ee.-]/)
1496
+ .filter((part) => part.length > 0)
1497
+ .reduce((accumualtor: string[], current: string): string[] => {
1498
+ // As of 09/2022, iOS Safari doesn't support support lookbehind in regular
1499
+ // expressions. As such, we need an alternative.
1500
+ // Because '-' can be used as a path separator, unless preceeded by an 'e' (as in 1e-5),
1501
+ // we need special cases:
1502
+ current = current.replace(/([^eE])[-]/g, '$1 -');
1503
+ const parts = current.split(' -');
1504
+ if (parts[0] !== '') {
1505
+ accumualtor.push(parts[0]);
1506
+ }
1507
+ accumualtor.push(...parts.slice(1).map((part) => `-${part}`));
1508
+ return accumualtor;
1509
+ }, []);
1486
1510
 
1487
- let numericArgs = argParts.map(arg => parseFloat(arg));
1511
+ let numericArgs = argParts.map((arg) => parseFloat(arg));
1488
1512
 
1489
1513
  let commandChar = current[1].toLowerCase();
1490
1514
  let uppercaseCommand = current[1] !== commandChar;
@@ -1501,7 +1525,7 @@ export class Path {
1501
1525
  commandChar = 'l';
1502
1526
  } else if (commandChar === 'z') {
1503
1527
  if (firstPos) {
1504
- numericArgs = [ firstPos.x, firstPos.y ];
1528
+ numericArgs = [firstPos.x, firstPos.y];
1505
1529
  firstPos = lastPos;
1506
1530
  } else {
1507
1531
  continue;
@@ -1512,65 +1536,66 @@ export class Path {
1512
1536
  commandChar = 'l';
1513
1537
  }
1514
1538
 
1515
-
1516
1539
  const commandArgCount: number = commandArgCounts[commandChar] ?? 0;
1517
- const allArgs = numericArgs.reduce((
1518
- accumulator: Point2[], current, index, parts
1519
- ): Point2[] => {
1520
- if (index % 2 !== 0) {
1521
- const currentAsFloat = current;
1522
- const prevAsFloat = parts[index - 1];
1523
- return accumulator.concat(Vec2.of(prevAsFloat, currentAsFloat));
1524
- } else {
1525
- return accumulator;
1526
- }
1527
- }, []).map((coordinate, index): Point2 => {
1528
- // Lowercase commands are relative, uppercase commands use absolute
1529
- // positioning
1530
- let newPos;
1531
- if (uppercaseCommand) {
1532
- newPos = coordinate;
1533
- } else {
1534
- newPos = lastPos.plus(coordinate);
1535
- }
1540
+ const allArgs = numericArgs
1541
+ .reduce((accumulator: Point2[], current, index, parts): Point2[] => {
1542
+ if (index % 2 !== 0) {
1543
+ const currentAsFloat = current;
1544
+ const prevAsFloat = parts[index - 1];
1545
+ return accumulator.concat(Vec2.of(prevAsFloat, currentAsFloat));
1546
+ } else {
1547
+ return accumulator;
1548
+ }
1549
+ }, [])
1550
+ .map((coordinate, index): Point2 => {
1551
+ // Lowercase commands are relative, uppercase commands use absolute
1552
+ // positioning
1553
+ let newPos;
1554
+ if (uppercaseCommand) {
1555
+ newPos = coordinate;
1556
+ } else {
1557
+ newPos = lastPos.plus(coordinate);
1558
+ }
1536
1559
 
1537
- if ((index + 1) % commandArgCount === 0) {
1538
- lastPos = newPos;
1539
- }
1560
+ if ((index + 1) % commandArgCount === 0) {
1561
+ lastPos = newPos;
1562
+ }
1540
1563
 
1541
- return newPos;
1542
- });
1564
+ return newPos;
1565
+ });
1543
1566
 
1544
1567
  if (allArgs.length % commandArgCount !== 0) {
1545
- throw new Error([
1546
- `Incorrect number of arguments: got ${JSON.stringify(allArgs)} with a length of ${allArgs.length} ≠ ${commandArgCount}k, k ∈ ℤ.`,
1547
- `The number of arguments to ${commandChar} must be a multiple of ${commandArgCount}!`,
1548
- `Command: ${current[0]}`,
1549
- ].join('\n'));
1568
+ throw new Error(
1569
+ [
1570
+ `Incorrect number of arguments: got ${JSON.stringify(allArgs)} with a length of ${allArgs.length} ≠ ${commandArgCount}k, k ∈ ℤ.`,
1571
+ `The number of arguments to ${commandChar} must be a multiple of ${commandArgCount}!`,
1572
+ `Command: ${current[0]}`,
1573
+ ].join('\n'),
1574
+ );
1550
1575
  }
1551
1576
 
1552
1577
  for (let argPos = 0; argPos < allArgs.length; argPos += commandArgCount) {
1553
1578
  const args = allArgs.slice(argPos, argPos + commandArgCount);
1554
1579
 
1555
1580
  switch (commandChar.toLowerCase()) {
1556
- case 'm':
1557
- if (argPos === 0) {
1558
- moveTo(args[0]);
1559
- } else {
1581
+ case 'm':
1582
+ if (argPos === 0) {
1583
+ moveTo(args[0]);
1584
+ } else {
1585
+ lineTo(args[0]);
1586
+ }
1587
+ break;
1588
+ case 'l':
1560
1589
  lineTo(args[0]);
1561
- }
1562
- break;
1563
- case 'l':
1564
- lineTo(args[0]);
1565
- break;
1566
- case 'c':
1567
- cubicBezierTo(args[0], args[1], args[2]);
1568
- break;
1569
- case 'q':
1570
- quadraticBeierTo(args[0], args[1]);
1571
- break;
1572
- default:
1573
- throw new Error(`Unknown path command ${commandChar}`);
1590
+ break;
1591
+ case 'c':
1592
+ cubicBezierTo(args[0], args[1], args[2]);
1593
+ break;
1594
+ case 'q':
1595
+ quadraticBeierTo(args[0], args[1]);
1596
+ break;
1597
+ default:
1598
+ throw new Error(`Unknown path command ${commandChar}`);
1574
1599
  }
1575
1600
 
1576
1601
  isFirstCommand = false;
@@ -1595,10 +1620,12 @@ export class Path {
1595
1620
 
1596
1621
  const hull = convexHull2Of(points);
1597
1622
 
1598
- const commands = hull.slice(1).map((p): LinePathCommand => ({
1599
- kind: PathCommandType.LineTo,
1600
- point: p,
1601
- }));
1623
+ const commands = hull.slice(1).map(
1624
+ (p): LinePathCommand => ({
1625
+ kind: PathCommandType.LineTo,
1626
+ point: p,
1627
+ }),
1628
+ );
1602
1629
  // Close -- connect back to the start
1603
1630
  commands.push({
1604
1631
  kind: PathCommandType.LineTo,