@itwin/core-geometry 4.0.0-dev.23 → 4.0.0-dev.28

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 (204) hide show
  1. package/CHANGELOG.md +7 -2
  2. package/lib/cjs/Geometry.d.ts +23 -0
  3. package/lib/cjs/Geometry.d.ts.map +1 -1
  4. package/lib/cjs/Geometry.js +25 -1
  5. package/lib/cjs/Geometry.js.map +1 -1
  6. package/lib/cjs/bspline/BSplineCurveOps.js +4 -5
  7. package/lib/cjs/bspline/BSplineCurveOps.js.map +1 -1
  8. package/lib/cjs/bspline/InterpolationCurve3d.js +7 -10
  9. package/lib/cjs/bspline/InterpolationCurve3d.js.map +1 -1
  10. package/lib/cjs/clipping/ClipPrimitive.js +2 -2
  11. package/lib/cjs/clipping/ClipPrimitive.js.map +1 -1
  12. package/lib/cjs/clipping/ClipUtils.js +4 -4
  13. package/lib/cjs/clipping/ClipUtils.js.map +1 -1
  14. package/lib/cjs/clipping/internalContexts/LineStringOffsetClipperContext.js +2 -2
  15. package/lib/cjs/clipping/internalContexts/LineStringOffsetClipperContext.js.map +1 -1
  16. package/lib/cjs/curve/CurveChainWithDistanceIndex.js +1 -1
  17. package/lib/cjs/curve/CurveChainWithDistanceIndex.js.map +1 -1
  18. package/lib/cjs/curve/CurveCollection.js +1 -1
  19. package/lib/cjs/curve/CurveCollection.js.map +1 -1
  20. package/lib/cjs/curve/StrokeOptions.js +1 -2
  21. package/lib/cjs/curve/StrokeOptions.js.map +1 -1
  22. package/lib/cjs/curve/spiral/DirectSpiral3d.js +1 -2
  23. package/lib/cjs/curve/spiral/DirectSpiral3d.js.map +1 -1
  24. package/lib/cjs/curve/spiral/IntegratedSpiral3d.js +1 -2
  25. package/lib/cjs/curve/spiral/IntegratedSpiral3d.js.map +1 -1
  26. package/lib/cjs/geometry3d/BarycentricTriangle.d.ts +195 -8
  27. package/lib/cjs/geometry3d/BarycentricTriangle.d.ts.map +1 -1
  28. package/lib/cjs/geometry3d/BarycentricTriangle.js +459 -11
  29. package/lib/cjs/geometry3d/BarycentricTriangle.js.map +1 -1
  30. package/lib/cjs/geometry3d/GrowableFloat64Array.js +2 -2
  31. package/lib/cjs/geometry3d/GrowableFloat64Array.js.map +1 -1
  32. package/lib/cjs/geometry3d/GrowableXYArray.d.ts +1 -1
  33. package/lib/cjs/geometry3d/GrowableXYArray.d.ts.map +1 -1
  34. package/lib/cjs/geometry3d/GrowableXYArray.js +2 -2
  35. package/lib/cjs/geometry3d/GrowableXYArray.js.map +1 -1
  36. package/lib/cjs/geometry3d/GrowableXYZArray.js +1 -1
  37. package/lib/cjs/geometry3d/GrowableXYZArray.js.map +1 -1
  38. package/lib/cjs/geometry3d/IndexedXYCollection.d.ts +22 -7
  39. package/lib/cjs/geometry3d/IndexedXYCollection.d.ts.map +1 -1
  40. package/lib/cjs/geometry3d/IndexedXYCollection.js +41 -5
  41. package/lib/cjs/geometry3d/IndexedXYCollection.js.map +1 -1
  42. package/lib/cjs/geometry3d/IndexedXYZCollection.d.ts +58 -4
  43. package/lib/cjs/geometry3d/IndexedXYZCollection.d.ts.map +1 -1
  44. package/lib/cjs/geometry3d/IndexedXYZCollection.js +102 -4
  45. package/lib/cjs/geometry3d/IndexedXYZCollection.js.map +1 -1
  46. package/lib/cjs/geometry3d/Point2dArrayCarrier.d.ts +10 -0
  47. package/lib/cjs/geometry3d/Point2dArrayCarrier.d.ts.map +1 -1
  48. package/lib/cjs/geometry3d/Point2dArrayCarrier.js +14 -0
  49. package/lib/cjs/geometry3d/Point2dArrayCarrier.js.map +1 -1
  50. package/lib/cjs/geometry3d/Point2dVector2d.js +4 -6
  51. package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
  52. package/lib/cjs/geometry3d/Point3dArrayCarrier.d.ts +0 -6
  53. package/lib/cjs/geometry3d/Point3dArrayCarrier.d.ts.map +1 -1
  54. package/lib/cjs/geometry3d/Point3dArrayCarrier.js +0 -6
  55. package/lib/cjs/geometry3d/Point3dArrayCarrier.js.map +1 -1
  56. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +1 -1
  57. package/lib/cjs/geometry3d/Point3dVector3d.js +6 -8
  58. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  59. package/lib/cjs/geometry3d/PointHelpers.d.ts +14 -1
  60. package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
  61. package/lib/cjs/geometry3d/PointHelpers.js +33 -1
  62. package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
  63. package/lib/cjs/geometry3d/PolygonOps.d.ts +120 -10
  64. package/lib/cjs/geometry3d/PolygonOps.d.ts.map +1 -1
  65. package/lib/cjs/geometry3d/PolygonOps.js +413 -13
  66. package/lib/cjs/geometry3d/PolygonOps.js.map +1 -1
  67. package/lib/cjs/geometry3d/Ray3d.js +1 -1
  68. package/lib/cjs/geometry3d/Ray3d.js.map +1 -1
  69. package/lib/cjs/polyface/AuxData.js +1 -1
  70. package/lib/cjs/polyface/AuxData.js.map +1 -1
  71. package/lib/cjs/polyface/FacetLocationDetail.d.ts +264 -0
  72. package/lib/cjs/polyface/FacetLocationDetail.d.ts.map +1 -0
  73. package/lib/cjs/polyface/FacetLocationDetail.js +376 -0
  74. package/lib/cjs/polyface/FacetLocationDetail.js.map +1 -0
  75. package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts +2 -5
  76. package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
  77. package/lib/cjs/polyface/IndexedPolyfaceVisitor.js +5 -2
  78. package/lib/cjs/polyface/IndexedPolyfaceVisitor.js.map +1 -1
  79. package/lib/cjs/polyface/PolyfaceBuilder.d.ts +20 -14
  80. package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
  81. package/lib/cjs/polyface/PolyfaceBuilder.js +21 -17
  82. package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
  83. package/lib/cjs/polyface/PolyfaceClip.js +6 -7
  84. package/lib/cjs/polyface/PolyfaceClip.js.map +1 -1
  85. package/lib/cjs/polyface/PolyfaceData.d.ts +1 -1
  86. package/lib/cjs/polyface/PolyfaceData.d.ts.map +1 -1
  87. package/lib/cjs/polyface/PolyfaceData.js.map +1 -1
  88. package/lib/cjs/polyface/PolyfaceQuery.d.ts +22 -1
  89. package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
  90. package/lib/cjs/polyface/PolyfaceQuery.js +52 -2
  91. package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
  92. package/lib/cjs/serialization/BGFBReader.js +4 -4
  93. package/lib/cjs/serialization/BGFBReader.js.map +1 -1
  94. package/lib/cjs/serialization/IModelJsonSchema.js +1 -2
  95. package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
  96. package/lib/cjs/solid/Sphere.d.ts +5 -5
  97. package/lib/cjs/solid/Sphere.js +5 -5
  98. package/lib/cjs/solid/Sphere.js.map +1 -1
  99. package/lib/cjs/solid/SweepContour.js +1 -1
  100. package/lib/cjs/solid/SweepContour.js.map +1 -1
  101. package/lib/cjs/topology/Triangulation.js +1 -1
  102. package/lib/cjs/topology/Triangulation.js.map +1 -1
  103. package/lib/esm/Geometry.d.ts +23 -0
  104. package/lib/esm/Geometry.d.ts.map +1 -1
  105. package/lib/esm/Geometry.js +24 -0
  106. package/lib/esm/Geometry.js.map +1 -1
  107. package/lib/esm/bspline/BSplineCurveOps.js +4 -5
  108. package/lib/esm/bspline/BSplineCurveOps.js.map +1 -1
  109. package/lib/esm/bspline/InterpolationCurve3d.js +7 -10
  110. package/lib/esm/bspline/InterpolationCurve3d.js.map +1 -1
  111. package/lib/esm/clipping/ClipPrimitive.js +2 -2
  112. package/lib/esm/clipping/ClipPrimitive.js.map +1 -1
  113. package/lib/esm/clipping/ClipUtils.js +4 -4
  114. package/lib/esm/clipping/ClipUtils.js.map +1 -1
  115. package/lib/esm/clipping/internalContexts/LineStringOffsetClipperContext.js +2 -2
  116. package/lib/esm/clipping/internalContexts/LineStringOffsetClipperContext.js.map +1 -1
  117. package/lib/esm/curve/CurveChainWithDistanceIndex.js +1 -1
  118. package/lib/esm/curve/CurveChainWithDistanceIndex.js.map +1 -1
  119. package/lib/esm/curve/CurveCollection.js +1 -1
  120. package/lib/esm/curve/CurveCollection.js.map +1 -1
  121. package/lib/esm/curve/StrokeOptions.js +1 -2
  122. package/lib/esm/curve/StrokeOptions.js.map +1 -1
  123. package/lib/esm/curve/spiral/DirectSpiral3d.js +1 -2
  124. package/lib/esm/curve/spiral/DirectSpiral3d.js.map +1 -1
  125. package/lib/esm/curve/spiral/IntegratedSpiral3d.js +1 -2
  126. package/lib/esm/curve/spiral/IntegratedSpiral3d.js.map +1 -1
  127. package/lib/esm/geometry3d/BarycentricTriangle.d.ts +195 -8
  128. package/lib/esm/geometry3d/BarycentricTriangle.d.ts.map +1 -1
  129. package/lib/esm/geometry3d/BarycentricTriangle.js +459 -12
  130. package/lib/esm/geometry3d/BarycentricTriangle.js.map +1 -1
  131. package/lib/esm/geometry3d/GrowableFloat64Array.js +2 -2
  132. package/lib/esm/geometry3d/GrowableFloat64Array.js.map +1 -1
  133. package/lib/esm/geometry3d/GrowableXYArray.d.ts +1 -1
  134. package/lib/esm/geometry3d/GrowableXYArray.d.ts.map +1 -1
  135. package/lib/esm/geometry3d/GrowableXYArray.js +2 -2
  136. package/lib/esm/geometry3d/GrowableXYArray.js.map +1 -1
  137. package/lib/esm/geometry3d/GrowableXYZArray.js +1 -1
  138. package/lib/esm/geometry3d/GrowableXYZArray.js.map +1 -1
  139. package/lib/esm/geometry3d/IndexedXYCollection.d.ts +22 -7
  140. package/lib/esm/geometry3d/IndexedXYCollection.d.ts.map +1 -1
  141. package/lib/esm/geometry3d/IndexedXYCollection.js +41 -5
  142. package/lib/esm/geometry3d/IndexedXYCollection.js.map +1 -1
  143. package/lib/esm/geometry3d/IndexedXYZCollection.d.ts +58 -4
  144. package/lib/esm/geometry3d/IndexedXYZCollection.d.ts.map +1 -1
  145. package/lib/esm/geometry3d/IndexedXYZCollection.js +103 -5
  146. package/lib/esm/geometry3d/IndexedXYZCollection.js.map +1 -1
  147. package/lib/esm/geometry3d/Point2dArrayCarrier.d.ts +10 -0
  148. package/lib/esm/geometry3d/Point2dArrayCarrier.d.ts.map +1 -1
  149. package/lib/esm/geometry3d/Point2dArrayCarrier.js +14 -0
  150. package/lib/esm/geometry3d/Point2dArrayCarrier.js.map +1 -1
  151. package/lib/esm/geometry3d/Point2dVector2d.js +4 -6
  152. package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
  153. package/lib/esm/geometry3d/Point3dArrayCarrier.d.ts +0 -6
  154. package/lib/esm/geometry3d/Point3dArrayCarrier.d.ts.map +1 -1
  155. package/lib/esm/geometry3d/Point3dArrayCarrier.js +0 -6
  156. package/lib/esm/geometry3d/Point3dArrayCarrier.js.map +1 -1
  157. package/lib/esm/geometry3d/Point3dVector3d.d.ts +1 -1
  158. package/lib/esm/geometry3d/Point3dVector3d.js +6 -8
  159. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  160. package/lib/esm/geometry3d/PointHelpers.d.ts +14 -1
  161. package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
  162. package/lib/esm/geometry3d/PointHelpers.js +33 -1
  163. package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
  164. package/lib/esm/geometry3d/PolygonOps.d.ts +120 -10
  165. package/lib/esm/geometry3d/PolygonOps.d.ts.map +1 -1
  166. package/lib/esm/geometry3d/PolygonOps.js +412 -13
  167. package/lib/esm/geometry3d/PolygonOps.js.map +1 -1
  168. package/lib/esm/geometry3d/Ray3d.js +1 -1
  169. package/lib/esm/geometry3d/Ray3d.js.map +1 -1
  170. package/lib/esm/polyface/AuxData.js +1 -1
  171. package/lib/esm/polyface/AuxData.js.map +1 -1
  172. package/lib/esm/polyface/FacetLocationDetail.d.ts +264 -0
  173. package/lib/esm/polyface/FacetLocationDetail.d.ts.map +1 -0
  174. package/lib/esm/polyface/FacetLocationDetail.js +369 -0
  175. package/lib/esm/polyface/FacetLocationDetail.js.map +1 -0
  176. package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts +2 -5
  177. package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
  178. package/lib/esm/polyface/IndexedPolyfaceVisitor.js +5 -2
  179. package/lib/esm/polyface/IndexedPolyfaceVisitor.js.map +1 -1
  180. package/lib/esm/polyface/PolyfaceBuilder.d.ts +20 -14
  181. package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
  182. package/lib/esm/polyface/PolyfaceBuilder.js +21 -17
  183. package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
  184. package/lib/esm/polyface/PolyfaceClip.js +6 -7
  185. package/lib/esm/polyface/PolyfaceClip.js.map +1 -1
  186. package/lib/esm/polyface/PolyfaceData.d.ts +1 -1
  187. package/lib/esm/polyface/PolyfaceData.d.ts.map +1 -1
  188. package/lib/esm/polyface/PolyfaceData.js.map +1 -1
  189. package/lib/esm/polyface/PolyfaceQuery.d.ts +22 -1
  190. package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
  191. package/lib/esm/polyface/PolyfaceQuery.js +52 -2
  192. package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
  193. package/lib/esm/serialization/BGFBReader.js +4 -4
  194. package/lib/esm/serialization/BGFBReader.js.map +1 -1
  195. package/lib/esm/serialization/IModelJsonSchema.js +1 -2
  196. package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
  197. package/lib/esm/solid/Sphere.d.ts +5 -5
  198. package/lib/esm/solid/Sphere.js +5 -5
  199. package/lib/esm/solid/Sphere.js.map +1 -1
  200. package/lib/esm/solid/SweepContour.js +1 -1
  201. package/lib/esm/solid/SweepContour.js.map +1 -1
  202. package/lib/esm/topology/Triangulation.js +1 -1
  203. package/lib/esm/topology/Triangulation.js.map +1 -1
  204. package/package.json +4 -4
