@js-draw/math 1.21.3 → 1.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/build-config.json +1 -1
  2. package/dist/cjs/Color4.d.ts +24 -1
  3. package/dist/cjs/Color4.js +35 -3
  4. package/dist/cjs/Mat33.d.ts +21 -11
  5. package/dist/cjs/Mat33.js +28 -24
  6. package/dist/cjs/Vec3.d.ts +12 -3
  7. package/dist/cjs/Vec3.js +20 -9
  8. package/dist/cjs/lib.d.ts +3 -0
  9. package/dist/cjs/lib.js +3 -0
  10. package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
  11. package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
  12. package/dist/cjs/shapes/LineSegment2.js +13 -17
  13. package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
  14. package/dist/cjs/shapes/Path.d.ts +1 -0
  15. package/dist/cjs/shapes/Path.js +50 -47
  16. package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
  17. package/dist/cjs/shapes/QuadraticBezier.js +26 -3
  18. package/dist/cjs/shapes/Rect2.d.ts +13 -0
  19. package/dist/cjs/shapes/Rect2.js +35 -16
  20. package/dist/cjs/shapes/Triangle.js +4 -5
  21. package/dist/cjs/utils/convexHull2Of.js +3 -3
  22. package/dist/mjs/Color4.d.ts +24 -1
  23. package/dist/mjs/Color4.mjs +35 -3
  24. package/dist/mjs/Mat33.d.ts +21 -11
  25. package/dist/mjs/Mat33.mjs +28 -24
  26. package/dist/mjs/Vec3.d.ts +12 -3
  27. package/dist/mjs/Vec3.mjs +20 -9
  28. package/dist/mjs/lib.d.ts +3 -0
  29. package/dist/mjs/lib.mjs +3 -0
  30. package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
  31. package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
  32. package/dist/mjs/shapes/LineSegment2.mjs +13 -17
  33. package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
  34. package/dist/mjs/shapes/Path.d.ts +1 -0
  35. package/dist/mjs/shapes/Path.mjs +50 -47
  36. package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
  37. package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
  38. package/dist/mjs/shapes/Rect2.d.ts +13 -0
  39. package/dist/mjs/shapes/Rect2.mjs +35 -16
  40. package/dist/mjs/shapes/Triangle.mjs +4 -5
  41. package/dist/mjs/utils/convexHull2Of.mjs +3 -3
  42. package/dist-test/test_imports/test-require.cjs +1 -1
  43. package/package.json +3 -3
  44. package/src/Color4.test.ts +21 -21
  45. package/src/Color4.ts +61 -18
  46. package/src/Mat33.fromCSSMatrix.test.ts +32 -46
  47. package/src/Mat33.test.ts +64 -102
  48. package/src/Mat33.ts +81 -104
  49. package/src/Vec2.test.ts +3 -3
  50. package/src/Vec3.test.ts +2 -3
  51. package/src/Vec3.ts +46 -61
  52. package/src/lib.ts +3 -2
  53. package/src/polynomial/solveQuadratic.test.ts +39 -13
  54. package/src/polynomial/solveQuadratic.ts +5 -6
  55. package/src/rounding/cleanUpNumber.test.ts +1 -1
  56. package/src/rounding/constants.ts +1 -3
  57. package/src/rounding/getLenAfterDecimal.ts +1 -2
  58. package/src/rounding/lib.ts +1 -2
  59. package/src/rounding/toRoundedString.test.ts +1 -1
  60. package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
  61. package/src/rounding/toStringOfSamePrecision.ts +1 -1
  62. package/src/shapes/BezierJSWrapper.ts +56 -37
  63. package/src/shapes/CubicBezier.ts +3 -3
  64. package/src/shapes/LineSegment2.test.ts +24 -17
  65. package/src/shapes/LineSegment2.ts +26 -29
  66. package/src/shapes/Parameterized2DShape.ts +5 -4
  67. package/src/shapes/Path.fromString.test.ts +5 -5
  68. package/src/shapes/Path.test.ts +122 -120
  69. package/src/shapes/Path.toString.test.ts +7 -7
  70. package/src/shapes/Path.ts +379 -352
  71. package/src/shapes/PointShape2D.ts +3 -3
  72. package/src/shapes/QuadraticBezier.test.ts +27 -21
  73. package/src/shapes/QuadraticBezier.ts +26 -11
  74. package/src/shapes/Rect2.test.ts +44 -75
  75. package/src/shapes/Rect2.ts +47 -35
  76. package/src/shapes/Triangle.test.ts +31 -29
  77. package/src/shapes/Triangle.ts +17 -18
  78. package/src/utils/convexHull2Of.test.ts +54 -15
  79. package/src/utils/convexHull2Of.ts +9 -7
  80. package/tsconfig.json +1 -3
  81. package/typedoc.json +2 -2
