@immugio/three-math-extensions 0.3.4 → 0.3.7

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 (45) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/cjs/Line2D.js +90 -13
  3. package/cjs/Polygon.js +3 -0
  4. package/docs/classes/BoundingBox.md +121 -121
  5. package/docs/classes/Line2D.md +1366 -1366
  6. package/docs/classes/Line3D.md +831 -831
  7. package/docs/classes/Polygon.md +297 -297
  8. package/docs/classes/Rectangle.md +291 -291
  9. package/docs/classes/Size2.md +55 -55
  10. package/docs/classes/Vec2.md +282 -282
  11. package/docs/classes/Vec3.md +338 -338
  12. package/docs/interfaces/Point2.md +30 -30
  13. package/docs/interfaces/Point3.md +41 -41
  14. package/docs/modules.md +209 -209
  15. package/eslint.config.mjs +111 -111
  16. package/esm/Line2D.js +90 -13
  17. package/esm/Polygon.js +3 -0
  18. package/package.json +62 -62
  19. package/src/BoundingBox.ts +13 -13
  20. package/src/Line2D.ts +951 -857
  21. package/src/Line3D.ts +586 -586
  22. package/src/MathConstants.ts +1 -1
  23. package/src/Point2.ts +3 -3
  24. package/src/Point3.ts +4 -4
  25. package/src/Polygon.ts +290 -286
  26. package/src/Rectangle.ts +92 -92
  27. package/src/Size2.ts +3 -3
  28. package/src/Vec2.ts +124 -124
  29. package/src/Vec3.ts +167 -167
  30. package/src/containsPoint.ts +65 -65
  31. package/src/directions.ts +9 -9
  32. package/src/directions2d.ts +7 -7
  33. package/src/ensurePolygonClockwise.ts +9 -9
  34. package/src/extendOrTrimPolylinesAtIntersections.ts +10 -10
  35. package/src/getPolygonArea.ts +21 -21
  36. package/src/index.ts +24 -24
  37. package/src/isContinuousClosedShape.ts +24 -24
  38. package/src/isPointInPolygon.ts +23 -23
  39. package/src/isPolygonClockwise.ts +15 -15
  40. package/src/normalizeAngleDegrees.ts +6 -6
  41. package/src/normalizeAngleRadians.ts +14 -14
  42. package/src/offsetPolyline.ts +26 -26
  43. package/src/polygonPerimeter.ts +13 -13
  44. package/src/sortLinesByConnections.ts +45 -45
  45. package/types/Line2D.d.ts +12 -5
package/CHANGELOG.md CHANGED
@@ -7,7 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
9
9
 