@@ -5,18 +5,75 @@
5
5
  /** @packageDocumentation
6
6
  * @module CartesianGeometry
7
7
  */
8
- import { Geometry } from "../Geometry";
8
+ import { assert } from "@itwin/core-bentley";
9
+ import { AxisOrder, Geometry, PolygonLocation } from "../Geometry";
9
10
  import { Matrix4d } from "../geometry4d/Matrix4d";
10
11
  import { Point4d } from "../geometry4d/Point4d";
11
12
  import { XYParitySearchContext } from "../topology/XYParitySearchContext";
12
13
  import { FrameBuilder } from "./FrameBuilder";
13
14
  import { GrowableXYZArray } from "./GrowableXYZArray";
14
15
  import { IndexedXYZCollection } from "./IndexedXYZCollection";
16
+ import { Matrix3d } from "./Matrix3d";
17
+ import { Plane3dByOriginAndUnitNormal } from "./Plane3dByOriginAndUnitNormal";
15
18
  import { Point2d, Vector2d } from "./Point2dVector2d";
16
19
  import { Point3dArrayCarrier } from "./Point3dArrayCarrier";
17
20
  import { Point3d, Vector3d } from "./Point3dVector3d";
18
21
  import { Ray3d } from "./Ray3d";
19
22
  import { SortablePolygon } from "./SortablePolygon";