@@ -1,9 +1,8 @@
1
-
2
1
  import solveQuadratic from './solveQuadratic';
3
2
 
4
3
  describe('solveQuadratic', () => {
5
4
  it('should solve linear equations', () => {
6
- expect(solveQuadratic(0, 1, 2)).toMatchObject([ -2, -2 ]);
5
+ expect(solveQuadratic(0, 1, 2)).toMatchObject([-2, -2]);
7
6
  expect(solveQuadratic(0, 0, 2)[0]).toBeNaN();
8
7
  });
9
8
 
@@ -11,21 +10,48 @@ describe('solveQuadratic', () => {
11
10
  type TestCase = [[number, number, number], [number, number]];
12
11
 
13
12
  const testCases: TestCase[] = [
14
- [ [ 1, 0, 0 ], [ 0, 0 ] ],
15
- [ [ 2, 0, 0 ], [ 0, 0 ] ],
13
+ [
14
+ [1, 0, 0],
15
+ [0, 0],
16
+ ],
17
+ [
18
+ [2, 0, 0],
19
+ [0, 0],
20
+ ],
16
21
 
17
- [ [ 1, 0, -1 ], [ 1, -1 ] ],
18
- [ [ 1, 0, -4 ], [ 2, -2 ] ],
19
- [ [ 1, 0, 4 ], [ NaN, NaN ] ],
22
+ [
23
+ [1, 0, -1],
24
+ [1, -1],
25
+ ],
26
+ [
27
+ [1, 0, -4],
28
+ [2, -2],
29
+ ],
30
+ [
31
+ [1, 0, 4],
32
+ [NaN, NaN],
33
+ ],
20
34
 
21
- [ [ 1, 1, 0 ], [ 0, -1 ] ],
22
- [ [ 1, 2, 0 ], [ 0, -2 ] ],
35
+ [
36
+ [1, 1, 0],
37
+ [0, -1],
38
+ ],
39
+ [
40
+ [1, 2, 0],
41
+ [0, -2],
42
+ ],
23
43
 
24
- [ [ 1, 2, 1 ], [ -1, -1 ] ],
25
- [ [ -9, 2, 1/3 ], [ 1/3, -1/9 ] ],
44
+ [
45
+ [1, 2, 1],
46
+ [-1, -1],
47
+ ],
48
+ [
49
+ [-9, 2, 1 / 3],
50
+ [1 / 3, -1 / 9],
51
+ ],
26
52
  ];
27
53
 
28
- for (const [ testCase, solution ] of testCases) {
54
+ for (const [testCase, solution] of testCases) {
29
55
  const foundSolutions = solveQuadratic(...testCase);
30
56
  for (let i = 0; i < 2; i++) {
31
57
  if (isNaN(solution[i]) && isNaN(foundSolutions[i])) {
@@ -36,4 +62,4 @@ describe('solveQuadratic', () => {
36
62
  }
37
63
  }
38
64
  });
39
- });
65
+ });
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Solves an equation of the form ax² + bx + c = 0.
4
3
  * The larger solution is returned first.
@@ -21,13 +20,13 @@ const solveQuadratic = (a: number, b: number, c: number): [number, number] => {
21
20
  solution = -c / b;
22
21
  }
23
22
 
24
- return [ solution, solution ];
23
+ return [solution, solution];
25
24
  }
26
25
 
27
26
  const discriminant = b * b - 4 * a * c;
28
27
 
29
28
  if (discriminant < 0) {
30
- return [ NaN, NaN ];
29
+ return [NaN, NaN];
31
30
  }
32
31
 
33
32
  const rootDiscriminant = Math.sqrt(discriminant);
@@ -35,9 +34,9 @@ const solveQuadratic = (a: number, b: number, c: number): [number, number] => {
35
34
  const solution2 = (-b - rootDiscriminant) / (2 * a);
36
35
 
37
36
  if (solution1 > solution2) {
38
- return [ solution1, solution2 ];
37
+ return [solution1, solution2];
39
38
  } else {
40
- return [ solution2, solution1 ];
39
+ return [solution2, solution1];
41
40
  }
42
41
  };
43
- export default solveQuadratic;
42
+ export default solveQuadratic;
@@ -12,4 +12,4 @@ it('cleanUpNumber', () => {
12
12
  expect(cleanUpNumber('1234.5')).toBe('1234.5');
13
13
  expect(cleanUpNumber('1234.500')).toBe('1234.5');
14
14
  expect(cleanUpNumber('1.1368683772161603e-13')).toBe('0');
15
- });
15
+ });
@@ -1,3 +1 @@
1
-
2
-
3
- export const numberRegex = /^([-]?)(\d*)[.](\d+)$/;
1
+ export const numberRegex = /^([-]?)(\d*)[.](\d+)$/;
@@ -1,6 +1,5 @@
1
1
  import { numberRegex } from './constants';