10
- ## [0.3.4](https://github.com/Immugio/three-math-extensions/compare/0.3.3...0.3.4)
10
+ ## [0.3.7](https://github.com/Immugio/three-math-extensions/compare/0.3.6...0.3.7)
11
+
12
+ ### Commits
13
+
14
+ - Polygon - Account for empty contour when calculating bounding box [`56bb6a9`](https://github.com/Immugio/three-math-extensions/commit/56bb6a9577eea00b756779aa2a78688b3bb1f2b8)
15
+
16
+ ## [0.3.6](https://github.com/Immugio/three-math-extensions/compare/0.3.5...0.3.6) - 2026-01-13
17
+
18
+ ### Commits
19
+
20
+ - Update the publish script to work with trusted providers. [`b6ccb15`](https://github.com/Immugio/three-math-extensions/commit/b6ccb1545c6190495fbcf14f97c06895a3de3670)
21
+
22
+ ## [0.3.5](https://github.com/Immugio/three-math-extensions/compare/0.3.4...0.3.5) - 2026-01-13
23
+
24
+ ### Merged
25
+
26
+ - Line2D - Joining lines should support tolerance [`#6`](https://github.com/Immugio/three-math-extensions/pull/6)
27
+
28
+ ### Commits
29
+
30
+ - Line2D - Improve unit test descriptions [`be66f7f`](https://github.com/Immugio/three-math-extensions/commit/be66f7f6113c5c3c7f7bf0e6c99afbc7b2bcfc05)
31
+
32
+ ## [0.3.4](https://github.com/Immugio/three-math-extensions/compare/0.3.3...0.3.4) - 2025-11-20
11
33
 
12
34
  ### Commits
13
35
 
package/cjs/Line2D.js CHANGED
@@ -264,14 +264,63 @@ class Line2D {
264
264
  }
265
265
  /**
266
266
  * Returns true if other line is collinear and overlaps or at least touching this line.
267
+ * Uses tolerances to allow for small gaps and angle differences.
267
268
  * @param other
268
- */
269
- isCollinearWithTouchOrOverlap(other) {
270
- if (!this.isPointOnInfiniteLine(other.start) || !this.isPointOnInfiniteLine(other.end)) {
269
+ * @param distanceTolerance Maximum distance between lines or points to be considered touching/overlapping
270
+ * @param parallelTolerance Maximum angle difference in radians for lines to be considered parallel
271
+ */
272
+ isCollinearWithTouchOrOverlap(other, distanceTolerance = 0, parallelTolerance = 0) {
273
+ // Exact logic (no tolerances)
274
+ if (!distanceTolerance && !parallelTolerance) {
275
+ if (!this.isPointOnInfiniteLine(other.start) || !this.isPointOnInfiniteLine(other.end)) {
276
+ return false;
277
+ }
278
+ return this.isPointOnLineSection(other.start) || this.isPointOnLineSection(other.end) ||
279
+ other.isPointOnLineSection(this.start) || other.isPointOnLineSection(this.end);
280
+ }
281
+ // If tolerances are provided, use tolerance-aware logic
282
+ // Check if lines are parallel
283
+ if (!this.isParallelTo(other, parallelTolerance)) {
284
+ return false;
285
+ }
286
+ // Check if points are close enough to the infinite line
287
+ const otherStartDistance = this.distanceToPointOnInfiniteLine(other.start);
288
+ const otherEndDistance = this.distanceToPointOnInfiniteLine(other.end);
289
+ const thisStartDistance = other.distanceToPointOnInfiniteLine(this.start);
290
+ const thisEndDistance = other.distanceToPointOnInfiniteLine(this.end);
291
+ if (otherStartDistance > distanceTolerance || otherEndDistance > distanceTolerance ||
292
+ thisStartDistance > distanceTolerance || thisEndDistance > distanceTolerance) {
271
293
  return false;
272
294
  }
273
- return this.isPointOnLineSection(other.start) || this.isPointOnLineSection(other.end) ||
274
- other.isPointOnLineSection(this.start) || other.isPointOnLineSection(this.end);
295
+ // Check if any endpoint is close to and beside the other line section
296
+ // OR if the lines are close enough to each other (for gap handling)
297
+ const hasOverlap = this.isPointCloseToAndBesideLineSection(other.start, distanceTolerance) ||
298
+ this.isPointCloseToAndBesideLineSection(other.end, distanceTolerance) ||
299
+ other.isPointCloseToAndBesideLineSection(this.start, distanceTolerance) ||
300
+ other.isPointCloseToAndBesideLineSection(this.end, distanceTolerance);
301
+ if (hasOverlap) {
302
+ return true;
303
+ }
304
+ // Check if lines are close enough to bridge a gap
305
+ // Project all endpoints onto the line's direction and check if projections are within tolerance
306
+ const direction = this.direction;
307
+ const startPoint = this.start;
308
+ // Project all points onto the line's direction vector
309
+ const projectPoint = (p) => {
310
+ const toPoint = new Vec2_1.Vec2().subVectors(p, startPoint);
311
+ return toPoint.dot(direction);
312
+ };
313
+ const thisStartProj = projectPoint(this.start);
314
+ const thisEndProj = projectPoint(this.end);
315
+ const otherStartProj = projectPoint(other.start);
316
+ const otherEndProj = projectPoint(other.end);
317
+ const minThis = Math.min(thisStartProj, thisEndProj);
318
+ const maxThis = Math.max(thisStartProj, thisEndProj);
319
+ const minOther = Math.min(otherStartProj, otherEndProj);
320
+ const maxOther = Math.max(otherStartProj, otherEndProj);
321
+ // Check if projections overlap or are within tolerance
322
+ const gap = Math.max(minThis, minOther) - Math.min(maxThis, maxOther);
323
+ return gap <= distanceTolerance;
275
324
  }
276
325
  /**
277
326
  * Returns true if there is any overlap between this line and the @other line section.
@@ -329,27 +378,55 @@ class Line2D {
329
378
  }
330
379
  /**
331
380
  * Joins a copy of @line with the @other line.
332
- * Other must be parallel to this line.
381
+ * Other must be parallel to this line (within tolerance).
333
382
  * Returns null if there is no overlap
334
383
  * Clones the line, does not modify.
335
384
  * @param line
336
385
  * @param other
386
+ * @param distanceTolerance Maximum distance between lines or points to be considered touching/overlapping
387
+ * @param parallelTolerance Maximum angle difference in radians for lines to be considered parallel
337
388
  */
338
- static joinLine(line, other) {
339
- if (!line.isCollinearWithTouchOrOverlap(other)) {
389
+ static joinLine(line, other, distanceTolerance = 0, parallelTolerance = 0) {
390
+ if (!line.isCollinearWithTouchOrOverlap(other, distanceTolerance, parallelTolerance)) {
340
391
  return null;
341
392
  }
342
- const p1 = !line.isPointOnLineSection(other.start) ? other.start : line.start;
343
- const p2 = !line.isPointOnLineSection(other.end) ? other.end : line.end;
393
+ // When using tolerances, we need to check if points are close enough to the line section
394
+ const useTolerance = distanceTolerance > 0 || parallelTolerance > 0;
395
+ if (!useTolerance) {
396
+ const p1 = !line.isPointOnLineSection(other.start) ? other.start : line.start;
397
+ const p2 = !line.isPointOnLineSection(other.end) ? other.end : line.end;
398
+ return new Line2D(p1.clone(), p2.clone(), line.index);
399
+ }
400
+ // Determine the endpoints of the joined line
401
+ // We want the outermost points that encompass both lines
402
+ const points = [line.start, line.end, other.start, other.end];
403
+ // Project all points onto the line's direction to find min/max
404
+ const direction = line.direction;
405
+ const startPoint = line.start;
406
+ // Project each point onto the line's direction vector
407
+ const projections = points.map(p => {
408
+ const toPoint = new Vec2_1.Vec2().subVectors(p, startPoint);
409
+ const t = toPoint.dot(direction);
410
+ return { point: p, t };
411
+ });
412
+ // Find the points with min and max projections
413
+ const minProj = projections.reduce((min, curr) => curr.t < min.t ? curr : min);
414
+ const maxProj = projections.reduce((max, curr) => curr.t > max.t ? curr : max);
415
+ const p1 = minProj.point;
416
+ const p2 = maxProj.point;
344
417
  return new Line2D(p1.clone(), p2.clone(), line.index);
345
418
  }
346
419
  /**
347
420
  * Joins provided lines into several joined lines.
348
- * Lines must be parallel for joining.
421
+ * Lines must be parallel for joining (within tolerance).
349
422
  * Clone the lines, does not modify.
350
423
  * @param lines
424
+ * @param distanceTolerance Maximum distance between lines or points to be considered touching/overlapping
425
+ * @param parallelTolerance Maximum angle difference in radians for lines to be considered parallel
351
426
  */
352
- static joinLines(lines) {
427
+ static joinLines(lines, distanceTolerance, parallelTolerance) {
428
+ const distTol = distanceTolerance ?? 0;
429
+ const parTol = parallelTolerance ?? 0;
353
430
  if (lines.length < 2) {
354
431
  return lines.map(x => x.clone());
355
432
  }
@@ -362,7 +439,7 @@ class Line2D {
362
439
  // Try to join each pair of lines
363
440
  for (let i = 0; i < result.length; i++) {
364
441
  for (let j = i + 1; j < result.length; j++) {
365
- const joinedLine = Line2D.joinLine(result[i], result[j]);
442
+ const joinedLine = Line2D.joinLine(result[i], result[j], distTol, parTol);
366
443
  if (joinedLine) {
367
444
  // Replace the first line with the joined line and remove the second
368
445
  result[i] = joinedLine;
package/cjs/Polygon.js CHANGED
@@ -80,6 +80,9 @@ class Polygon {
80
80
  return (0, getPolygonArea_1.getPolygonArea)(this.contour);
81
81
  }
82
82
  boundingBox() {
83
+ if (!this.contour.length) {
84
+ return new BoundingBox_1.BoundingBox(0, 0, 0, 0);
85
+ }
83
86
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
84
87
  for (const p of this.contour) {
85
88
  if (minX > p.x)
@@ -1,121 +1,121 @@
1
- [@immugio/three-math-extensions](../../README.md) / [Exports](../modules.md) / BoundingBox
2
-
3
- # Class: BoundingBox
4
-
5
- ## Table of contents
6
-
7
- ### Constructors
8
-
9
- - [constructor](BoundingBox.md#constructor)
10
-
11
- ### Properties
12
-
13
- - [maxX](BoundingBox.md#maxx)
14
- - [maxY](BoundingBox.md#maxy)
15
- - [minX](BoundingBox.md#minx)
16
- - [minY](BoundingBox.md#miny)
17
-
18
- ### Accessors
19
-
20
- - [size](BoundingBox.md#size)
21
-
22
- ### Methods
23
-
24
- - [equals](BoundingBox.md#equals)
25
-
26
- ## Constructors
27
-
28
- ### constructor
29
-
30
- • **new BoundingBox**(`minX`, `maxX`, `minY`, `maxY`): [`BoundingBox`](BoundingBox.md)
31
-
32
- #### Parameters
33
-
34
- | Name | Type |
35
- | :------ | :------ |
36
- | `minX` | `number` |
37
- | `maxX` | `number` |
38
- | `minY` | `number` |
39
- | `maxY` | `number` |
40
-
41
- #### Returns
42
-
43
- [`BoundingBox`](BoundingBox.md)
44
-
45
- #### Defined in
46
-
47
- [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
48
-
49
- ## Properties
50
-
51
- ### maxX
52
-
53
- • **maxX**: `number`
54
-
55
- #### Defined in
56
-
57
- [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
58
-
59
- ___
60
-
61
- ### maxY
62
-
63
- • **maxY**: `number`
64
-
65
- #### Defined in
66
-
67
- [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
68
-
69
- ___
70
-
71
- ### minX
72
-
73
- • **minX**: `number`
74
-
75
- #### Defined in
76
-
77
- [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
78
-
79
- ___
80
-
81
- ### minY
82
-
83
- • **minY**: `number`
84
-
85
- #### Defined in
86
-
87
- [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
88
-
89
- ## Accessors
90
-
91
- ### size
92
-
93
- • `get` **size**(): [`Vec2`](Vec2.md)
94
-
95
- #### Returns
96
-
97
- [`Vec2`](Vec2.md)
98
-
99
- #### Defined in
100
-
101
- [src/BoundingBox.ts:11](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L11)
102
-
103
- ## Methods
104
-
105
- ### equals
106
-
107
- ▸ **equals**(`other`): `boolean`
108
-
109
- #### Parameters
110
-
111
- | Name | Type |
112
- | :------ | :------ |
113
- | `other` | [`BoundingBox`](BoundingBox.md) |
114
-
115
- #### Returns
116
-
117
- `boolean`
118
-
119
- #### Defined in
120
-
121
- [src/BoundingBox.ts:7](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L7)
1
+ [@immugio/three-math-extensions](../../README.md) / [Exports](../modules.md) / BoundingBox
2
+
3
+ # Class: BoundingBox
4
+
5
+ ## Table of contents
6
+
7
+ ### Constructors
8
+
9
+ - [constructor](BoundingBox.md#constructor)
10
+
11
+ ### Properties
12
+
13
+ - [maxX](BoundingBox.md#maxx)
14
+ - [maxY](BoundingBox.md#maxy)
15
+ - [minX](BoundingBox.md#minx)
16
+ - [minY](BoundingBox.md#miny)
17
+
18
+ ### Accessors
19
+
20
+ - [size](BoundingBox.md#size)
21
+
22
+ ### Methods
23
+
24
+ - [equals](BoundingBox.md#equals)
25
+
26
+ ## Constructors
27
+
28
+ ### constructor
29
+
30
+ • **new BoundingBox**(`minX`, `maxX`, `minY`, `maxY`): [`BoundingBox`](BoundingBox.md)
31
+
32
+ #### Parameters
33
+
34
+ | Name | Type |
35
+ | :------ | :------ |
36
+ | `minX` | `number` |
37
+ | `maxX` | `number` |
38
+ | `minY` | `number` |
39
+ | `maxY` | `number` |
40
+
41
+ #### Returns
42
+
43
+ [`BoundingBox`](BoundingBox.md)
44
+
45
+ #### Defined in
46
+
47
+ [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
48
+
49
+ ## Properties
50
+
51
+ ### maxX
52
+
53
+ • **maxX**: `number`
54
+
55
+ #### Defined in
56
+
57
+ [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
58
+
59
+ ___
60
+
61
+ ### maxY
62
+
63
+ • **maxY**: `number`
64
+
65
+ #### Defined in
66
+
67
+ [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
68
+
69
+ ___
70
+
71
+ ### minX
72
+
73
+ • **minX**: `number`
74
+
75
+ #### Defined in
76
+
77
+ [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
78
+
79
+ ___
80
+
81
+ ### minY
82
+
83
+ • **minY**: `number`
84
+
85
+ #### Defined in
86
+
87
+ [src/BoundingBox.ts:4](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L4)
88
+
89
+ ## Accessors
90
+
91
+ ### size
92
+
93
+ • `get` **size**(): [`Vec2`](Vec2.md)
94
+
95
+ #### Returns
96
+
97
+ [`Vec2`](Vec2.md)
98
+
99
+ #### Defined in
100
+
101
+ [src/BoundingBox.ts:11](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L11)
102
+
103
+ ## Methods
104
+
105
+ ### equals
106
+
107
+ ▸ **equals**(`other`): `boolean`
108
+
109
+ #### Parameters
110
+
111
+ | Name | Type |
112
+ | :------ | :------ |
113
+ | `other` | [`BoundingBox`](BoundingBox.md) |
114
+
115
+ #### Returns
116
+
117
+ `boolean`
118
+
119
+ #### Defined in
120
+
121
+ [src/BoundingBox.ts:7](https://github.com/Immugio/three-math-extensions/blob/e397290/src/BoundingBox.ts#L7)