23
+ /**
24
+ * Carries data about a point in the plane of a polygon.
25
+ * @public
26
+ */
27
+ export class PolygonLocationDetail {
28
+ constructor() {
29
+ this.point = new Point3d();
30
+ this.a = 0.0;
31
+ this.v = new Vector3d();
32
+ this.code = PolygonLocation.Unknown;
33
+ this.closestEdgeIndex = 0;
34
+ this.closestEdgeParam = 0.0;
35
+ }
36
+ /** Invalidate this detail. */
37
+ invalidate() {
38
+ this.point.setZero();
39
+ this.a = 0.0;
40
+ this.v.setZero();
41
+ this.code = PolygonLocation.Unknown;
42
+ this.closestEdgeIndex = 0;
43
+ this.closestEdgeParam = 0.0;
44
+ }
45
+ /** Create an invalid detail.
46
+ * @param result optional pre-allocated object to fill and return
47
+ */
48
+ static create(result) {
49
+ if (undefined === result)
50
+ result = new PolygonLocationDetail();
51
+ else
52
+ result.invalidate();
53
+ return result;
54
+ }
55
+ /** Set the instance contents from the other detail.
56
+ * @param other detail to clone
57
+ */
58
+ copyContentsFrom(other) {
59
+ this.point.setFrom(other.point);
60
+ this.a = other.a;
61
+ this.v.setFrom(other.v);
62
+ this.code = other.code;
63
+ this.closestEdgeIndex = other.closestEdgeIndex;
64
+ this.closestEdgeParam = other.closestEdgeParam;
65
+ }
66
+ /** Whether this detail is valid. */
67
+ get isValid() {
68
+ return this.code !== PolygonLocation.Unknown;
69
+ }
70
+ /** Whether this instance specifies a location inside or on the polygon. */
71
+ get isInsideOrOn() {
72
+ return this.code === PolygonLocation.InsidePolygon ||
73
+ this.code === PolygonLocation.OnPolygonVertex || this.code === PolygonLocation.OnPolygonEdgeInterior ||
74
+ this.code === PolygonLocation.InsidePolygonProjectsToVertex || this.code === PolygonLocation.InsidePolygonProjectsToEdgeInterior;
75
+ }
76
+ }
20
77
  /**
21
78
  * Carrier for a loop extracted from clip operation, annotated for sorting
22
79
  * @internal
@@ -533,29 +590,27 @@ export class PolygonOps {
533
590
  }
534
591
  }
535
592
  /** Test the direction of turn at the vertices of the polygon, ignoring z-coordinates.
536
- *
537
- * * For a polygon without self intersections, this is a convexity and orientation test: all positive is convex and counterclockwise,
538
- * all negative is convex and clockwise
539
- * * Beware that a polygon which turns through more than a full turn can cross itself and close, but is not convex
540
- * * Returns 1 if all turns are to the left, -1 if all to the right, and 0 if there are any zero or reverse turns
593
+ * * For a polygon without self-intersections and successive colinear edges, this is a convexity and orientation test: all positive is convex and counterclockwise, all negative is convex and clockwise.
594
+ * * Beware that a polygon which turns through more than a full turn can cross itself and close, but is not convex.
595
+ * @returns 1 if all turns are to the left, -1 if all to the right, and 0 if there are any zero or reverse turns
541
596
  */