2
2
 
3
-
4
3
  /**
5
4
  * Returns the length of `numberAsString` after a decimal point.
6
5
  *
@@ -16,7 +15,7 @@ export const getLenAfterDecimal = (numberAsString: string) => {
16
15
  // like NaN or Infinity)
17
16
  if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
18
17
  return -1;
19
- // Or it has no decimal point
18
+ // Or it has no decimal point
20
19
  } else {
21
20
  return 0;
22
21
  }
@@ -1,2 +1 @@
1
-
2
- export { toRoundedString } from './toRoundedString';
1
+ export { toRoundedString } from './toRoundedString';
@@ -29,4 +29,4 @@ describe('toRoundedString', () => {
29
29
  expect(toRoundedString(-10.123499)).toBe('-10.123499');
30
30
  expect(toRoundedString(0.00123499)).toBe('.00123499');
31
31
  });
32
- });
32
+ });
@@ -1,6 +1,5 @@
1
1
  import { toStringOfSamePrecision } from './toStringOfSamePrecision';
2
2
 
3
-
4
3
  it('toStringOfSamePrecision', () => {
5
4
  expect(toStringOfSamePrecision(1.23456, '1.12')).toBe('1.23');
6
5
  expect(toStringOfSamePrecision(1.23456, '1.120')).toBe('1.235');
@@ -18,4 +17,4 @@ it('toStringOfSamePrecision', () => {
18
17
  expect(toStringOfSamePrecision(-0.9999999999999432, '291.3')).toBe('-1');
19
18
  expect(toStringOfSamePrecision(9998.9, '.1', '-11')).toBe('9998.9');
20
19
  expect(toStringOfSamePrecision(-14.20000000000002, '.000001', '-11')).toBe('-14.2');
21
- });
20
+ });
@@ -60,4 +60,4 @@ export const toStringOfSamePrecision = (num: number, ...references: string[]): s
60
60
  return cleanUpNumber(`${negativeSign}${preDecimal}.${postDecimal}`);
61
61
  };
62
62
 
63
- export default toStringOfSamePrecision;
63
+ export default toStringOfSamePrecision;
@@ -14,11 +14,9 @@ import Parameterized2DShape from './Parameterized2DShape';
14
14
  * @internal
15
15
  */
