@itwin/core-geometry 4.0.0-dev.22 → 4.0.0-dev.24

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