542
- static testXYPolygonTurningDirections(pPointArray) {
597
+ static testXYPolygonTurningDirections(points) {
543
598
  // Reduce count by trailing duplicates; leaves iLast at final index
544
- let numPoint = pPointArray.length;
599
+ let numPoint = points.length;
545
600
  let iLast = numPoint - 1;
546
- while (iLast > 1 && pPointArray[iLast].x === pPointArray[0].x && pPointArray[iLast].y === pPointArray[0].y) {
601
+ while (iLast > 1 && points[iLast].x === points[0].x && points[iLast].y === points[0].y) {
547
602
  numPoint = iLast--;
548
603
  }
549
604
  if (numPoint > 2) {
550
- let vector0 = Point2d.create(pPointArray[iLast].x - pPointArray[iLast - 1].x, pPointArray[iLast].y - pPointArray[iLast - 1].y);
551
- const vector1 = Point2d.create(pPointArray[0].x - pPointArray[iLast].x, pPointArray[0].y - pPointArray[iLast].y);
605
+ let vector0 = Point2d.create(points[iLast].x - points[iLast - 1].x, points[iLast].y - points[iLast - 1].y);
606
+ const vector1 = Point2d.create(points[0].x - points[iLast].x, points[0].y - points[iLast].y);
552
607
  const baseArea = vector0.x * vector1.y - vector0.y * vector1.x;
553
608
  // In a convex polygon, all successive-vector cross products will
554
609
  // have the same sign as the base area, hence all products will be
555
610
  // positive.
556
611
  for (let i1 = 1; i1 < numPoint; i1++) {
557
612
  vector0 = vector1.clone();
558
- Point2d.create(pPointArray[i1].x - pPointArray[i1 - 1].x, pPointArray[i1].y - pPointArray[i1 - 1].y, vector1);
613
+ Point2d.create(points[i1].x - points[i1 - 1].x, points[i1].y - points[i1 - 1].y, vector1);
559
614
  const currArea = vector0.x * vector1.y - vector0.y * vector1.x;
560
615
  if (currArea * baseArea <= 0.0)
561
616
  return 0;
@@ -565,6 +620,36 @@ export class PolygonOps {
565
620
  }
566
621
  return 0;
567
622
  }
623
+ /**
624
+ * Determine whether the polygon is convex.
625
+ * @param polygon vertices, closure point optional
626
+ * @returns whether the polygon is convex.
627
+ */
628
+ static isConvex(polygon) {
629
+ if (!(polygon instanceof IndexedXYZCollection))
630
+ return this.isConvex(new Point3dArrayCarrier(polygon));
631
+ let n = polygon.length;
632
+ if (n > 1 && polygon.getPoint3dAtUncheckedPointIndex(0).isExactEqual(polygon.getPoint3dAtUncheckedPointIndex(n - 1)))
633
+ --n; // ignore closure point
634
+ const normal = Vector3d.create();
635
+ if (!this.unitNormal(polygon, normal))
636
+ return false;
637
+ let positiveArea = 0.0;
638
+ let negativeArea = 0.0;
639
+ const vecA = this._vector0;
640
+ let vecB = Vector3d.createStartEnd(polygon.getPoint3dAtUncheckedPointIndex(n - 1), polygon.getPoint3dAtUncheckedPointIndex(0), this._vector1);
641
+ for (let i = 1; i <= n; i++) {
642
+ // check turn through vertices i-1,i,i+1
643
+ vecA.setFromVector3d(vecB);
644
+ vecB = Vector3d.createStartEnd(polygon.getPoint3dAtUncheckedPointIndex(i - 1), polygon.getPoint3dAtUncheckedPointIndex(i % n), vecB);
645
+ const signedArea = normal.tripleProduct(vecA, vecB);
646
+ if (signedArea >= 0.0)
647
+ positiveArea += signedArea;
648
+ else
649
+ negativeArea += signedArea;
650
+ }
651
+ return Math.abs(negativeArea) < Geometry.smallMetricDistanceSquared * positiveArea;
652
+ }
568
653
  /**
569
654
  * Test if point (x,y) is IN, OUT or ON a polygon.
570
655
  * @return (1) for in, (-1) for OUT, (0) for ON
@@ -685,7 +770,7 @@ export class PolygonOps {
685
770
  */
686
771
  static sortOuterAndHoleLoops(loops, defaultNormal) {
687
772
  const localToWorld = FrameBuilder.createRightHandedFrame(defaultNormal, loops);
688
- const worldToLocal = localToWorld === null || localToWorld === void 0 ? void 0 : localToWorld.inverse();
773
+ const worldToLocal = localToWorld?.inverse();
689
774
  const xyLoops = [];
690
775
  if (worldToLocal !== undefined) {
691
776
  // transform into plane so we can ignore z in the sort
@@ -712,6 +797,320 @@ export class PolygonOps {
712
797
  }
713
798
  return sortedLoopsArray;
714
799
  }
800
+ /** Compute the closest point on the polygon boundary to the given point.
801
+ * @param polygon points of the polygon, closure point optional
802
+ * @param testPoint point p to project onto the polygon edges. Works best when p is in the plane of the polygon.
803
+ * @param tolerance optional distance tolerance to determine point-vertex and point-edge coincidence.
804
+ * @param result optional pre-allocated object to fill and return
805
+ * @returns details d of the closest point `d.point`:
806
+ * * `d.isValid()` returns true if and only if the polygon is nontrivial.
807
+ * * `d.edgeIndex` and `d.edgeParam` specify the location of the closest point, within `distTol`.
808
+ * * `d.code` classifies the closest point as a vertex (`PolygonLocation.OnPolygonVertex`) or as a point on an edge (`PolygonLocation.OnPolygonEdgeInterior`).
809
+ * * `d.a` is the distance from testPoint to the closest point.
810
+ * * `d.v` can be used to classify p (if p and polygon are coplanar): if n is the polygon normal then `d.v.dotProduct(n)` is +/-/0 if and only if p is inside/outside/on the polygon.
811
+ */
812
+ static closestPointOnBoundary(polygon, testPoint, tolerance = Geometry.smallMetricDistance, result) {
813
+ if (!(polygon instanceof IndexedXYZCollection))
814
+ return this.closestPointOnBoundary(new Point3dArrayCarrier(polygon), testPoint, tolerance, result);
815
+ const distTol2 = tolerance * tolerance;
816
+ let numPoints = polygon.length;
817
+ while (numPoints > 1) {
818
+ if (polygon.distanceSquaredIndexIndex(0, numPoints - 1) > distTol2)
819
+ break;
820
+ --numPoints; // ignore closure point
821
+ }
822
+ result = PolygonLocationDetail.create(result);
823
+ if (0 === numPoints)
824
+ return result; // invalid
825
+ if (1 === numPoints) {
826
+ polygon.getPoint3dAtUncheckedPointIndex(0, result.point);
827
+ result.a = result.point.distance(testPoint);
828
+ result.v.setZero();
829
+ result.code = PolygonLocation.OnPolygonVertex;
830
+ result.closestEdgeIndex = 0;
831
+ result.closestEdgeParam = 0.0;
832
+ return result;
833
+ }
834
+ let iPrev = numPoints - 1;
835
+ let minDist2 = Geometry.largeCoordinateResult;
836
+ for (let iBase = 0; iBase < numPoints; ++iBase) {
837
+ let iNext = iBase + 1;
838
+ if (iNext === numPoints)
839
+ iNext = 0;
840
+ const uDotU = polygon.distanceSquaredIndexIndex(iBase, iNext);
841
+ if (uDotU <= distTol2)
842
+ continue; // ignore trivial polygon edge (keep iPrev)
843
+ const vDotV = polygon.distanceSquaredIndexXYAndZ(iBase, testPoint);
844
+ const uDotV = polygon.dotProductIndexIndexXYAndZ(iBase, iNext, testPoint);
845
+ const edgeParam = uDotV / uDotU; // param of projection of testPoint onto this edge
846
+ if (edgeParam <= 0.0) { // testPoint projects to/before edge start
847
+ const distToStart2 = vDotV;
848
+ if (distToStart2 <= distTol2) {
849
+ // testPoint is at edge start; we are done
850
+ polygon.getPoint3dAtUncheckedPointIndex(iBase, result.point);
851
+ result.a = Math.sqrt(distToStart2);
852
+ result.v.setZero();
853
+ result.code = PolygonLocation.OnPolygonVertex;
854
+ result.closestEdgeIndex = iBase;
855
+ result.closestEdgeParam = 0.0;
856
+ return result;
857
+ }
858
+ if (distToStart2 < minDist2) {
859
+ if (polygon.dotProductIndexIndexXYAndZ(iBase, iPrev, testPoint) <= 0.0) {
860
+ // update candidate (to edge start) only if previous edge was NOOP
861
+ polygon.getPoint3dAtUncheckedPointIndex(iBase, result.point);
862
+ result.a = Math.sqrt(distToStart2);
863
+ polygon.crossProductIndexIndexIndex(iBase, iPrev, iNext, result.v);
864
+ result.code = PolygonLocation.OnPolygonVertex;
865
+ result.closestEdgeIndex = iBase;
866
+ result.closestEdgeParam = 0.0;
867
+ minDist2 = distToStart2;
868
+ }
869
+ }
870
+ }
871
+ else if (edgeParam <= 1.0) { // testPoint projects inside edge, or to edge end
872
+ const projDist2 = vDotV - edgeParam * edgeParam * uDotU;
873
+ if (projDist2 <= distTol2) {
874
+ // testPoint is on edge; we are done
875
+ const distToStart2 = vDotV;
876
+ if (edgeParam <= 0.5 && distToStart2 <= distTol2) {
877
+ // testPoint is at edge start
878
+ polygon.getPoint3dAtUncheckedPointIndex(iBase, result.point);
879
+ result.a = Math.sqrt(distToStart2);
880
+ result.v.setZero();
881
+ result.code = PolygonLocation.OnPolygonVertex;
882
+ result.closestEdgeIndex = iBase;
883
+ result.closestEdgeParam = 0.0;
884
+ return result;
885
+ }
886
+ const distToEnd2 = projDist2 + (1.0 - edgeParam) * (1.0 - edgeParam) * uDotU;
887
+ if (edgeParam > 0.5 && distToEnd2 <= distTol2) {
888
+ // testPoint is at edge end
889
+ polygon.getPoint3dAtUncheckedPointIndex(iNext, result.point);
890
+ result.a = Math.sqrt(distToEnd2);
891
+ result.v.setZero();
892
+ result.code = PolygonLocation.OnPolygonVertex;
893
+ result.closestEdgeIndex = iNext;
894
+ result.closestEdgeParam = 0.0;
895
+ return result;
896
+ }
897
+ // testPoint is on edge interior
898
+ polygon.interpolateIndexIndex(iBase, edgeParam, iNext, result.point);
899
+ result.a = Math.sqrt(projDist2);
900
+ result.v.setZero();
901
+ result.code = PolygonLocation.OnPolygonEdgeInterior;
902
+ result.closestEdgeIndex = iBase;
903
+ result.closestEdgeParam = edgeParam;
904
+ return result;
905
+ }
906
+ if (projDist2 < minDist2) {
907
+ // update candidate (to edge interior)
908
+ polygon.interpolateIndexIndex(iBase, edgeParam, iNext, result.point);
909
+ result.a = Math.sqrt(projDist2);
910
+ polygon.crossProductIndexIndexXYAndZ(iBase, iNext, testPoint, result.v);
911
+ result.code = PolygonLocation.OnPolygonEdgeInterior;
912
+ result.closestEdgeIndex = iBase;
913
+ result.closestEdgeParam = edgeParam;
914
+ minDist2 = projDist2;
915
+ }
916
+ }
917
+ else { // edgeParam > 1.0
918
+ // NOOP: testPoint projects beyond edge end, handled by next edge
919
+ }
920
+ iPrev = iBase;
921
+ }
922
+ return result;
923
+ }
924
+ /** Compute the intersection of a line (parameterized as a ray) with the plane of this polygon.
925
+ * @param polygon points of the polygon, closure point optional
926
+ * @param ray infinite line to intersect, as a ray
927
+ * @param tolerance optional distance tolerance to determine point-vertex and point-edge coincidence.
928
+ * @param result optional pre-allocated object to fill and return
929
+ * @returns details d of the line-plane intersection `d.point`:
930
+ * * `d.isValid()` returns true if and only if the line intersects the plane.
931
+ * * `d.code` indicates where the intersection lies with respect to the polygon.
932
+ * * `d.a` is the ray intersection parameter. If `d.a` >= 0, the ray intersects the plane of the polygon.
933
+ * * `d.edgeIndex` and `d.edgeParam` specify the location of the closest point on the polygon to the intersection, within `distTol`.
934
+ */
935
+ static intersectRay3d(polygon, ray, tolerance = Geometry.smallMetricDistance, result) {
936
+ if (!(polygon instanceof IndexedXYZCollection))
937
+ return this.intersectRay3d(new Point3dArrayCarrier(polygon), ray, tolerance, result);
938
+ if (!this.unitNormal(polygon, this._normal))
939
+ return PolygonLocationDetail.create(result); // invalid
940
+ this._workPlane = Plane3dByOriginAndUnitNormal.createXYZUVW(polygon.getXAtUncheckedPointIndex(0), polygon.getYAtUncheckedPointIndex(0), polygon.getZAtUncheckedPointIndex(0), this._normal.x, this._normal.y, this._normal.z, this._workPlane);
941
+ const intersectionPoint = Point3d.createZero(this._workXYZ);
942
+ const rayParam = ray.intersectionWithPlane(this._workPlane, intersectionPoint);
943
+ if (undefined === rayParam)
944
+ return PolygonLocationDetail.create(result);
945
+ result = this.closestPointOnBoundary(polygon, intersectionPoint, tolerance, result);
946
+ if (result.isValid) {
947
+ result.point.setFrom(intersectionPoint);
948
+ result.a = rayParam;
949
+ const dot = result.v.dotProduct(this._normal);
950
+ if (dot === 0.0) {
951
+ // NOOP: intersectionPoint is on the polygon, so result.code already classifies it
952
+ }
953
+ else {
954
+ // intersectionPoint is not on polygon, so result.code refers to the closest point. Update it to refer to intersectionPoint.
955
+ if (PolygonLocation.OnPolygonVertex === result.code)
956
+ result.code = (dot > 0.0) ? PolygonLocation.InsidePolygonProjectsToVertex : PolygonLocation.OutsidePolygonProjectsToVertex;
957
+ else if (PolygonLocation.OnPolygonEdgeInterior === result.code)
958
+ result.code = (dot > 0.0) ? PolygonLocation.InsidePolygonProjectsToEdgeInterior : PolygonLocation.OutsidePolygonProjectsToEdgeInterior;
959
+ }
960
+ }
961
+ return result;
962
+ }
963
+ /** Compute the intersection of a line (parameterized as a line segment) with the plane of this polygon.
964
+ * @param polygon points of the polygon, closure point optional
965
+ * @param point0 start point of segment on line to intersect
966
+ * @param point1 end point of segment on line to intersect
967
+ * @param tolerance optional distance tolerance to determine point-vertex and point-edge coincidence.
968
+ * @param result optional pre-allocated object to fill and return
969
+ * @returns details d of the line-plane intersection `d.point`:
970
+ * * `d.isValid()` returns true if and only if the line intersects the plane.
971
+ * * `d.code` indicates where the intersection lies with respect to the polygon.
972
+ * * `d.a` is the segment intersection parameter. If `d.a` is in [0,1], the segment intersects the plane of the polygon.
973
+ * * `d.edgeIndex` and `d.edgeParam` specify the location of the closest point on the polygon to the intersection, within `distTol`.
974
+ * @see intersectRay3d
975
+ */
976
+ static intersectSegment(polygon, point0, point1, tolerance = Geometry.smallMetricDistance, result) {
977
+ this._workRay = Ray3d.createStartEnd(point0, point1, this._workRay);
978
+ return this.intersectRay3d(polygon, this._workRay, tolerance, result);
979
+ }
980
+ /** Compute edge data for the barycentric coordinate computation, ignoring all z-coordinates.
981
+ * @param polygon points of the polygon (without closure point)
982
+ * @param edgeStartVertexIndex index of start vertex of the edge (unchecked)
983
+ * @param point point to project to the edge
984
+ * @param edgeOutwardUnitNormal pre-allocated vector to be populated on return with the unit perpendicular to the edge, facing outward, in xy-plane
985
+ * @param tolerance used to clamp outputs
986
+ * @param result optional pre-allocated result
987
+ * @returns x: signed projection distance of `point` to the edge, y: edge parameter of the projection
988
+ */
989
+ static computeEdgeDataXY(polygon, edgeStartVertexIndex, point, edgeOutwardUnitNormal, tolerance = Geometry.smallMetricDistance, result) {
990
+ const i0 = edgeStartVertexIndex % polygon.length;
991
+ const i1 = (i0 + 1) % polygon.length;
992
+ polygon.vectorIndexIndex(i0, i1, edgeOutwardUnitNormal).unitPerpendicularXY(edgeOutwardUnitNormal).negate(edgeOutwardUnitNormal); // z is zero
993
+ const hypDeltaX = polygon.getXAtUncheckedPointIndex(i0) - point.x;
994
+ const hypDeltaY = polygon.getYAtUncheckedPointIndex(i0) - point.y;
995
+ let projDist = Geometry.dotProductXYXY(hypDeltaX, hypDeltaY, edgeOutwardUnitNormal.x, edgeOutwardUnitNormal.y);
996
+ const edgeDist = Geometry.crossProductXYXY(hypDeltaX, hypDeltaY, edgeOutwardUnitNormal.x, edgeOutwardUnitNormal.y);
997
+ const edgeLength = Geometry.distanceXYXY(polygon.getXAtUncheckedPointIndex(i0), polygon.getYAtUncheckedPointIndex(i0), polygon.getXAtUncheckedPointIndex(i1), polygon.getYAtUncheckedPointIndex(i1));
998
+ let edgeParam = Geometry.safeDivideFraction(edgeDist, edgeLength, 0.0);
999
+ if (Geometry.isSameCoordinate(0.0, projDist, tolerance))
1000
+ projDist = 0.0;
1001
+ if (Geometry.isSameCoordinate(0.0, edgeParam, tolerance))
1002
+ edgeParam = 0.0;
1003
+ else if (Geometry.isSameCoordinate(1.0, edgeParam, tolerance))
1004
+ edgeParam = 1.0;
1005
+ return Point2d.create(projDist, edgeParam, result);
1006
+ }
1007
+ /** Compute the barycentric coordinates for a point on either of a pair of adjacent edges of a convex polygon.
1008
+ * @param polygon points of the polygon, assumed to be convex. Assumed to have no closure point.
1009
+ * @param iPrev start index of previous edge
1010
+ * @param prevNormal outward unit normal of previous edge
1011
+ * @param prevProj x = signed distance from point to previous edge; y = edge parameter of this projection in [0,1]
1012
+ * @param i start index of current edge
1013
+ * @param normal outward unit normal of current edge
1014
+ * @param proj x = signed distance from point to current edge; y = edge parameter of this projection in [0,1]
1015
+ * @param coords pre-allocated barycentric coordinate array to return, assumed to have length at least `polygon.length`
1016
+ * @returns barycentric coordinates, or undefined if not on either edge
1017
+ */
1018
+ static convexBarycentricCoordinatesOnEdge(polygon, iPrev, prevNormal, prevProj, i, normal, proj, coords) {
1019
+ // ignore degenerate edges
1020
+ const pointIsOnPrevEdge = !prevNormal.isZero && (0.0 === prevProj.x) && Geometry.isIn01(prevProj.y);
1021
+ const pointIsOnEdge = !normal.isZero && (0.0 === proj.x) && Geometry.isIn01(proj.y);
1022
+ if (pointIsOnPrevEdge && pointIsOnEdge) { // the point is at vertex i
1023
+ coords.fill(0);
1024
+ coords[i] = 1.0;
1025
+ return coords;
1026
+ }
1027
+ const n = polygon.length;
1028
+ if (pointIsOnPrevEdge) { // the point is on the previous edge
1029
+ coords.fill(0);
1030
+ const i0 = iPrev;
1031
+ const i1 = i;
1032
+ const edgeParam = prevProj.y;
1033
+ coords[i0] = 1.0 - edgeParam;
1034
+ coords[i1] = edgeParam;
1035
+ return coords;
1036
+ }
1037
+ if (pointIsOnEdge) { // the point is on the edge starting at the i_th vertex
1038
+ coords.fill(0);
1039
+ const i0 = i;
1040
+ const i1 = (i + 1) % n;
1041
+ const edgeParam = proj.y;
1042
+ coords[i0] = 1.0 - edgeParam;
1043
+ coords[i1] = edgeParam;
1044
+ return coords;
1045
+ }
1046
+ return undefined; // not on edge
1047
+ }
1048
+ // cspell:word CAGD
1049
+ /** Compute the barycentric coordinates for a point inside a convex polygon.
1050
+ * @param polygon points of the polygon, assumed to be convex. Closure point optional.
1051
+ * @param point point assumed to be inside or on polygon
1052
+ * @param tolerance distance tolerance for point to be considered on a polygon edge
1053
+ * @return barycentric coordinates of the interior point, or undefined if invalid polygon or exterior point. Length is same as `polygon.length`.
1054
+ * @see BarycentricTriangle.pointToFraction
1055
+ */
1056
+ static convexBarycentricCoordinates(polygon, point, tolerance = Geometry.smallMetricDistance) {
1057
+ // cf. "Barycentric Coordinates for Convex Sets", by Warren et al., CAGD (2003)
1058
+ if (Array.isArray(polygon))
1059
+ return this.convexBarycentricCoordinates(new Point3dArrayCarrier(polygon), point);
1060
+ let n = polygon.length;
1061
+ while (n > 1 && polygon.getPoint3dAtUncheckedPointIndex(0).isExactEqual(polygon.getPoint3dAtUncheckedPointIndex(n - 1)))
1062
+ --n; // ignore closure point(s)
1063
+ if (n < 3 || !PolygonOps.unitNormal(polygon, this._normal))
1064
+ return undefined;
1065
+ const localToWorld = this._workMatrix3d = Matrix3d.createRigidHeadsUp(this._normal, AxisOrder.ZXY, this._workMatrix3d);
1066
+ const polygonXY = new GrowableXYZArray(n);
1067
+ for (let i = 0; i < n; ++i)
1068
+ polygonXY.push(localToWorld.multiplyInverseXYZAsPoint3d(polygon.getXAtUncheckedPointIndex(i), polygon.getYAtUncheckedPointIndex(i), polygon.getZAtUncheckedPointIndex(i), this._workXYZ));
1069
+ const pointXY = this._workXYZ = localToWorld.multiplyInverseXYZAsPoint3d(point.x, point.y, point.z, this._workXYZ);
1070
+ // now we know polygon orientation is ccw, its last edge has positive length, and we can ignore z-coords
1071
+ let iPrev = n - 1;
1072
+ const outwardUnitNormalOfLastEdge = this._vector0;
1073
+ const projToLastEdge = this._workXY0 = this.computeEdgeDataXY(polygonXY, iPrev, pointXY, outwardUnitNormalOfLastEdge, tolerance, this._workXY0);
1074
+ // we can compare to exact zero because computeEdgeDataXY has chopped small distances to zero
1075
+ if (projToLastEdge.x < 0.0)
1076
+ return undefined; // point is outside polygon, or polygon is nonconvex
1077
+ const outwardUnitNormalOfPrevEdge = Vector3d.createFrom(outwardUnitNormalOfLastEdge, this._vector1);
1078
+ const projToPrevEdge = this._workXY1 = Point2d.createFrom(projToLastEdge, this._workXY1);
1079
+ const coords = Array(polygon.length).fill(0); // use original length
1080
+ const largestResult = (tolerance > 0.0) ? 1.0 / (tolerance * tolerance) : Geometry.largeCoordinateResult;
1081
+ let coordSum = 0.0;
1082
+ for (let i = 0; i < n; ++i) {
1083
+ const outwardUnitNormalOfEdge = Vector3d.createFrom(outwardUnitNormalOfLastEdge, this._vector2);
1084
+ const projToEdge = this._workXY2 = (i < n - 1) ? this.computeEdgeDataXY(polygonXY, i, pointXY, outwardUnitNormalOfEdge, tolerance, this._workXY2) : Point2d.createFrom(projToLastEdge, this._workXY2);
1085
+ if (projToEdge.x < 0.0)
1086
+ return undefined; // point is outside polygon, or polygon is nonconvex
1087
+ if (undefined !== this.convexBarycentricCoordinatesOnEdge(polygonXY, iPrev, outwardUnitNormalOfPrevEdge, projToPrevEdge, i, outwardUnitNormalOfEdge, projToEdge, coords))
1088
+ return coords; // point is on vertex or edge; we are done
1089
+ if (outwardUnitNormalOfEdge.x === 0.0 && outwardUnitNormalOfEdge.y === 0.0)
1090
+ continue; // edge is degenerate; coords[i] = 0; keep previous edge data
1091
+ if (0.0 === projToPrevEdge.x || 0.0 === projToEdge.x)
1092
+ continue; // point is on subsequent colinear edge (ASSUMING interior point, convex polygon!); coords[i] = 0; keep previous edge data
1093
+ const areaOfNormalParallelogram = Math.abs(outwardUnitNormalOfPrevEdge.crossProductXY(outwardUnitNormalOfEdge));
1094
+ const coord = Geometry.conditionalDivideCoordinate(areaOfNormalParallelogram, projToPrevEdge.x * projToEdge.x, largestResult);
1095
+ if (undefined === coord) {
1096
+ assert(!"unexpectedly small projection distance to an edge");
1097
+ return undefined; // shouldn't happen due to chopping in computeEdgeDataXY: area/(dist*dist) <= 1/tol^2 = largestResult
1098
+ }
1099
+ coords[i] = coord;
1100
+ coordSum += coord;
1101
+ outwardUnitNormalOfPrevEdge.setFrom(outwardUnitNormalOfEdge);
1102
+ projToPrevEdge.setFrom(projToEdge);
1103
+ iPrev = i;
1104
+ }
1105
+ const scale = Geometry.conditionalDivideCoordinate(1.0, coordSum);
1106
+ if (undefined === scale) {
1107
+ assert(!"unexpected zero barycentric coordinate sum");
1108
+ return undefined;
1109
+ }
1110
+ for (let i = 0; i < n; ++i)
1111
+ coords[i] *= scale; // normalized
1112
+ return coords;
1113
+ }
715
1114
  }
716
1115
  /** These values are the integrated area moment products [xx,xy,xz, x]
717
1116
  * for a right triangle in the first quadrant at the origin -- (0,0),(1,0),(0,1)