16
16
  export abstract class BezierJSWrapper extends Parameterized2DShape {
17
- #bezierJs: Bezier|null = null;
17
+ #bezierJs: Bezier | null = null;
18
18
 
19
- protected constructor(
20
- bezierJsBezier?: Bezier
21
- ) {
19
+ protected constructor(bezierJsBezier?: Bezier) {
22
20
  super();
23
21
 
24
22
  if (bezierJsBezier) {
@@ -31,7 +29,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
31
29
 
32
30
  protected getBezier() {
33
31
  if (!this.#bezierJs) {
34
- this.#bezierJs = new Bezier(this.getPoints().map(p => p.xy));
32
+ this.#bezierJs = new Bezier(this.getPoints().map((p) => p.xy));
35
33
  }
36
34
  return this.#bezierJs;
37
35
  }
@@ -58,6 +56,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
58
56
  return Vec2.ofXY(this.getBezier().get(t));
59
57
  }
60
58
 
59
+ /** @returns the curve's directional derivative at `t`. */
61
60
  public derivativeAt(t: number): Point2 {
62
61
  return Vec2.ofXY(this.getBezier().derivative(t));
63
62
  }
@@ -66,6 +65,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
66
65
  return Vec2.ofXY((this.getBezier() as any).dderivative(t));
67
66
  }
68
67
 
68
+ /** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
69
69
  public normal(t: number): Vec2 {
70
70
  return Vec2.ofXY(this.getBezier().normal(t));
71
71
  }
@@ -96,41 +96,49 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
96
96
  const asLine = LineSegment2.ofSmallestContainingPoints(this.getPoints());
97
97
  if (asLine) {
98
98
  const intersection = asLine.intersectsLineSegment(line);
99
- return intersection.map(p => this.nearestPointTo(p).parameterValue);
99
+ return intersection.map((p) => this.nearestPointTo(p).parameterValue);
100
100
  }
101
101
 
102
102
  const bezier = this.getBezier();
103
103
 
104
- return bezier.intersects(line).map(t => {
105
- // We're using the .intersects(line) function, which is documented
106
- // to always return numbers. However, to satisfy the type checker (and
107
- // possibly improperly-defined types),
108
- if (typeof t === 'string') {
109
- t = parseFloat(t);
110
- }
111
-
112
- const point = Vec2.ofXY(this.at(t));
113
-
114
- // Ensure that the intersection is on the line segment
115
- if (point.distanceTo(line.p1) > line.length
116
- || point.distanceTo(line.p2) > line.length) {
117
- return null;
118
- }
119
-
120
- return t;
121
- }).filter(entry => entry !== null);
104
+ return bezier
105
+ .intersects(line)
106
+ .map((t) => {
107
+ // We're using the .intersects(line) function, which is documented
108
+ // to always return numbers. However, to satisfy the type checker (and
109
+ // possibly improperly-defined types),
110
+ if (typeof t === 'string') {
111
+ t = parseFloat(t);
112
+ }
113
+
114
+ const point = Vec2.ofXY(this.at(t));
115
+
116
+ // Ensure that the intersection is on the line segment
117
+ if (point.distanceTo(line.p1) > line.length || point.distanceTo(line.p2) > line.length) {
118
+ return null;
119
+ }
120
+
121
+ return t;
122
+ })
123
+ .filter((entry) => entry !== null);
122
124
  }
123
125
 
124
126
  public override splitAt(t: number): [BezierJSWrapper] | [BezierJSWrapper, BezierJSWrapper] {
125
127
  if (t <= 0 || t >= 1) {
126
- return [ this ];
128
+ return [this];
127
129
  }
128
130
 
129
131
  const bezier = this.getBezier();
130
132
  const split = bezier.split(t);
131
133
  return [
132
- new BezierJSWrapperImpl(split.left.points.map(point => Vec2.ofXY(point)), split.left),
133
- new BezierJSWrapperImpl(split.right.points.map(point => Vec2.ofXY(point)), split.right),
134
+ new BezierJSWrapperImpl(
135
+ split.left.points.map((point) => Vec2.ofXY(point)),
136
+ split.left,
137
+ ),
138
+ new BezierJSWrapperImpl(
139
+ split.right.points.map((point) => Vec2.ofXY(point)),
140
+ split.right,
141
+ ),
134
142
  ];
135
143
  }
136
144
 
@@ -162,7 +170,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
162
170
 
163
171
  // Start by testing a few points:
164
172
  const pointsToTest = 4;
165
- for (let i = 0; i < pointsToTest; i ++) {
173
+ for (let i = 0; i < pointsToTest; i++) {
166
174
  const testT = i / (pointsToTest - 1);
167
175
  const testMinSqrDist = sqrDistAt(testT);
168
176
 
@@ -181,8 +189,12 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
181
189
  const bPrime = this.derivativeAt(t);
182
190
  const bPrimePrime = this.secondDerivativeAt(t);
183
191
  return (
184
- 2 * bPrime.x * bPrime.x + 2 * b.x * bPrimePrime.x - 2 * point.x * bPrimePrime.x
185
- + 2 * bPrime.y * bPrime.y + 2 * b.y * bPrimePrime.y - 2 * point.y * bPrimePrime.y
192
+ 2 * bPrime.x * bPrime.x +
193
+ 2 * b.x * bPrimePrime.x -
194
+ 2 * point.x * bPrimePrime.x +
195
+ 2 * bPrime.y * bPrime.y +
196
+ 2 * b.y * bPrimePrime.y -
197
+ 2 * point.y * bPrimePrime.y
186
198
  );
187
199
  };
188
200
  // Because we're zeroing f'(t), we also need to be able to compute it:
@@ -191,8 +203,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
191
203
  const b = this.at(t);
192
204
  const bPrime = this.derivativeAt(t);
193
205
  return (
194
- 2 * b.x * bPrime.x - 2 * point.x * bPrime.x
195
- + 2 * b.y * bPrime.y - 2 * point.y * bPrime.y
206
+ 2 * b.x * bPrime.x - 2 * point.x * bPrime.x + 2 * b.y * bPrime.y - 2 * point.y * bPrime.y
196
207
  );
197
208
  };
198
209
 
@@ -226,7 +237,10 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
226
237
  }
227
238
 
228
239
  public intersectsBezier(other: BezierJSWrapper) {
229
- const intersections = this.getBezier().intersects(other.getBezier()) as (string[] | null | undefined);
240
+ const intersections = this.getBezier().intersects(other.getBezier()) as
241
+ | string[]
242
+ | null
243
+ | undefined;
230
244
  if (!intersections || intersections.length === 0) {
231
245
  return [];
232
246
  }
@@ -239,7 +253,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
239
253
 
240
254
  if (!match) {
241
255
  throw new Error(
242
- `Incorrect format returned by .intersects: ${intersections} should be array of "number/number"!`
256
+ `Incorrect format returned by .intersects: ${intersections} should be array of "number/number"!`,
243
257
  );
244
258
  }
245
259
 
@@ -253,7 +267,9 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
253
267
  }
254
268
 
255
269
  public override toString() {
256
- return `Bézier(${this.getPoints().map(point => point.toString()).join(', ')})`;
270
+ return `Bézier(${this.getPoints()
271
+ .map((point) => point.toString())
272
+ .join(', ')})`;
257
273
  }
258
274
  }
259
275
 
@@ -262,7 +278,10 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
262
278
  * around a `Bezier`.
263
279
  */
264
280
  class BezierJSWrapperImpl extends BezierJSWrapper {
265
- public constructor(private controlPoints: readonly Point2[], curve?: Bezier) {
281
+ public constructor(
282
+ private controlPoints: readonly Point2[],
283
+ curve?: Bezier,
284
+ ) {
266
285
  super(curve);
267
286
  }
268
287
 
@@ -271,4 +290,4 @@ class BezierJSWrapperImpl extends BezierJSWrapper {
271
290
  }
272
291
  }
273
292
 
274
- export default BezierJSWrapper;
293
+ export default BezierJSWrapper;
@@ -23,13 +23,13 @@ class CubicBezier extends BezierJSWrapper {
23
23
  }
24
24
 
25
25
  public override getPoints() {
26
- return [ this.p0, this.p1, this.p2, this.p3 ];
26
+ return [this.p0, this.p1, this.p2, this.p3];
27
27
  }
28
28
 
29
29
  /** Returns an overestimate of this shape's bounding box. */
30
30
  public override getLooseBoundingBox(): Rect2 {
31
- return Rect2.bboxOf([ this.p0, this.p1, this.p2, this.p3 ]);
31
+ return Rect2.bboxOf([this.p0, this.p1, this.p2, this.p3]);
32
32
  }
33
33
  }
34
34
 
35
- export default CubicBezier;
35
+ export default CubicBezier;
@@ -2,7 +2,6 @@ import LineSegment2 from './LineSegment2';
2
2
  import { Vec2 } from '../Vec2';
3
3
  import Mat33 from '../Mat33';
4
4
 
5
-
6
5
  describe('Line2', () => {
7
6
  it('x and y axes should intersect at (0, 0)', () => {
8
7
  const xAxis = new LineSegment2(Vec2.of(-10, 0), Vec2.of(10, 0));
@@ -65,7 +64,7 @@ describe('Line2', () => {
65
64
  // Points taken from issue observed directly in editor
66
65
  const p1 = Vec2.of(769.6126045442547, 221.037877485765);
67
66
  const p2 = Vec2.of(770.3873954557453, 224.962122514235);
68
- const p3 = Vec2.of( 763.3590010920082, 223.66723995850086);
67
+ const p3 = Vec2.of(763.3590010920082, 223.66723995850086);
69
68
  const p4 = Vec2.of(763.5494167642871, 223.66723995850086);
70
69
 
71
70
  const line1 = new LineSegment2(p1, p2);
@@ -115,7 +114,7 @@ describe('Line2', () => {
115
114
  // Halving
116
115
  //
117
116
  expect(lineSegment.at(0.5)).objEq(midpoint);
118
- const [ firstHalf, secondHalf ] = lineSegment.splitAt(0.5);
117
+ const [firstHalf, secondHalf] = lineSegment.splitAt(0.5);
119
118
 
120
119
  if (!secondHalf) {
121
120
  throw new Error('Splitting a line segment in half should yield two line segments.');
@@ -136,24 +135,32 @@ describe('Line2', () => {
136
135
  it('equivalence check should allow ignoring direction', () => {
137
136
  expect(new LineSegment2(Vec2.zero, Vec2.unitX)).objEq(new LineSegment2(Vec2.zero, Vec2.unitX));
138
137
  expect(new LineSegment2(Vec2.zero, Vec2.unitX)).objEq(new LineSegment2(Vec2.unitX, Vec2.zero));
139
- expect(new LineSegment2(Vec2.zero, Vec2.unitX)).not.objEq(new LineSegment2(Vec2.unitX, Vec2.zero), { ignoreDirection: false });
138
+ expect(new LineSegment2(Vec2.zero, Vec2.unitX)).not.objEq(
139
+ new LineSegment2(Vec2.unitX, Vec2.zero),
140
+ { ignoreDirection: false },
141
+ );
140
142
  });
141
143
 
142
144
  it('should support creating from a collection of points', () => {
143
145
  expect(LineSegment2.ofSmallestContainingPoints([])).toBeNull();
144
146
  expect(LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 1)])).toBeNull();
145
- expect(LineSegment2.ofSmallestContainingPoints(
146
- [Vec2.of(1, 1), Vec2.of(1, 2), Vec2.of(3, 3)]
147
- )).toBeNull();
148
-
149
- expect(LineSegment2.ofSmallestContainingPoints(
150
- [Vec2.of(1, 1), Vec2.of(1, 2)]
151
- )).objEq(new LineSegment2(Vec2.of(1, 1), Vec2.of(1, 2)));
152
- expect(LineSegment2.ofSmallestContainingPoints(
153
- [Vec2.of(1, 1), Vec2.of(2, 2), Vec2.of(3, 3)]
154
- )).objEq(new LineSegment2(Vec2.of(1, 1), Vec2.of(3, 3)));
155
- expect(LineSegment2.ofSmallestContainingPoints(
156
- [Vec2.of(3, 3), Vec2.of(2, 2), Vec2.of(2.4, 2.4), Vec2.of(3, 3)]
157
- )).objEq(new LineSegment2(Vec2.of(2, 2), Vec2.of(3, 3)));
147
+ expect(
148
+ LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 1), Vec2.of(1, 2), Vec2.of(3, 3)]),
149
+ ).toBeNull();
150
+
151
+ expect(LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 1), Vec2.of(1, 2)])).objEq(
152
+ new LineSegment2(Vec2.of(1, 1), Vec2.of(1, 2)),
153
+ );
154
+ expect(
155
+ LineSegment2.ofSmallestContainingPoints([Vec2.of(1, 1), Vec2.of(2, 2), Vec2.of(3, 3)]),
156
+ ).objEq(new LineSegment2(Vec2.of(1, 1), Vec2.of(3, 3)));
157
+ expect(
158
+ LineSegment2.ofSmallestContainingPoints([
159
+ Vec2.of(3, 3),
160
+ Vec2.of(2, 2),
161
+ Vec2.of(2.4, 2.4),
162
+ Vec2.of(3, 3),
163
+ ]),
164
+ ).objEq(new LineSegment2(Vec2.of(2, 2), Vec2.of(3, 3)));
158
165
  });
159
166
  });
@@ -42,7 +42,7 @@ export class LineSegment2 extends Parameterized2DShape {
42
42
  /** Creates a new `LineSegment2` from its endpoints. */
43
43
  public constructor(
44
44
  private readonly point1: Point2,
45
- private readonly point2: Point2
45
+ private readonly point2: Point2,
46
46
  ) {
47
47
  super();
48
48
 
@@ -70,7 +70,7 @@ export class LineSegment2 extends Parameterized2DShape {
70
70
  public static ofSmallestContainingPoints(points: readonly Point2[]) {
71
71
  if (points.length <= 1) return null;
72
72
 
73
- const sorted = [...points].sort((a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y);
73
+ const sorted = [...points].sort((a, b) => (a.x !== b.x ? a.x - b.x : a.y - b.y));
74
74
  const line = new LineSegment2(sorted[0], sorted[sorted.length - 1]);
75
75
 
76
76
  for (const point of sorted) {
@@ -127,15 +127,12 @@ export class LineSegment2 extends Parameterized2DShape {
127
127
  return this.direction;
128
128
  }
129
129
 
130
- public splitAt(t: number): [LineSegment2]|[LineSegment2,LineSegment2] {
130
+ public splitAt(t: number): [LineSegment2] | [LineSegment2, LineSegment2] {
131
131
  if (t <= 0 || t >= 1) {
132
132
  return [this];
133
133
  }
134
134
 
135
- return [
136
- new LineSegment2(this.point1, this.at(t)),
137
- new LineSegment2(this.at(t), this.point2),
138
- ];
135
+ return [new LineSegment2(this.point1, this.at(t)), new LineSegment2(this.at(t), this.point2)];
139
136
  }
140
137
 
141
138
  /**
@@ -146,7 +143,7 @@ export class LineSegment2 extends Parameterized2DShape {
146
143
  * This will change in a future release.
147
144
  * @deprecated
148
145
  */
149
- public intersection(other: LineSegment2): IntersectionResult|null {
146
+ public intersection(other: LineSegment2): IntersectionResult | null {
150
147
  // TODO(v2.0.0): Make this return a `t` value from `0` to `1`.
151
148
 
152
149
  // We want x₁(t) = x₂(t) and y₁(t) = y₂(t)
@@ -191,21 +188,18 @@ export class LineSegment2 extends Parameterized2DShape {
191
188
 
192
189
  const xIntersect = this.point1.x;
193
190
  const yIntersect =
194
- (this.point1.x - other.point1.x) * other.direction.y / other.direction.x + other.point1.y;
191
+ ((this.point1.x - other.point1.x) * other.direction.y) / other.direction.x + other.point1.y;
195
192
  resultPoint = Vec2.of(xIntersect, yIntersect);
196
193
  resultT = (yIntersect - this.point1.y) / this.direction.y;
197
194
  } else {
198
195
  // From above,
199
196
  // x = ((o₁ᵧ - o₂ᵧ)(d₁ₓd₂ₓ) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
200
- const numerator = (
201
- (this.point1.y - other.point1.y) * this.direction.x * other.direction.x
202
- + this.direction.x * other.direction.y * other.point1.x
203
- - this.direction.y * other.direction.x * this.point1.x
204
- );
205
- const denominator = (
206
- other.direction.y * this.direction.x
207
- - this.direction.y * other.direction.x
208
- );
197
+ const numerator =
198
+ (this.point1.y - other.point1.y) * this.direction.x * other.direction.x +
199
+ this.direction.x * other.direction.y * other.point1.x -
200
+ this.direction.y * other.direction.x * this.point1.x;
201
+ const denominator =
202
+ other.direction.y * this.direction.x - this.direction.y * other.direction.x;
209
203
 
210
204
  // Avoid dividing by zero. It means there is no intersection
211
205
  if (denominator === 0) {
@@ -225,10 +219,12 @@ export class LineSegment2 extends Parameterized2DShape {
225
219
  const resultToP3 = resultPoint.distanceTo(other.point1);
226
220
  const resultToP4 = resultPoint.distanceTo(other.point2);
227
221
 
228
- if (resultToP1 > this.length
229
- || resultToP2 > this.length
230
- || resultToP3 > other.length
231
- || resultToP4 > other.length) {
222
+ if (
223
+ resultToP1 > this.length ||
224
+ resultToP2 > this.length ||
225
+ resultToP3 > other.length ||
226
+ resultToP4 > other.length
227
+ ) {
232
228
  return null;
233
229
  }
234
230
 
@@ -246,7 +242,7 @@ export class LineSegment2 extends Parameterized2DShape {
246
242
  const intersection = this.intersection(lineSegment);
247
243
 
248
244
  if (intersection) {
249
- return [ intersection.t / this.length ];
245
+ return [intersection.t / this.length];
250
246
  }
251
247
  return [];
252
248
  }
@@ -263,7 +259,7 @@ export class LineSegment2 extends Parameterized2DShape {
263
259
  const intersection = this.intersection(lineSegment);
264
260
 
265
261
  if (intersection) {
266
- return [ intersection.point ];
262
+ return [intersection.point];
267
263
  }
268
264
  return [];
269
265
  }
@@ -273,7 +269,7 @@ export class LineSegment2 extends Parameterized2DShape {
273
269
  return this.nearestPointTo(target).point;
274
270
  }
275
271
 
276
- public override nearestPointTo(target: Vec3): { point: Vec3; parameterValue: number; } {
272
+ public override nearestPointTo(target: Vec3): { point: Vec3; parameterValue: number } {
277
273
  // Distance from P1 along this' direction.
278
274
  const projectedDistFromP1 = target.minus(this.p1).dot(this.direction);
279
275
  const projectedDistFromP2 = this.length - projectedDistFromP1;
@@ -304,7 +300,8 @@ export class LineSegment2 extends Parameterized2DShape {
304
300
  /** Returns a copy of this line segment transformed by the given `affineTransfm`. */
305
301
  public transformedBy(affineTransfm: Mat33): LineSegment2 {
306
302
  return new LineSegment2(
307
- affineTransfm.transformVec2(this.p1), affineTransfm.transformVec2(this.p2)
303
+ affineTransfm.transformVec2(this.p1),
304
+ affineTransfm.transformVec2(this.p2),
308
305
  );
309
306
  }
310
307
 
@@ -324,7 +321,7 @@ export class LineSegment2 extends Parameterized2DShape {
324
321
  * - `tolerance`: The maximum difference between endpoints. (Default: 0)
325
322
  * - `ignoreDirection`: Allow matching a version of `this` with opposite direction. (Default: `true`)
326
323
  */
327
- public eq(other: LineSegment2, options?: { tolerance?: number, ignoreDirection?: boolean }) {
324
+ public eq(other: LineSegment2, options?: { tolerance?: number; ignoreDirection?: boolean }) {
328
325
  if (!(other instanceof LineSegment2)) {
329
326
  return false;
330
327
  }
@@ -333,8 +330,8 @@ export class LineSegment2 extends Parameterized2DShape {
333
330
  const ignoreDirection = options?.ignoreDirection ?? true;
334
331
 
335
332
  return (
336
- (other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance))
337
- || (ignoreDirection && other.p1.eq(this.p2, tolerance) && other.p2.eq(this.p1, tolerance))
333
+ (other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance)) ||
334
+ (ignoreDirection && other.p1.eq(this.p2, tolerance) && other.p2.eq(this.p1, tolerance))
338
335
  );
339
336
  }
340
337
  }
@@ -20,13 +20,15 @@ export abstract class Parameterized2DShape extends Abstract2DShape {
20
20
  /**
21
21
  * Divides this shape into two separate shapes at parameter value $t$.
22
22
  */
23
- abstract splitAt(t: number): [ Parameterized2DShape ] | [ Parameterized2DShape, Parameterized2DShape ];
23
+ abstract splitAt(
24
+ t: number,
25
+ ): [Parameterized2DShape] | [Parameterized2DShape, Parameterized2DShape];
24
26
 
25
27
  /**
26
28
  * Returns the nearest point on `this` to `point` and the `parameterValue` at which
27
29
  * that point occurs.
28
30
  */
29
- abstract nearestPointTo(point: Point2): { point: Point2, parameterValue: number };
31
+ abstract nearestPointTo(point: Point2): { point: Point2; parameterValue: number };
30
32
 
31
33
  /**
32
34
  * Returns the **parameter values** at which `lineSegment` intersects this shape.
@@ -35,9 +37,8 @@ export abstract class Parameterized2DShape extends Abstract2DShape {
35
37
  */
36
38
  public abstract argIntersectsLineSegment(lineSegment: LineSegment2): number[];
37
39
 
38
-
39
40
  public override intersectsLineSegment(line: LineSegment2): Point2[] {
40
- return this.argIntersectsLineSegment(line).map(t => this.at(t));
41
+ return this.argIntersectsLineSegment(line).map((t) => this.at(t));
41
42
  }
42
43
  }
43
44
 
@@ -27,7 +27,7 @@ describe('Path.fromString', () => {
27
27
  },
28
28
  {
29
29
  kind: PathCommandType.MoveTo,
30
- point: Vec2.of(3,3),
30
+ point: Vec2.of(3, 3),
31
31
  },
32
32
  ]);
33
33
  expect(path3.startPoint).toMatchObject(Vec2.of(1, 1));
@@ -71,7 +71,7 @@ describe('Path.fromString', () => {
71
71
  const path1 = Path.fromString('m3,3 l1,2 l1,1 z');
72
72
  const path2 = Path.fromString('m3,3 l1,2 l1,1 Z');
73
73
 
74
- expect(path1.startPoint).toMatchObject(Vec2.of(3,3));
74
+ expect(path1.startPoint).toMatchObject(Vec2.of(3, 3));
75
75
  expect(path2.startPoint).toMatchObject(path1.startPoint);
76
76
  expect(path1.parts).toMatchObject(path2.parts);
77
77
  expect(path1.parts).toMatchObject([
@@ -86,7 +86,7 @@ describe('Path.fromString', () => {
86
86
  {
87
87
  kind: PathCommandType.LineTo,
88
88
  point: path1.startPoint,
89
- }
89
+ },
90
90
  ]);
91
91
  });
92
92
 
@@ -128,7 +128,7 @@ describe('Path.fromString', () => {
128
128
  controlPoint1: Vec2.of(1, 1),
129
129
  controlPoint2: Vec2.of(0.1, 0.1),
130
130
  endPoint: Vec2.zero,
131
- }
131
+ },
132
132
  ]);
133
133
  });
134
134
 
@@ -220,4 +220,4 @@ describe('Path.fromString', () => {
220
220
  ]);
221
221
  expect(path.startPoint).toMatchObject(Vec2.of(5, 10));
222
222
  });
223
- });
223
+ });