@itwin/core-geometry 4.7.0-dev.7 → 4.7.0-dev.9

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 (113) hide show
  1. package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +7 -1
  2. package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
  3. package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js +20 -4
  4. package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
  5. package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js +1 -1
  6. package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
  7. package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js +1 -1
  8. package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
  9. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +3 -3
  10. package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
  11. package/lib/cjs/geometry3d/Point3dVector3d.js +4 -3
  12. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  13. package/lib/cjs/geometry3d/PointHelpers.d.ts +7 -0
  14. package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
  15. package/lib/cjs/geometry3d/PointHelpers.js +19 -0
  16. package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
  17. package/lib/cjs/polyface/AuxData.d.ts +18 -10
  18. package/lib/cjs/polyface/AuxData.d.ts.map +1 -1
  19. package/lib/cjs/polyface/AuxData.js +24 -12
  20. package/lib/cjs/polyface/AuxData.js.map +1 -1
  21. package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts +2 -1
  22. package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
  23. package/lib/cjs/polyface/IndexedPolyfaceVisitor.js +2 -1
  24. package/lib/cjs/polyface/IndexedPolyfaceVisitor.js.map +1 -1
  25. package/lib/cjs/polyface/Polyface.d.ts +10 -2
  26. package/lib/cjs/polyface/Polyface.d.ts.map +1 -1
  27. package/lib/cjs/polyface/Polyface.js +29 -17
  28. package/lib/cjs/polyface/Polyface.js.map +1 -1
  29. package/lib/cjs/polyface/PolyfaceData.d.ts +10 -9
  30. package/lib/cjs/polyface/PolyfaceData.d.ts.map +1 -1
  31. package/lib/cjs/polyface/PolyfaceData.js +17 -2
  32. package/lib/cjs/polyface/PolyfaceData.js.map +1 -1
  33. package/lib/cjs/polyface/PolyfaceQuery.d.ts +321 -270
  34. package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
  35. package/lib/cjs/polyface/PolyfaceQuery.js +405 -351
  36. package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
  37. package/lib/cjs/serialization/BGFBReader.d.ts +18 -20
  38. package/lib/cjs/serialization/BGFBReader.d.ts.map +1 -1
  39. package/lib/cjs/serialization/BGFBReader.js +119 -84
  40. package/lib/cjs/serialization/BGFBReader.js.map +1 -1
  41. package/lib/cjs/serialization/BGFBWriter.d.ts +1 -1
  42. package/lib/cjs/serialization/BGFBWriter.d.ts.map +1 -1
  43. package/lib/cjs/serialization/BGFBWriter.js +10 -15
  44. package/lib/cjs/serialization/BGFBWriter.js.map +1 -1
  45. package/lib/cjs/serialization/IModelJsonSchema.d.ts +52 -7
  46. package/lib/cjs/serialization/IModelJsonSchema.d.ts.map +1 -1
  47. package/lib/cjs/serialization/IModelJsonSchema.js +26 -78
  48. package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
  49. package/lib/cjs/serialization/SerializationHelpers.d.ts +17 -0
  50. package/lib/cjs/serialization/SerializationHelpers.d.ts.map +1 -1
  51. package/lib/cjs/serialization/SerializationHelpers.js +85 -0
  52. package/lib/cjs/serialization/SerializationHelpers.js.map +1 -1
  53. package/lib/cjs/solid/Sphere.d.ts +1 -0
  54. package/lib/cjs/solid/Sphere.d.ts.map +1 -1
  55. package/lib/cjs/solid/Sphere.js +4 -2
  56. package/lib/cjs/solid/Sphere.js.map +1 -1
  57. package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +7 -1
  58. package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
  59. package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js +20 -4
  60. package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
  61. package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js +1 -1
  62. package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
  63. package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js +1 -1
  64. package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
  65. package/lib/esm/geometry3d/Point3dVector3d.d.ts +3 -3
  66. package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
  67. package/lib/esm/geometry3d/Point3dVector3d.js +4 -3
  68. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  69. package/lib/esm/geometry3d/PointHelpers.d.ts +7 -0
  70. package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
  71. package/lib/esm/geometry3d/PointHelpers.js +19 -0
  72. package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
  73. package/lib/esm/polyface/AuxData.d.ts +18 -10
  74. package/lib/esm/polyface/AuxData.d.ts.map +1 -1
  75. package/lib/esm/polyface/AuxData.js +24 -12
  76. package/lib/esm/polyface/AuxData.js.map +1 -1
  77. package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts +2 -1
  78. package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
  79. package/lib/esm/polyface/IndexedPolyfaceVisitor.js +2 -1
  80. package/lib/esm/polyface/IndexedPolyfaceVisitor.js.map +1 -1
  81. package/lib/esm/polyface/Polyface.d.ts +10 -2
  82. package/lib/esm/polyface/Polyface.d.ts.map +1 -1
  83. package/lib/esm/polyface/Polyface.js +29 -17
  84. package/lib/esm/polyface/Polyface.js.map +1 -1
  85. package/lib/esm/polyface/PolyfaceData.d.ts +10 -9
  86. package/lib/esm/polyface/PolyfaceData.d.ts.map +1 -1
  87. package/lib/esm/polyface/PolyfaceData.js +17 -2
  88. package/lib/esm/polyface/PolyfaceData.js.map +1 -1
  89. package/lib/esm/polyface/PolyfaceQuery.d.ts +321 -270
  90. package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
  91. package/lib/esm/polyface/PolyfaceQuery.js +405 -351
  92. package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
  93. package/lib/esm/serialization/BGFBReader.d.ts +18 -20
  94. package/lib/esm/serialization/BGFBReader.d.ts.map +1 -1
  95. package/lib/esm/serialization/BGFBReader.js +119 -84
  96. package/lib/esm/serialization/BGFBReader.js.map +1 -1
  97. package/lib/esm/serialization/BGFBWriter.d.ts +1 -1
  98. package/lib/esm/serialization/BGFBWriter.d.ts.map +1 -1
  99. package/lib/esm/serialization/BGFBWriter.js +10 -15
  100. package/lib/esm/serialization/BGFBWriter.js.map +1 -1
  101. package/lib/esm/serialization/IModelJsonSchema.d.ts +52 -7
  102. package/lib/esm/serialization/IModelJsonSchema.d.ts.map +1 -1
  103. package/lib/esm/serialization/IModelJsonSchema.js +26 -78
  104. package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
  105. package/lib/esm/serialization/SerializationHelpers.d.ts +17 -0
  106. package/lib/esm/serialization/SerializationHelpers.d.ts.map +1 -1
  107. package/lib/esm/serialization/SerializationHelpers.js +85 -0
  108. package/lib/esm/serialization/SerializationHelpers.js.map +1 -1
  109. package/lib/esm/solid/Sphere.d.ts +1 -0
  110. package/lib/esm/solid/Sphere.d.ts.map +1 -1
  111. package/lib/esm/solid/Sphere.js +4 -2
  112. package/lib/esm/solid/Sphere.js.map +1 -1
  113. package/package.json +3 -3
@@ -9,6 +9,7 @@ exports.PolyfaceQuery = exports.DuplicateFacetClusterSelector = exports.OffsetMe
9
9
  * @module Polyface
10
10
  */
11
11
  /* eslint-disable @typescript-eslint/naming-convention, no-empty */
12
+ // cspell:word internaldocs
12
13
  const CurveCollection_1 = require("../curve/CurveCollection");
13
14
  const CurveOps_1 = require("../curve/CurveOps");
14
15
  const MultiChainCollector_1 = require("../curve/internalContexts/MultiChainCollector");
@@ -51,8 +52,7 @@ const RangeLengthData_1 = require("./RangeLengthData");
51
52
  * @public
52
53
  */
53
54
  class SweepLineStringToFacetsOptions {
54
- /** constructor -- captures fully-checked parameters from static create method.
55
- */
55
+ /** Constructor. Captures fully-checked parameters from static create method. */
56
56
  constructor(vectorToEye, sideAngle, assembleChains, collectOnForwardFacets, collectOnSideFacets, collectOnRearFacets) {
57
57
  this.vectorToEye = vectorToEye;
58
58
  this.sideAngle = sideAngle;
@@ -61,20 +61,27 @@ class SweepLineStringToFacetsOptions {
61
61
  this.collectOnSideFacets = collectOnSideFacets;
62
62
  this.collectOnRearFacets = collectOnRearFacets;
63
63
  }
64
- /** Create an options structure.
65
- * * Default vectorToEye is positive Z
66
- * * Default sideAngle has radians value Geometry.smallAngleRadians
67
- * * Default assembleChains is true
68
- * * Default collectOnForwardFacets, collectOnSideFacets, collectOnRearFacets are all true.
64
+ /**
65
+ * Create an options structure.
66
+ * * Default `vectorToEye` is (0,0,1).
67
+ * * Default `sideAngle` is `Geometry.smallAngleRadians`.
68
+ * * Default `assembleChains` is `true`.
69
+ * * Default `collectOnForwardFacets`, `collectOnSideFacets`, `collectOnRearFacets` are all `true`.
69
70
  */
70
71
  static create(vectorToEye, sideAngle, assembleChains, collectOnForwardFacets, collectOnSideFacets, collectOnRearFacets) {
71
72
  return new SweepLineStringToFacetsOptions(vectorToEye === undefined ? Point3dVector3d_1.Vector3d.unitZ() : vectorToEye.clone(), sideAngle === undefined ? Angle_1.Angle.createRadians(Geometry_1.Geometry.smallAngleRadians) : sideAngle.clone(), Geometry_1.Geometry.resolveValue(assembleChains, true), Geometry_1.Geometry.resolveValue(collectOnForwardFacets, true), Geometry_1.Geometry.resolveValue(collectOnSideFacets, true), Geometry_1.Geometry.resolveValue(collectOnRearFacets, true));
72
73
  }
73
- /** Return true if all outputs are requested */
74
- get collectAll() { return this.collectOnForwardFacets === true && this.collectOnRearFacets === true && this.collectOnRearFacets === true; }
75
- /** Decide if the instance flags accept this facet.
76
- * * Facets whose facet normal have positive, zero, or negative dot product with the vectorToEye are forward, side, and rear.
77
- * * Undefined facet normal returns false
74
+ /** Return `true` if all outputs are requested. */
75
+ get collectAll() {
76
+ return this.collectOnForwardFacets === true &&
77
+ this.collectOnSideFacets === true &&
78
+ this.collectOnRearFacets === true;
79
+ }
80
+ /**
81
+ * Decide if the instance collector flags accept a facet with the given normal.
82
+ * * A facet whose facet normal has a positive, zero, or negative dot product with `vectorToEye` is classified
83
+ * as forward, side, or rear, respectively.
84
+ * * `undefined` facet normal returns `false`.
78
85
  */
79
86
  collectFromThisFacetNormal(facetNormal) {
80
87
  if (facetNormal === undefined)
@@ -89,22 +96,27 @@ exports.SweepLineStringToFacetsOptions = SweepLineStringToFacetsOptions;
89
96
  /**
90
97
  * Options carrier for [[PolyfaceQuery.cloneOffset]].
91
98
  * * Default options are strongly recommended.
92
- * * The option most likely to be changed is chamferTurnAngle
99
+ * * The option most likely to be changed is `chamferAngleBetweenNormals`.
93
100
  * @public
94
101
  */
95
102
  class OffsetMeshOptions {
96
- /** Constructor -- CAPTURE parameters ... */
97
- constructor(smoothSingleAngleBetweenNormals = Angle_1.Angle.createDegrees(25), smoothAccumulatedAngleBetweenNormals = Angle_1.Angle.createDegrees(60), chamferTurnAngle = Angle_1.Angle.createDegrees(90)) {
103
+ /** Constructor -- CAPTURE parameters. */
104
+ constructor(smoothSingleAngleBetweenNormals = Angle_1.Angle.createDegrees(25), smoothAccumulatedAngleBetweenNormals = Angle_1.Angle.createDegrees(60), chamferAngleBetweenNormals = Angle_1.Angle.createDegrees(90)) {
98
105
  this.smoothSingleAngleBetweenNormals = smoothSingleAngleBetweenNormals.clone();
99
106
  this.smoothAccumulatedAngleBetweenNormals = smoothAccumulatedAngleBetweenNormals.clone();
100
- this.chamferAngleBetweenNormals = chamferTurnAngle.clone();
107
+ this.chamferAngleBetweenNormals = chamferAngleBetweenNormals.clone();
101
108
  }
102
- /** construct and return an OffsetMeshOptions with given parameters.
109
+ /**
110
+ * Construct and return an `OffsetMeshOptions` with given parameters.
103
111
  * * Angles are forced to minimum values.
104
112
  * * Clones of the angles are given to the constructor.
105
- * @param smoothSingleRadiansBetweenNormals an angle larger than this (between facets) is considered a sharp edge
106
- * @param smoothAccumulatedAngleBetweenNormals angles that sum to this much may be consolidated for average normal
107
- * @param chamferTurnAngleBetweenNormals when facets meet with larger angle, a chamfer edge may be added if the angle between facet normals is larger than this.
113
+ * @param smoothSingleRadiansBetweenNormals an angle larger than this (between facets) is considered a sharp edge.
114
+ * Default value is `25` degrees.
115
+ * @param smoothAccumulatedAngleBetweenNormals angles that sum to this much may be consolidated for average normal.
116
+ * Default value is `60` degrees.
117
+ * @param chamferTurnAngleBetweenNormals when facets meet and the turn angle (i.e., angle between facet normals)
118
+ * is larger than `chamferTurnAngleBetweenNormals`, a chamfer edge may be added to prevent offset mesh from having
119
+ * facets that extend out too far away from the source mesh. Default value is `120` degrees.
108
120
  */
109
121
  static create(smoothSingleAngleBetweenNormals = Angle_1.Angle.createDegrees(25), smoothAccumulatedAngleBetweenNormals = Angle_1.Angle.createDegrees(60), chamferTurnAngleBetweenNormals = Angle_1.Angle.createDegrees(120)) {
110
122
  const mySmoothSingleRadiansBetweenNormals = smoothSingleAngleBetweenNormals.clone();
@@ -121,25 +133,27 @@ class OffsetMeshOptions {
121
133
  }
122
134
  exports.OffsetMeshOptions = OffsetMeshOptions;
123
135
  /**
124
- * Enumeration of cases for retaining facets among duplicates
136
+ * Enumeration of cases for retaining facets among duplicates.
125
137
  * @public
126
138
  */
127
139
  var DuplicateFacetClusterSelector;
128
140
  (function (DuplicateFacetClusterSelector) {
129
- /** retain none of the duplicates */
141
+ /** Retain none of the duplicates. */
130
142
  DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectNone"] = 0] = "SelectNone";
131
- /** retain any one member among duplicates */
143
+ /** Retain any one member among duplicates. */
132
144
  DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectAny"] = 1] = "SelectAny";
133
- /** retain all members among duplicates */
145
+ /** Retain all members among duplicates. */
134
146
  DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectAll"] = 2] = "SelectAll";
135
- /** retain one from any cluster with an odd number of faces */
147
+ /** Retain one from any cluster with an odd number of faces. */
136
148
  DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectOneByParity"] = 3] = "SelectOneByParity";
137
149
  })(DuplicateFacetClusterSelector || (exports.DuplicateFacetClusterSelector = DuplicateFacetClusterSelector = {}));
138
- /** PolyfaceQuery is a static class whose methods implement queries on a polyface or polyface visitor provided as a parameter to each method.
150
+ /**
151
+ * PolyfaceQuery is a static class whose methods implement queries on a `Polyface` or `PolyfaceVisitor` provided as a
152
+ * parameter to each method.
139
153
  * @public
140
154
  */
141
155
  class PolyfaceQuery {
142
- /** copy the points from a visitor into a Linestring3d in a Loop object */
156
+ /** Copy the points from a visitor into a linestring loop. */
143
157
  static visitorToLoop(visitor) {
144
158
  const ls = LineString3d_1.LineString3d.createPoints(visitor.point.getPoint3dArray());
145
159
  return Loop_1.Loop.create(ls);
@@ -154,11 +168,15 @@ class PolyfaceQuery {
154
168
  }
155
169
  return result;
156
170
  }
157
- /** Return the sum of all facet areas.
158
- * @param vectorToEye compute sum of *signed* facet areas projected to a view plane perpendicular to this vector
159
- */
171
+ /**
172
+ * Sum all facet areas.
173
+ * @param source polyface or visitor.
174
+ * @param vectorToEye compute sum of (signed) facet areas projected to a view plane perpendicular to `vectorToEye`.
175
+ * If `vectorToEye` is not provided, actual facet areas are calculated (without any projection).
176
+ * @returns the sum of all facet areas. Return 0 if `source` is `undefined`.
177
+ */
160
178
  static sumFacetAreas(source, vectorToEye) {
161
- let s = 0;
179
+ let sum = 0;
162
180
  if (source !== undefined) {
163
181
  if (source instanceof Polyface_1.Polyface)
164
182
  return PolyfaceQuery.sumFacetAreas(source.createVisitor(1), vectorToEye);
@@ -167,20 +185,21 @@ class PolyfaceQuery {
167
185
  unitVectorToEye = vectorToEye.normalize();
168
186
  source.reset();
169
187
  while (source.moveToNextFacet()) {
170
- const scaledNormal = PolygonOps_1.PolygonOps.areaNormal(source.point.getPoint3dArray());
171
- s += unitVectorToEye ? scaledNormal.dotProduct(unitVectorToEye) : scaledNormal.magnitude();
188
+ const areaNormal = PolygonOps_1.PolygonOps.areaNormal(source.point.getPoint3dArray());
189
+ sum += unitVectorToEye ? areaNormal.dotProduct(unitVectorToEye) : areaNormal.magnitude();
172
190
  }
173
191
  }
174
- return s;
192
+ return sum;
175
193
  }
176
- /** sum volumes of tetrahedra from origin to all facets.
177
- * * if origin is omitted, the first point encountered (by the visitor) is used as origin.
194
+ /**
195
+ * Sum volumes of tetrahedra from origin to all facets.
196
+ * * If origin is `undefined`, the first point encountered (by the visitor) is used as origin.
178
197
  * * If the mesh is closed, this sum is the volume.
179
- * * If the mesh is not closed, this sum is the volume of a mesh with various additional facets
180
- * from the origin to facets.
181
- */
198
+ * * If the mesh is not closed, this sum is the volume of a mesh with various additional facets from the origin
199
+ * to facets.
200
+ */
182
201
  static sumTetrahedralVolumes(source, origin) {
183
- let s = 0;
202
+ let sum = 0;
184
203
  if (source instanceof Polyface_1.Polyface)
185
204
  return PolyfaceQuery.sumTetrahedralVolumes(source.createVisitor(0), origin);
186
205
  let myOrigin = origin;
@@ -195,17 +214,18 @@ class PolyfaceQuery {
195
214
  for (let i = 1; i + 1 < source.point.length; i++) {
196
215
  source.point.getPoint3dAtUncheckedPointIndex(i, targetA);
197
216
  source.point.getPoint3dAtUncheckedPointIndex(i + 1, targetB);
198
- s += myOrigin.tripleProductToPoints(facetOrigin, targetA, targetB);
217
+ sum += myOrigin.tripleProductToPoints(facetOrigin, targetA, targetB);
199
218
  }
200
219
  }
201
- return s / 6.0;
220
+ return sum / 6.0;
202
221
  }
203
- /** sum (signed) volumes between facets and a plane.
222
+ /**
223
+ * Sum (signed) volumes between facets and a plane.
204
224
  * Return a structure with multiple sums:
205
225
  * * volume = the sum of (signed) volumes between facets and the plane.
206
- * * positiveAreaMomentData, negativeProjectedFacetAreaMoments = moment data with centroid, area, and second moments with respect to the centroid.
207
- *
208
- */
226
+ * * positiveProjectedFacetAreaMoments, negativeProjectedFacetAreaMoments = moment data with centroid, area, and second
227
+ * moments with respect to the centroid.
228
+ */
209
229
  static sumVolumeBetweenFacetsAndPlane(source, plane) {
210
230
  if (source instanceof Polyface_1.Polyface)
211
231
  return PolyfaceQuery.sumVolumeBetweenFacetsAndPlane(source.createVisitor(0), plane);
@@ -223,18 +243,18 @@ class PolyfaceQuery {
223
243
  const singleFacetProducts = Matrix4d_1.Matrix4d.createZero();
224
244
  const projectToPlane = plane.getProjectionToPlane();
225
245
  source.reset();
226
- // For each facet ..
227
- // Form triangles from facet origin to each far edge.
228
- // Sum signed area and volume contributions
229
- // each "projectedArea" contribution is twice the area of a triangle.
230
- // each volume contribution is 3 times the actual volume -- (1/3) of the altitude sums was the centroid altitude.
246
+ // For each facet:
247
+ // - form triangles from facet origin to each far edge.
248
+ // - sum signed area and volume contributions.
249
+ // each projected area contribution is twice the area of a triangle.
250
+ // each volume contribution is 3 times the actual volume -- (1/3) of the altitude sums was the centroid altitude.
231
251
  while (source.moveToNextFacet()) {
232
252
  source.point.getPoint3dAtUncheckedPointIndex(0, facetOrigin);
233
253
  h0 = plane.altitude(facetOrigin);
234
254
  singleFacetArea = 0;
235
255
  // within a single facets, the singleFacetArea sum is accumulated with signs of individual triangles.
236
- // For a non-convex facet, this can be a mixture of positive and negative areas.
237
- // The absoluteProjectedAreaSum contribution is forced positive after the sum for the facet.
256
+ // for a non-convex facet, this can be a mixture of positive and negative areas.
257
+ // the absoluteProjectedAreaSum contribution is forced positive after the sum for the facet.
238
258
  for (let i = 1; i + 1 < source.point.length; i++) {
239
259
  source.point.getPoint3dAtUncheckedPointIndex(i, targetA);
240
260
  source.point.getPoint3dAtUncheckedPointIndex(i + 1, targetB);
@@ -265,7 +285,7 @@ class PolyfaceQuery {
265
285
  negativeProjectedFacetAreaMoments: negativeAreaMoments,
266
286
  };
267
287
  }
268
- /** Return the inertia products [xx,xy,xz,xw, yw, etc] integrated over all all facets, as viewed from origin. */
288
+ /** Return the inertia products [xx,xy,xz,xw,yw, etc] integrated over all all facets as viewed from origin. */
269
289
  static sumFacetSecondAreaMomentProducts(source, origin) {
270
290
  if (source instanceof Polyface_1.Polyface)
271
291
  return PolyfaceQuery.sumFacetSecondAreaMomentProducts(source.createVisitor(0), origin);
@@ -276,7 +296,7 @@ class PolyfaceQuery {
276
296
  }
277
297
  return products;
278
298
  }
279
- /** Return the inertia products [xx,xy,xz,xw, yw, etc] integrated over all tetrahedral volumes from origin */
299
+ /** Return the inertia products [xx,xy,xz,xw,yw, etc] integrated over all tetrahedral volumes from origin. */
280
300
  static sumFacetSecondVolumeMomentProducts(source, origin) {
281
301
  if (source instanceof Polyface_1.Polyface)
282
302
  return PolyfaceQuery.sumFacetSecondVolumeMomentProducts(source.createVisitor(0), origin);
@@ -287,10 +307,11 @@ class PolyfaceQuery {
287
307
  }
288
308
  return products;
289
309
  }
290
- /** Compute area moments for the mesh. In the returned MomentData:
291
- * * origin is the centroid.
292
- * * localToWorldMap has the origin and principal directions
293
- * * radiiOfGyration radii for rotation around the x,y,z axes.
310
+ /**
311
+ * Compute area moments for the mesh. In the returned `MomentData`:
312
+ * * `origin` is the centroid.
313
+ * * `localToWorldMap` has the origin and principal directions.
314
+ * * `radiiOfGyration` radii for rotation around the x,y,z axes.
294
315
  */
295
316
  static computePrincipalAreaMoments(source) {
296
317
  const origin = source.data.getPoint(0);
@@ -299,12 +320,13 @@ class PolyfaceQuery {
299
320
  const inertiaProducts = PolyfaceQuery.sumFacetSecondAreaMomentProducts(source, origin);
300
321
  return MomentData_1.MomentData.inertiaProductsToPrincipalAxes(origin, inertiaProducts);
301
322
  }
302
- /** Compute area moments for the mesh. In the returned MomentData:
303
- * * origin is the centroid.
304
- * * localToWorldMap has the origin and principal directions
305
- * * radiiOfGyration radii for rotation around the x,y,z axes.
323
+ /**
324
+ * Compute area moments for the mesh. In the returned MomentData:
325
+ * * `origin` is the centroid.
326
+ * * `localToWorldMap` has the origin and principal directions.
327
+ * * `radiiOfGyration` radii for rotation around the x,y,z axes.
306
328
  * * The result is only valid if the mesh is closed.
307
- * * There is no test for closure. Use `PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface)` to test for closure.
329
+ * * There is no test for closure. Use `PolyfaceQuery.isPolyfaceClosedByEdgePairing(polyface)` to test for closure.
308
330
  */
309
331
  static computePrincipalVolumeMoments(source) {
310
332
  const origin = source.data.getPoint(0);
@@ -313,8 +335,10 @@ class PolyfaceQuery {
313
335
  const inertiaProducts = PolyfaceQuery.sumFacetSecondVolumeMomentProducts(source, origin);
314
336
  return MomentData_1.MomentData.inertiaProductsToPrincipalAxes(origin, inertiaProducts);
315
337
  }
316
- /** Determine whether all facets are convex.
317
- * @param source mesh to examine
338
+ /**
339
+ * Determine whether all facets are convex.
340
+ * @param source polyface or visitor.
341
+ * @returns `true` if all facets are convex; `false` otherwise.
318
342
  */
319
343
  static areFacetsConvex(source) {
320
344
  if (source instanceof Polyface_1.Polyface)
@@ -330,56 +354,50 @@ class PolyfaceQuery {
330
354
  return true;
331
355
  }
332
356
  /**
333
- * Test for convex volume by dihedral angle tests on all edges.
334
- * * This tests if all dihedral angles are positive.
335
- * * In a closed solid, this is a strong test for overall convexity.
336
- * * With `ignoreBoundaries` true, this may be a useful test when all the facets are in a single edge-connected component, such as a pyramid with no underside.
337
- * * It is not a correct test if there are multiple, disjoint components.
338
- * * Take the above-mentioned pyramid with no underside.
339
- * * Within the same mesh, have a second pyramid placed to the side, still facing upward.
340
- * * The angles will pass the dihedral convexity test, but the composite thing surely is not convex.
341
- * @param source mesh to examine
342
- * @param ignoreBoundaries if true, ignore simple boundary edges, i.e. allow unclosed meshes.
343
- * @returns true if the mesh is closed and has all dihedral angles (angle across edge) positive
357
+ * Compute a number summarizing the dihedral angles in the mesh.
358
+ * * A dihedral angle is the signed angle between adjacent facets' normals. This angle is positive when the cross
359
+ * product `normalA x normalB` has the same direction as facetA's traversal of the facets' shared edge.
360
+ * @param source mesh.
361
+ * @param ignoreBoundaries if `true` ignore simple boundary edges, i.e., allow unclosed meshes. Default is `false`.
362
+ * See [[isConvexByDihedralAngleCount]] for comments about passing true when there are multiple
363
+ * connected components.
364
+ * * Return `0` if all dihedral angles are zero (and `ignoreBoundaries === true`). The mesh is planar.
365
+ * * Otherwise, return `1` if all dihedral angles are non-negative. The mesh probably encloses a convex volume and
366
+ * has outward normals.
367
+ * * Otherwise, return `-1` if all dihedral angles are non-positive. The mesh probably encloses a convex volume and
368
+ * has inward normals.
369
+ * * Otherwise, return `-2`. Also return `-2` if a non-manifold condition was detected, or a facet normal could not
370
+ * be computed. A non-manifold condition is a positive-length edge adjacent to more than 2 facets or (if
371
+ * `ignoreBoundaries` is false) adjacent to exactly one facet.
344
372
  */
345
- static isConvexByDihedralAngleCount(source, ignoreBoundaries = false) {
346
- return this.dihedralAngleSummary(source, ignoreBoundaries) > 0;
347
- }
348
- /**
349
- * Compute a number summarizing the dihedral angles in the mesh.
350
- * @see [[isConvexByDihedralAngleCount]] for comments about ignoreBoundaries===true when there are multiple connected components.
351
- * @param source mesh to examine
352
- * @param ignoreBoundaries if true, ignore simple boundary edges, i.e. allow unclosed meshes.
353
- * @returns a number summarizing the dihedral angles in the mesh.
354
- * * Return 1 if all angles are positive or planar. The mesh is probably convex with outward normals.
355
- * * Return -1 if all angles are negative or planar. The mesh is probably convex with inward normals.
356
- * * Return 0 if
357
- * * angles area mixed
358
- * * any edge has other than 1 incident facet or more than 2 incident facets.
359
- * * (but null edges are permitted -- These occur naturally at edges of quads at north or south pole)
360
- */
361
373
  static dihedralAngleSummary(source, ignoreBoundaries = false) {
374
+ // more info can be found at geometry/internaldocs/Polyface.md
362
375
  const edges = new IndexedEdgeMatcher_1.IndexedEdgeMatcher();
363
376
  const visitor = source.createVisitor(1);
364
377
  visitor.reset();
378
+ // find centroid normals of all facets
365
379
  const centroidNormal = [];
366
380
  let normalCounter = 0;
367
381
  while (visitor.moveToNextFacet()) {
368
382
  const numEdges = visitor.pointCount - 1;
369
383
  const normal = PolygonOps_1.PolygonOps.centroidAreaNormal(visitor.point);
370
384
  if (normal === undefined)
371
- return 0;
385
+ return -2;
372
386
  centroidNormal.push(normal);
373
387
  for (let i = 0; i < numEdges; i++) {
374
388
  edges.addEdge(visitor.clientPointIndex(i), visitor.clientPointIndex(i + 1), normalCounter);
375
389
  }
376
390
  normalCounter++;
377
391
  }
392
+ // find "manifold clusters" and "bad clusters"
393
+ // manifold clusters are edges adjacent to 2 facets
394
+ // bad clusters are edges adjacent to more than 2 facets or (if ignoreBoundaries is false) adjacent to 1 facet
378
395
  const badClusters = [];
379
396
  const manifoldClusters = [];
380
397
  edges.sortAndCollectClusters(manifoldClusters, ignoreBoundaries ? undefined : badClusters, undefined, badClusters);
381
398
  if (badClusters.length > 0)
382
- return 0;
399
+ return -2;
400
+ // find angle between facet centroid normals (dihedral angles)
383
401
  let numPositive = 0;
384
402
  let numPlanar = 0;
385
403
  let numNegative = 0;
@@ -387,8 +405,7 @@ class PolyfaceQuery {
387
405
  for (const cluster of manifoldClusters) {
388
406
  const sideA = cluster[0];
389
407
  const sideB = cluster[1];
390
- if (sideA instanceof IndexedEdgeMatcher_1.SortableEdge
391
- && sideB instanceof IndexedEdgeMatcher_1.SortableEdge
408
+ if (sideA instanceof IndexedEdgeMatcher_1.SortableEdge && sideB instanceof IndexedEdgeMatcher_1.SortableEdge
392
409
  && source.data.point.vectorIndexIndex(sideA.vertexIndexA, sideA.vertexIndexB, edgeVector)) {
393
410
  const dihedralAngle = centroidNormal[sideA.facetIndex].direction.signedAngleTo(centroidNormal[sideB.facetIndex].direction, edgeVector);
394
411
  if (dihedralAngle.isAlmostZero)
@@ -399,26 +416,40 @@ class PolyfaceQuery {
399
416
  numNegative++;
400
417
  }
401
418
  }
419
+ // categorize the mesh
402
420
  if (numPositive > 0 && numNegative === 0)
403
421
  return 1;
404
422
  if (numNegative > 0 && numPositive === 0)
405
423
  return -1;
406
- // problem case: if all edges have zero dihedral angle, record it as convex.
407
- if (numPlanar > 0 && numPositive === 0 && numNegative === 0)
408
- return 1;
409
- return 0;
424
+ if (numPlanar > 0 && numPositive === 0 && numNegative === 0) // planar mesh
425
+ return 0;
426
+ return -2;
410
427
  }
411
428
  /**
412
- * Test if the facets in `source` occur in perfectly mated pairs, as is required for a closed manifold volume.
413
- */
414
- static isPolyfaceClosedByEdgePairing(source) {
415
- return this.isPolyfaceManifold(source, false);
429
+ * Test for convex volume by dihedral angle tests on all edges.
430
+ * * This tests if all dihedral angles of the mesh are positive.
431
+ * * In a closed solid, this is a strong test for overall mesh convexity with outward facing normals.
432
+ * * See [[dihedralAngleSummary]] for the definition of "dihedral angle".
433
+ * * With `ignoreBoundaries` true, this may be a useful test when all the facets are in a single edge-connected
434
+ * component, such as a pyramid with no underside.
435
+ * * It is not a correct test if there are multiple, disjoint components.
436
+ * * Take the above-mentioned pyramid with no underside.
437
+ * * Within the same mesh, have a second pyramid placed to the side, still facing upward.
438
+ * * The angles will pass the dihedral convexity test, but the composite thing surely is not convex.
439
+ * @param source mesh.
440
+ * @param ignoreBoundaries if `true` ignore simple boundary edges, i.e., allow unclosed meshes. Default is `false`.
441
+ * @returns true if all dihedral angles of the mesh are positive.
442
+ */
443
+ static isConvexByDihedralAngleCount(source, ignoreBoundaries = false) {
444
+ return this.dihedralAngleSummary(source, ignoreBoundaries) > 0;
416
445
  }
417
- /** Test edges pairing in `source` mesh.
418
- * * for `allowSimpleBoundaries === false` true return means this is a closed 2-manifold surface
419
- * * for `allowSimpleBoundaries === true` true means this is a 2-manifold surface which may have boundary, but is still properly matched internally.
420
- * * Any edge with 3 or more incident facets triggers `false` return.
421
- * * Any edge with 2 incident facets in the same direction triggers a `false` return.
446
+ /**
447
+ * Test edges pairing in `source` mesh.
448
+ * * For `allowSimpleBoundaries === false` true return means this is a closed 2-manifold surface.
449
+ * * For `allowSimpleBoundaries === true` true means this is a 2-manifold surface which may have boundary, but is
450
+ * still properly matched internally.
451
+ * * Any edge with 3 or more adjacent facets triggers `false` return.
452
+ * * Any edge with 2 adjacent facets in the same direction triggers a `false` return.
422
453
  */
423
454
  static isPolyfaceManifold(source, allowSimpleBoundaries = false) {
424
455
  const edges = new IndexedEdgeMatcher_1.IndexedEdgeMatcher();
@@ -434,44 +465,18 @@ class PolyfaceQuery {
434
465
  edges.sortAndCollectClusters(undefined, allowSimpleBoundaries ? undefined : badClusters, undefined, badClusters);
435
466
  return badClusters.length === 0;
436
467
  }
437
- /**
438
- * construct a CurveCollection containing boundary edges.
439
- * * each edge is a LineSegment3d
440
- * @param source polyface or visitor
441
- * @param includeTypical true to in include typical boundary edges with a single incident facet
442
- * @param includeMismatch true to include edges with more than 2 incident facets
443
- * @param includeNull true to include edges with identical start and end vertex indices.
444
- */
445
- static boundaryEdges(source, includeTypical = true, includeMismatch = true, includeNull = true) {
446
- const result = new CurveCollection_1.BagOfCurves();
447
- const announceEdge = (pointA, pointB, _indexA, _indexB, _readIndex) => {
448
- result.tryAddChild(LineSegment3d_1.LineSegment3d.create(pointA, pointB));
449
- };
450
- PolyfaceQuery.announceBoundaryEdges(source, announceEdge, includeTypical, includeMismatch, includeNull);
451
- if (result.children.length === 0)
452
- return undefined;
453
- return result;
454
- }
455
- /**
456
- * Collect boundary edges.
457
- * * Return the edges as the simplest collection of chains of line segments.
458
- * @param source facets
459
- * @param includeTypical true to in include typical boundary edges with a single incident facet
460
- * @param includeMismatch true to include edges with more than 2 incident facets
461
- * @param includeNull true to include edges with identical start and end vertex indices.
462
- */
463
- static collectBoundaryEdges(source, includeTypical = true, includeMismatch = true, includeNull = true) {
464
- const collector = new MultiChainCollector_1.MultiChainCollector(Geometry_1.Geometry.smallMetricDistance, Geometry_1.Geometry.smallMetricDistance);
465
- PolyfaceQuery.announceBoundaryEdges(source, (ptA, ptB) => collector.captureCurve(LineSegment3d_1.LineSegment3d.create(ptA, ptB)), includeTypical, includeMismatch, includeNull);
466
- return collector.grabResult(true);
468
+ /** Test if the facets in `source` occur in perfectly mated pairs, as is required for a closed manifold volume. */
469
+ static isPolyfaceClosedByEdgePairing(source) {
470
+ return this.isPolyfaceManifold(source, false);
467
471
  }
468
472
  /**
469
473
  * Test if the facets in `source` occur in perfectly mated pairs, as is required for a closed manifold volume.
470
474
  * If not, extract the boundary edges as lines.
471
- * @param source polyface or visitor
472
- * @param announceEdge function to be called with each boundary edge. The announcement is start and end points, start and end indices, and facet index.
473
- * @param includeTypical true to announce typical boundary edges with a single incident facet
474
- * @param includeMismatch true to announce edges with more than 2 incident facets
475
+ * @param source polyface or visitor.
476
+ * @param announceEdge function to be called with each boundary edge. The announcement is start and end points,
477
+ * start and end indices, and facet index.
478
+ * @param includeTypical true to announce typical boundary edges with a single adjacent facet.
479
+ * @param includeMismatch true to announce edges with more than 2 adjacent facets.
475
480
  * @param includeNull true to announce edges with identical start and end vertex indices.
476
481
  */
477
482
  static announceBoundaryEdges(source, announceEdge, includeTypical = true, includeMismatch = true, includeNull = true) {
@@ -487,17 +492,17 @@ class PolyfaceQuery {
487
492
  edges.addEdge(visitor.clientPointIndex(i), visitor.clientPointIndex(i + 1), visitor.currentReadIndex());
488
493
  }
489
494
  }
490
- const bad1 = [];
491
- const bad2 = [];
492
- const bad0 = [];
493
- edges.sortAndCollectClusters(undefined, bad1, bad0, bad2);
495
+ const boundaryEdges = [];
496
+ const nullEdges = [];
497
+ const allOtherEdges = [];
498
+ edges.sortAndCollectClusters(undefined, boundaryEdges, nullEdges, allOtherEdges);
494
499
  const badList = [];
495
- if (includeTypical && bad1.length > 0)
496
- badList.push(bad1);
497
- if (includeMismatch && bad2.length > 0)
498
- badList.push(bad2);
499
- if (includeNull && bad0.length > 0)
500
- badList.push(bad0);
500
+ if (includeTypical && boundaryEdges.length > 0)
501
+ badList.push(boundaryEdges);
502
+ if (includeNull && nullEdges.length > 0)
503
+ badList.push(nullEdges);
504
+ if (includeMismatch && allOtherEdges.length > 0)
505
+ badList.push(allOtherEdges);
501
506
  if (badList.length === 0)
502
507
  return undefined;
503
508
  const sourcePolyface = visitor.clientPolyface();
@@ -514,12 +519,61 @@ class PolyfaceQuery {
514
519
  }
515
520
  }
516
521
  /**
517
- * Invoke the callback on each manifold edge whose adjacent facet normals form vectorToEye dot products with opposite sign.
522
+ * Construct a CurveCollection containing boundary edges.
523
+ * * Each edge is a LineSegment3d.
524
+ * @param source polyface or visitor.
525
+ * @param includeTypical true to in include typical boundary edges with a single adjacent facet.
526
+ * @param includeMismatch true to include edges with more than 2 adjacent facets.
527
+ * @param includeNull true to include edges with identical start and end vertex indices.
528
+ */
529
+ static boundaryEdges(source, includeTypical = true, includeMismatch = true, includeNull = true) {
530
+ const result = new CurveCollection_1.BagOfCurves();
531
+ const announceEdge = (pointA, pointB, _indexA, _indexB, _readIndex) => {
532
+ result.tryAddChild(LineSegment3d_1.LineSegment3d.create(pointA, pointB));
533
+ };
534
+ PolyfaceQuery.announceBoundaryEdges(source, announceEdge, includeTypical, includeMismatch, includeNull);
535
+ if (result.children.length === 0)
536
+ return undefined;
537
+ return result;
538
+ }
539
+ /**
540
+ * Collect boundary edges.
541
+ * * Return the edges as the simplest collection of chains of line segments.
542
+ * @param source polyface or visitor.
543
+ * @param includeTypical true to in include typical boundary edges with a single adjacent facet.
544
+ * @param includeMismatch true to include edges with more than 2 adjacent facets.
545
+ * @param includeNull true to include edges with identical start and end vertex indices.
546
+ */
547
+ static collectBoundaryEdges(source, includeTypical = true, includeMismatch = true, includeNull = true) {
548
+ const collector = new MultiChainCollector_1.MultiChainCollector(Geometry_1.Geometry.smallMetricDistance, Geometry_1.Geometry.smallMetricDistance);
549
+ PolyfaceQuery.announceBoundaryEdges(source, (ptA, ptB) => collector.captureCurve(LineSegment3d_1.LineSegment3d.create(ptA, ptB)), includeTypical, includeMismatch, includeNull);
550
+ return collector.grabResult(true);
551
+ }
552
+ /**
553
+ * Load all half edges from a mesh to an IndexedEdgeMatcher.
554
+ * @param polyface a mesh or a visitor assumed to have numWrap === 1.
555
+ */
556
+ static createIndexedEdges(polyface) {
557
+ if (polyface instanceof Polyface_1.Polyface)
558
+ return this.createIndexedEdges(polyface.createVisitor(1));
559
+ const edges = new IndexedEdgeMatcher_1.IndexedEdgeMatcher();
560
+ polyface.reset();
561
+ while (polyface.moveToNextFacet()) {
562
+ const numEdges = polyface.pointCount - 1;
563
+ for (let i = 0; i < numEdges; i++) {
564
+ edges.addEdge(polyface.clientPointIndex(i), polyface.clientPointIndex(i + 1), polyface.currentReadIndex());
565
+ }
566
+ }
567
+ return edges;
568
+ }
569
+ /**
570
+ * Invoke the callback on each manifold edge whose adjacent facet normals form vectorToEye dot products
571
+ * with opposite sign.
518
572
  * * The callback is not called on boundary edges.
519
- * @param source facets
520
- * @param announce callback function invoked on manifold silhouette edges
521
- * @param vectorToEye normal of plane in which to compute silhouette edges
522
- * @param sideAngle angular tolerance for perpendicularity test
573
+ * @param source polyface or visitor.
574
+ * @param announce callback function invoked on manifold silhouette edges.
575
+ * @param vectorToEye normal of plane in which to compute silhouette edges.
576
+ * @param sideAngle angular tolerance for perpendicularity test.
523
577
  */
524
578
  static announceSilhouetteEdges(source, announce, vectorToEye, sideAngle = Angle_1.Angle.createSmallAngle()) {
525
579
  if (source instanceof Polyface_1.Polyface)
@@ -569,16 +623,17 @@ class PolyfaceQuery {
569
623
  * Collect manifold edges whose adjacent facet normals form vectorToEye dot products with opposite sign.
570
624
  * * Does not return boundary edges.
571
625
  * * Return the edges as chains of line segments.
572
- * @param source facets
573
- * @param vectorToEye normal of plane in which to compute silhouette edges
574
- * @param sideAngle angular tolerance for perpendicularity test
626
+ * @param source polyface or visitor.
627
+ * @param vectorToEye normal of plane in which to compute silhouette edges.
628
+ * @param sideAngle angular tolerance for perpendicularity test.
575
629
  */
576
630
  static collectSilhouetteEdges(source, vectorToEye, sideAngle = Angle_1.Angle.createSmallAngle()) {
577
631
  const collector = new MultiChainCollector_1.MultiChainCollector(Geometry_1.Geometry.smallMetricDistance, Geometry_1.Geometry.smallMetricDistance);
578
632
  PolyfaceQuery.announceSilhouetteEdges(source, (ptA, ptB) => collector.captureCurve(LineSegment3d_1.LineSegment3d.create(ptA, ptB)), vectorToEye, sideAngle);
579
633
  return collector.grabResult(true);
580
634
  }
581
- /** Find segments (within the linestring) which project to facets.
635
+ /**
636
+ * Find segments (within the linestring) which project to facets.
582
637
  * * Announce each pair of linestring segment and on-facet segment through a callback.
583
638
  * * Facets are ASSUMED to be convex and planar, and not overlap in the z direction.
584
639
  */
@@ -591,7 +646,24 @@ class PolyfaceQuery {
591
646
  }
592
647
  }
593
648
  }
594
- /** Execute context.projectToPolygon until its work estimates accumulate to workLimit */
649
+ /**
650
+ * Set the limit on work during an async time blocks, and return the old value.
651
+ * * This should be a large number -- default is 1.0e6
652
+ * @internal
653
+ */
654
+ static setAsyncWorkLimit(value) {
655
+ const oldValue = this._asyncWorkLimit;
656
+ this._asyncWorkLimit = value;
657
+ return oldValue;
658
+ }
659
+ /**
660
+ * Query the current limit on work during an async time block.
661
+ * @internal
662
+ */
663
+ static get asyncWorkLimit() {
664
+ return this._asyncWorkLimit;
665
+ }
666
+ /** Execute `context.projectToPolygon` until its work estimates accumulate to workLimit. */
595
667
  static async continueAnnounceSweepLinestringToConvexPolyfaceXY(context, visitor, announce) {
596
668
  let workCount = 0;
597
669
  while ((workCount < this.asyncWorkLimit) && visitor.moveToNextFacet()) {
@@ -599,16 +671,8 @@ class PolyfaceQuery {
599
671
  }
600
672
  return workCount;
601
673
  }
602
- /** Set the limit on work during an async time blocks, and return the old value.
603
- * * This should be a large number -- default is 1.0e6
604
- * @internal
605
- */
606
- static setAsyncWorkLimit(value) { const a = this._asyncWorkLimit; this._asyncWorkLimit = value; return a; }
607
- /** Query the current limit on work during an async time block.
608
- * @internal
609
- */
610
- static get asyncWorkLimit() { return this._asyncWorkLimit; }
611
- /** Find segments (within the linestring) which project to facets.
674
+ /**
675
+ * Find segments (within the linestring) which project to facets.
612
676
  * * Announce each pair of linestring segment and on-facet segment through a callback.
613
677
  * * Facets are ASSUMED to be convex and planar, and not overlap in the z direction.
614
678
  * * REMARK: Although this is public, the usual use is via slightly higher level public methods, viz:
@@ -631,14 +695,15 @@ class PolyfaceQuery {
631
695
  // GeometryCoreTestIO.consoleLog({ myWorkTotal: workTotal, myBlockCount: this.awaitBlockCount });
632
696
  return workTotal;
633
697
  }
634
- /** Search the facets for facet subsets that are connected with at least vertex contact.
698
+ /**
699
+ * Search the facets for facet subsets that are connected with at least vertex contact.
635
700
  * * Return array of arrays of facet indices.
636
701
  */
637
702
  static partitionFacetIndicesByVertexConnectedComponent(polyface) {
638
703
  if (polyface instanceof Polyface_1.Polyface) {
639
704
  return this.partitionFacetIndicesByVertexConnectedComponent(polyface.createVisitor(0));
640
705
  }
641
- // The polyface is really a visitor !!!
706
+ // The polyface is really a visitor
642
707
  const context = new UnionFind_1.UnionFindContext(this.visitorClientPointCount(polyface));
643
708
  for (polyface.reset(); polyface.moveToNextFacet();) {
644
709
  const firstVertexIndexOnThisFacet = polyface.pointIndex[0];
@@ -666,9 +731,9 @@ class PolyfaceQuery {
666
731
  /**
667
732
  * * Examine the normal orientation for each faces.
668
733
  * * Separate to 3 partitions:
669
- * * facets with normal in the positive direction of the vectorToEye (partition 0)
670
- * * facets with normal in the negative direction of the vectorToEye (partition 1)
671
- * * facets nearly perpendicular to the view vector (partition 2)
734
+ * * Facets with normal in the positive direction of the vectorToEye (partition 0).
735
+ * * Facets with normal in the negative direction of the vectorToEye (partition 1).
736
+ * * Facets nearly perpendicular to the view vector (partition 2).
672
737
  * * Return array of arrays of facet indices.
673
738
  */
674
739
  static partitionFacetIndicesByVisibilityVector(polyface, vectorToEye, sideAngleTolerance) {
@@ -703,13 +768,13 @@ class PolyfaceQuery {
703
768
  }
704
769
  /**
705
770
  * Return the boundary of facets that are facing the eye.
706
- * @param polyface
771
+ * @param polyface the indexed polyface
707
772
  * @param visibilitySubset selector among the visible facet sets extracted by partitionFacetIndicesByVisibilityVector
708
773
  * * 0 ==> forward facing
709
774
  * * 1 ==> rear facing
710
775
  * * 2 ==> side facing
711
- * @param vectorToEye
712
- * @param sideAngleTolerance
776
+ * @param vectorToEye the vector to eye
777
+ * @param sideAngleTolerance the tolerance of side angle
713
778
  */
714
779
  static boundaryOfVisibleSubset(polyface, visibilitySelect, vectorToEye, sideAngleTolerance = Angle_1.Angle.createDegrees(1.0e-3)) {
715
780
  const partitionedIndices = this.partitionFacetIndicesByVisibilityVector(polyface, vectorToEye, sideAngleTolerance);
@@ -719,10 +784,9 @@ class PolyfaceQuery {
719
784
  return this.boundaryEdges(visitor, true, false, false);
720
785
  }
721
786
  /**
722
- * Search for edges with only 1 incident facet.
723
- * * chain them into loops
724
- * * emit the loops to the announceLoop function
725
- * @param mesh
787
+ * Search for edges with only 1 adjacent facet.
788
+ * * Chain them into loops.
789
+ * * Emit the loops to the announceLoop function.
726
790
  */
727
791
  static announceBoundaryChainsAsLineString3d(mesh, announceLoop) {
728
792
  const collector = new MultiChainCollector_1.MultiChainCollector(Geometry_1.Geometry.smallMetricDistance, 1000);
@@ -733,9 +797,9 @@ class PolyfaceQuery {
733
797
  * Return a mesh with
734
798
  * * clusters of adjacent, coplanar facets merged into larger facets.
735
799
  * * other facets included unchanged.
736
- * @param mesh existing mesh or visitor
737
- * @param maxSmoothEdgeAngle maximum dihedral angle across an edge between facets deemed coplanar. If undefined, uses `Geometry.smallAngleRadians`.
738
- * @returns
800
+ * @param mesh existing mesh or visitor.
801
+ * @param maxSmoothEdgeAngle maximum dihedral angle across an edge between facets deemed coplanar. If undefined,
802
+ * uses `Geometry.smallAngleRadians`.
739
803
  */
740
804
  static cloneWithMaximalPlanarFacets(mesh, maxSmoothEdgeAngle) {
741
805
  if (mesh instanceof Polyface_1.Polyface)
@@ -794,14 +858,14 @@ class PolyfaceQuery {
794
858
  * * Unclosed chains are rejected.
795
859
  * * Closed chains are triangulated and returned as a mesh.
796
860
  * * The options structure enforces restrictions on how complicated the hole filling can be:
797
- * * maxEdgesAroundHole -- holes with more edges are skipped
861
+ * * maxEdgesAroundHole -- holes with more edges are skipped.
798
862
  * * maxPerimeter -- holes with larger summed edge lengths are skipped.
799
863
  * * upVector -- holes that do not have positive area along this view are skipped.
800
- * * includeOriginalMesh -- includes the original mesh in the output mesh, so the composite mesh is a clone with holes filled
801
- * @param mesh existing mesh
864
+ * * includeOriginalMesh -- includes the original mesh in the output mesh, so the composite mesh is a
865
+ * clone with holes filled.
866
+ * @param mesh existing mesh.
802
867
  * @param options options controlling the hole fill.
803
868
  * @param unfilledChains optional array to receive the points around holes that were not filled.
804
- * @returns
805
869
  */
806
870
  static fillSimpleHoles(mesh, options, unfilledChains) {
807
871
  if (mesh instanceof Polyface_1.Polyface)
@@ -837,9 +901,7 @@ class PolyfaceQuery {
837
901
  }
838
902
  return builder.claimPolyface(true);
839
903
  }
840
- /** Clone the facets in each partition to a separate polyface.
841
- *
842
- */
904
+ /** Clone the facets in each partition to a separate polyface. */
843
905
  static clonePartitions(polyface, partitions) {
844
906
  if (polyface instanceof Polyface_1.Polyface) {
845
907
  return this.clonePartitions(polyface.createVisitor(0), partitions);
@@ -862,7 +924,7 @@ class PolyfaceQuery {
862
924
  }
863
925
  return polyfaces;
864
926
  }
865
- /** Clone facets that pass a filter function */
927
+ /** Clone facets that pass a filter function. */
866
928
  static cloneFiltered(source, filter) {
867
929
  if (source instanceof Polyface_1.Polyface) {
868
930
  return this.cloneFiltered(source.createVisitor(0), filter);
@@ -932,46 +994,44 @@ class PolyfaceQuery {
932
994
  }
933
995
  return builder.claimPolyface(true);
934
996
  }
935
- /** If the visitor's client is a polyface, simply return its point array length.
936
- * If not a polyface, visit all facets to find the largest index.
937
- */
938
- static visitorClientPointCount(visitor) {
939
- if (visitor instanceof Polyface_1.Polyface)
940
- return visitor.data.point.length;
941
- const polyface = visitor.clientPolyface();
997
+ /** Return the point count of the `source`. */
998
+ static visitorClientPointCount(source) {
999
+ if (source instanceof Polyface_1.Polyface)
1000
+ return source.data.point.length;
1001
+ const polyface = source.clientPolyface();
942
1002
  if (polyface !== undefined)
943
1003
  return polyface.data.point.length;
944
- visitor.reset();
1004
+ source.reset();
945
1005
  let maxIndex = -1;
946
- while (visitor.moveToNextFacet()) {
947
- for (const pointIndex of visitor.pointIndex)
1006
+ while (source.moveToNextFacet()) {
1007
+ for (const pointIndex of source.pointIndex)
948
1008
  if (pointIndex > maxIndex)
949
1009
  maxIndex = pointIndex;
950
1010
  }
951
1011
  return maxIndex + 1;
952
1012
  }
953
- /** If the visitor's client is a polyface, simply return its facet count.
954
- * If not a polyface, visit all facets to accumulate a count.
955
- */
956
- static visitorClientFacetCount(visitor) {
957
- if (visitor instanceof Polyface_1.Polyface) {
958
- if (visitor.facetCount !== undefined)
959
- return visitor.facetCount;
960
- visitor = visitor.createVisitor(0);
1013
+ /** Return the facet count of the `source`. */
1014
+ static visitorClientFacetCount(source) {
1015
+ if (source instanceof Polyface_1.Polyface) {
1016
+ if (source.facetCount !== undefined)
1017
+ return source.facetCount;
1018
+ source = source.createVisitor(0);
961
1019
  }
962
- const polyface = visitor.clientPolyface();
1020
+ const polyface = source.clientPolyface();
963
1021
  if (polyface !== undefined && polyface.facetCount !== undefined)
964
1022
  return polyface.facetCount;
965
1023
  let facetCount = 0;
966
- visitor.reset();
967
- while (visitor.moveToNextFacet())
1024
+ source.reset();
1025
+ while (source.moveToNextFacet())
968
1026
  ++facetCount;
969
1027
  return facetCount;
970
1028
  }
971
- /** Partition the facet set into connected components such that two adjacent facets are in the same component if and only if they are adjacent across a clustered edge.
1029
+ /**
1030
+ * Partition the facet set into connected components such that two adjacent facets are in the same component if and
1031
+ * only if they are adjacent across a clustered edge.
972
1032
  * @param edgeClusters sorted and clustered edges (cf. `IndexedEdgeMatcher.sortAndCollectClusters`).
973
1033
  * @param numFacets facet count in the parent mesh. In particular, `edge.facetIndex < numFacets` for every input edge.
974
- * @return collection of facet index arrays, one array per connected component
1034
+ * @return collection of facet index arrays, one array per connected component.
975
1035
  */
976
1036
  static partitionFacetIndicesBySortableEdgeClusters(edgeClusters, numFacets) {
977
1037
  const context = new UnionFind_1.UnionFindContext(numFacets);
@@ -1002,10 +1062,12 @@ class PolyfaceQuery {
1002
1062
  }
1003
1063
  return facetsInComponent;
1004
1064
  }
1005
- /** Partition the facet set into connected components. Each facet in a given component shares an edge only with other facets in the component (or is a boundary edge).
1006
- * @param polyface facets to partition
1007
- * @param stopAtVisibleEdges whether to further split connected components by visible edges of the polyface
1008
- * @return collection of facet index arrays, one per connected component
1065
+ /**
1066
+ * Partition the facet set into connected components. Each facet in a given component shares an edge only with
1067
+ * other facets in the component (or is a boundary edge).
1068
+ * @param polyface facets to partition.
1069
+ * @param stopAtVisibleEdges whether to further split connected components by visible edges of the polyface.
1070
+ * @return collection of facet index arrays, one per connected component.
1009
1071
  */
1010
1072
  static partitionFacetIndicesByEdgeConnectedComponent(polyface, stopAtVisibleEdges = false) {
1011
1073
  if (polyface instanceof Polyface_1.Polyface) {
@@ -1030,7 +1092,8 @@ class PolyfaceQuery {
1030
1092
  matcher.sortAndCollectClusters(allEdges, allEdges, allEdges, allEdges);
1031
1093
  return this.partitionFacetIndicesBySortableEdgeClusters(allEdges, numFacets);
1032
1094
  }
1033
- /** Find segments (within the line string) which project to facets.
1095
+ /**
1096
+ * Find segments (within the line string) which project to facets.
1034
1097
  * * Assemble each input segment paired with its projected segment/point as a quad/triangle facet in a new polyface.
1035
1098
  * * Input facets are ASSUMED to be convex and planar, and not overlap in the z direction.
1036
1099
  */
@@ -1044,7 +1107,7 @@ class PolyfaceQuery {
1044
1107
  });
1045
1108
  return builder.claimPolyface(true);
1046
1109
  }
1047
- /** @deprecated in 4.x. Use sweepLineStringToFacetsXYReturnSweptFacets instead. */
1110
+ /** @deprecated in 4.x. Use [[sweepLineStringToFacetsXYReturnSweptFacets]] instead. */
1048
1111
  static sweepLinestringToFacetsXYreturnSweptFacets(linestringPoints, polyface) {
1049
1112
  return this.sweepLineStringToFacetsXYReturnSweptFacets(linestringPoints, polyface);
1050
1113
  }
@@ -1057,11 +1120,10 @@ class PolyfaceQuery {
1057
1120
  */
1058
1121
  static sweepLineStringToFacets(linestringPoints, polyfaceOrVisitor, options) {
1059
1122
  let result = [];
1060
- // setup default options:
1123
+ // setup default options
1061
1124
  if (options === undefined)
1062
1125
  options = SweepLineStringToFacetsOptions.create(Point3dVector3d_1.Vector3d.unitZ(), Angle_1.Angle.createRadians(Geometry_1.Geometry.smallAngleRadians), // tight geometry tolerance for vertical side facets
1063
- true, // assemble chains
1064
- true, true, true); // accept all outputs
1126
+ true, true, true, true);
1065
1127
  let chainContext;
1066
1128
  if (options.assembleChains)
1067
1129
  chainContext = ChainMerge_1.ChainMergeContext.create();
@@ -1092,9 +1154,9 @@ class PolyfaceQuery {
1092
1154
  }
1093
1155
  /**
1094
1156
  * Sweep the line string in the z-direction to intersections with a mesh, using a search object for speedup.
1095
- * @param lineStringPoints input line string to drape on the mesh
1096
- * @param polyfaceOrVisitor mesh, or mesh visitor to traverse only part of a mesh
1097
- * @param searchByReadIndex object for searching facet 2D ranges tagged by mesh read index
1157
+ * @param lineStringPoints input line string to drape on the mesh.
1158
+ * @param polyfaceOrVisitor mesh, or mesh visitor to traverse only part of a mesh.
1159
+ * @param searchByReadIndex object for searching facet 2D ranges tagged by mesh read index.
1098
1160
  * @example Using a 5x5 indexed search grid:
1099
1161
  * ```
1100
1162
  * const xyRange = Range2d.createFrom(myPolyface.range());
@@ -1104,7 +1166,7 @@ class PolyfaceQuery {
1104
1166
  * }
1105
1167
  * const drapedLineStrings = PolyfaceQuery.sweepLineStringToFacetsXY(lineString, myPolyface, searcher);
1106
1168
  * ```
1107
- * @returns collected line strings
1169
+ * @returns the collected line strings.
1108
1170
  */
1109
1171
  static sweepLineStringToFacetsXY(lineStringPoints, polyfaceOrVisitor, searchByReadIndex) {
1110
1172
  const chainContext = ChainMerge_1.ChainMergeContext.create();
@@ -1138,32 +1200,36 @@ class PolyfaceQuery {
1138
1200
  chainContext.clusterAndMergeVerticesXYZ();
1139
1201
  return chainContext.collectMaximalChains();
1140
1202
  }
1141
- /** Find segments (within the linestring) which project to facets.
1203
+ /**
1204
+ * Find segments (within the linestring) which project to facets.
1142
1205
  * * Return collected line segments.
1143
1206
  * * This calls [[sweepLineStringToFacets]] with options created by
1144
- * `const options = SweepLineStringToFacetsOptions.create(Vector3d.unitZ(), Angle.createSmallAngle(),false, true, true, true);`
1145
- * @deprecated in 4.x. Use [[sweepLineStringToFacets]] to get further options.
1207
+ * `const options = SweepLineStringToFacetsOptions.create(Vector3d.unitZ(), Angle.createSmallAngle(), false, true, true, true);`
1208
+ * @deprecated in 4.x. Use [[PolyfaceQuery.sweepLineStringToFacets]] to get further options.
1146
1209
  */
1147
1210
  static sweepLinestringToFacetsXYReturnLines(linestringPoints, polyface) {
1148
1211
  const options = SweepLineStringToFacetsOptions.create(Point3dVector3d_1.Vector3d.unitZ(), Angle_1.Angle.createSmallAngle(), false, true, true, true);
1149
1212
  const result = PolyfaceQuery.sweepLineStringToFacets(linestringPoints, polyface, options);
1150
1213
  return result;
1151
1214
  }
1152
- /** Find segments (within the linestring) which project to facets.
1215
+ /**
1216
+ * Find segments (within the linestring) which project to facets.
1153
1217
  * * Return chains.
1154
1218
  * * This calls [[sweepLineStringToFacets]] with options created by
1155
1219
  * `const options = SweepLineStringToFacetsOptions.create(Vector3d.unitZ(), Angle.createSmallAngle(),true, true, true, true);`
1156
- * @deprecated in 4.x. Use [[sweepLineStringToFacets]] to get further options.
1220
+ * @deprecated in 4.x. Use [[PolyfaceQuery.sweepLineStringToFacets]] to get further options.
1157
1221
  */
1158
1222
  static sweepLinestringToFacetsXYReturnChains(linestringPoints, polyface) {
1159
1223
  const options = SweepLineStringToFacetsOptions.create(Point3dVector3d_1.Vector3d.unitZ(), Angle_1.Angle.createSmallAngle(), true, true, true, true);
1160
1224
  const result = PolyfaceQuery.sweepLineStringToFacets(linestringPoints, polyface, options);
1161
1225
  return result;
1162
1226
  }
1163
- /** Find segments (within the linestring) which project to facets.
1227
+ /**
1228
+ * Find segments (within the linestring) which project to facets.
1164
1229
  * * This is done as a sequence of "await" steps.
1165
- * * Each "await" step deals with approximately PolyfaceQuery.asyncWorkLimit pairings of (linestring edge) with (facet edge)
1166
- * * PolyfaceQuery.setAsyncWorkLimit() to change work blocks from default
1230
+ * * Each "await" step deals with approximately PolyfaceQuery.asyncWorkLimit pairings of "linestring edge"
1231
+ * with "facet edge".
1232
+ * * PolyfaceQuery.setAsyncWorkLimit() to change work blocks from default.
1167
1233
  * * Return chains.
1168
1234
  * * Facets are ASSUMED to be convex and planar, and not overlap in the z direction.
1169
1235
  */
@@ -1177,7 +1243,7 @@ class PolyfaceQuery {
1177
1243
  return chains;
1178
1244
  }
1179
1245
  /**
1180
- * * Examine ranges of facets.
1246
+ * Examine ranges of facets.
1181
1247
  * * Return statistical summary of x,y,z ranges.
1182
1248
  */
1183
1249
  static collectRangeLengthData(polyface) {
@@ -1185,17 +1251,18 @@ class PolyfaceQuery {
1185
1251
  return this.collectRangeLengthData(polyface.createVisitor(0));
1186
1252
  }
1187
1253
  const rangeData = new RangeLengthData_1.RangeLengthData();
1188
- // polyface is a visitor ...
1254
+ // polyface is a visitor
1189
1255
  for (polyface.reset(); polyface.moveToNextFacet();)
1190
1256
  rangeData.accumulateGrowableXYZArrayRange(polyface.point);
1191
1257
  return rangeData;
1192
1258
  }
1193
- /** Clone the facets, inserting vertices (within edges) where points not part of each facet's vertex indices impinge within edges.
1194
- *
1259
+ /**
1260
+ * Clone the facets, inserting vertices (within edges) where points not part of each facet's vertex indices
1261
+ * impinge within edges.
1195
1262
  */
1196
1263
  static cloneWithTVertexFixup(polyface) {
1197
- const oldFacetVisitor = polyface.createVisitor(1); // This is to visit the existing facets.
1198
- const newFacetVisitor = polyface.createVisitor(0); // This is to build the new facets.
1264
+ const oldFacetVisitor = polyface.createVisitor(1); // this is to visit the existing facets
1265
+ const newFacetVisitor = polyface.createVisitor(0); // this is to build the new facets
1199
1266
  const rangeSearcher = XYPointBuckets_1.XYPointBuckets.create(polyface.data.point, 30);
1200
1267
  const builder = PolyfaceBuilder_1.PolyfaceBuilder.create();
1201
1268
  const edgeRange = Range_1.Range3d.createNull();
@@ -1206,7 +1273,7 @@ class PolyfaceQuery {
1206
1273
  for (oldFacetVisitor.reset(); oldFacetVisitor.moveToNextFacet();) {
1207
1274
  newFacetVisitor.clearArrays();
1208
1275
  for (let i = 0; i + 1 < oldFacetVisitor.point.length; i++) {
1209
- // each base vertex is part of the result ...
1276
+ // each base vertex is part of the result
1210
1277
  oldFacetVisitor.point.getPoint3dAtUncheckedPointIndex(i, point0);
1211
1278
  oldFacetVisitor.point.getPoint3dAtUncheckedPointIndex(i + 1, point1);
1212
1279
  newFacetVisitor.pushDataFrom(oldFacetVisitor, i);
@@ -1217,12 +1284,12 @@ class PolyfaceQuery {
1217
1284
  edgeRange.extend(point1);
1218
1285
  edgeRange.ensureMinLengths(Geometry_1.Geometry.smallMetricDistance); // add some slop in case segment is axis-aligned
1219
1286
  rangeSearcher.announcePointsInRange(edgeRange, (index, _x, _y, _z) => {
1220
- // x,y,z has x,y within the range of the search ... test for exact on (in full 3d!)
1287
+ // x,y,z has x,y within the range of the search; test for exact on (in full 3d)
1221
1288
  polyface.data.point.getPoint3dAtUncheckedPointIndex(index, spacePoint);
1222
1289
  const detail = segment.closestPoint(spacePoint, false);
1223
1290
  if (undefined !== detail) {
1224
- if (detail.fraction > 0.0 && detail.fraction < 1.0 && !detail.point.isAlmostEqual(point0) && !detail.point.isAlmostEqual(point1)
1225
- && spacePoint.isAlmostEqual(detail.point)) {
1291
+ if (detail.fraction > 0.0 && detail.fraction < 1.0 && !detail.point.isAlmostEqual(point0) &&
1292
+ !detail.point.isAlmostEqual(point1) && spacePoint.isAlmostEqual(detail.point)) {
1226
1293
  if (detailArray === undefined)
1227
1294
  detailArray = [];
1228
1295
  detail.a = index;
@@ -1243,14 +1310,13 @@ class PolyfaceQuery {
1243
1310
  return builder.claimPolyface();
1244
1311
  }
1245
1312
  /**
1313
+ * Compare index arrays formatted as follows. Return 0 if arrays are the same.
1246
1314
  * * Each array input structure is: [facetIndex, vertexIndex0, vertexIndex1, ....]
1247
- * * Vertex indices assumed reversed so it
1248
- * * vertexIndex0 is the lowest index on the facet
1249
- * * vertexIndex1 is the lowest neighbor of vertex0
1315
+ * * Vertex indices assumed reversed so:
1316
+ * * vertexIndex0 is the lowest index on the facet.
1317
+ * * vertexIndex1 is the lowest neighbor of vertex0.
1250
1318
  * * first different entry among vertex indices determines lexical result.
1251
- * * Hence facets with duplicate indices (whether forward or reversed) are considered equal.
1252
- * @param arrayA
1253
- * @param arrayB
1319
+ * * hence facets with duplicate indices (whether forward or reversed) are considered equal.
1254
1320
  */
1255
1321
  static compareFacetIndexAndVertexIndices(arrayA, arrayB) {
1256
1322
  if (arrayA.length !== arrayB.length)
@@ -1263,25 +1329,12 @@ class PolyfaceQuery {
1263
1329
  return 0;
1264
1330
  }
1265
1331
  /**
1266
- * * Return an array of arrays describing facet duplication.
1267
- * @param includeSingletons if true, non-duplicated facets are included in the output.
1268
- * * Each array `entry` in the output contains read indices of a cluster of facets with the same vertex indices.
1269
- */
1270
- static collectDuplicateFacetIndices(polyface, includeSingletons = false) {
1271
- const result = [];
1272
- this.announceDuplicateFacetIndices(polyface, (clusterFacetIndices) => {
1273
- if (includeSingletons || clusterFacetIndices.length > 1)
1274
- result.push(clusterFacetIndices.slice());
1275
- });
1276
- return result;
1277
- }
1278
- /**
1279
- * * Return an array of arrays describing facet duplication.
1280
- * @param includeSingletons if true, non-duplicated facets are included in the output.
1281
- * * Each array `entry` in the output contains read indices of a cluster of facets with the same vertex indices.
1332
+ * Announce facet duplicates.
1333
+ * @returns an array of arrays describing facet duplication. Each array `entry` in the output contains read
1334
+ * indices of a cluster of facets with the same vertex indices.
1282
1335
  */
1283
1336
  static announceDuplicateFacetIndices(polyface, announceCluster) {
1284
- const visitor = polyface.createVisitor(0); // This is to visit the existing facets.
1337
+ const visitor = polyface.createVisitor(0); // this is to visit the existing facets
1285
1338
  const facetIndexAndVertexIndices = [];
1286
1339
  for (visitor.reset(); visitor.moveToNextFacet();) {
1287
1340
  const facetIndex = visitor.currentReadIndex();
@@ -1289,12 +1342,12 @@ class PolyfaceQuery {
1289
1342
  const pointIndex = visitor.pointIndex;
1290
1343
  const numPointsThisFacet = pointIndex.length;
1291
1344
  let lowIndex = 0;
1292
- // find the lowest point index ...
1345
+ // find the lowest point index
1293
1346
  for (let i = 1; i < visitor.pointIndex.length; i++) {
1294
1347
  if (pointIndex[i] < pointIndex[lowIndex])
1295
1348
  lowIndex = i;
1296
1349
  }
1297
- // find its lowest neighbor -- assemble sort array in that direction
1350
+ // find its lowest neighbor; assemble sort array in that direction
1298
1351
  if (pointIndex[(lowIndex + 1) % numPointsThisFacet] < pointIndex[(lowIndex + numPointsThisFacet - 1) % numPointsThisFacet]) {
1299
1352
  for (let i = 0; i < numPointsThisFacet; i++) {
1300
1353
  entry.push(pointIndex[(lowIndex + i) % numPointsThisFacet]);
@@ -1322,9 +1375,26 @@ class PolyfaceQuery {
1322
1375
  announceCluster(clusterArray);
1323
1376
  }
1324
1377
  }
1325
- /** Return a new facet set with a subset of facets in source
1378
+ /**
1379
+ * Collect facet duplicates.
1380
+ * @param polyface the polyface.
1381
+ * @param includeSingletons if true, non-duplicated facets are included in the output.
1382
+ * @returns an array of arrays describing facet duplication. Each array `entry` in the output contains read
1383
+ * indices of a cluster of facets with the same vertex indices.
1384
+ */
1385
+ static collectDuplicateFacetIndices(polyface, includeSingletons = false) {
1386
+ const result = [];
1387
+ this.announceDuplicateFacetIndices(polyface, (clusterFacetIndices) => {
1388
+ if (includeSingletons || clusterFacetIndices.length > 1)
1389
+ result.push(clusterFacetIndices.slice());
1390
+ });
1391
+ return result;
1392
+ }
1393
+ /**
1394
+ * Return a new facet set with a subset of facets in polyface.
1395
+ * @param source the polyface.
1326
1396
  * @param includeSingletons true to copy facets that only appear once
1327
- * @param clusterSelector indicates whether duplicate clusters are to have 0, 1, or all facets included
1397
+ * @param clusterSelector indicates whether duplicate clusters are to have 0, 1, or all facets included.
1328
1398
  */
1329
1399
  static cloneByFacetDuplication(source, includeSingletons, clusterSelector) {
1330
1400
  const builder = PolyfaceBuilder_1.PolyfaceBuilder.create();
@@ -1350,25 +1420,23 @@ class PolyfaceQuery {
1350
1420
  });
1351
1421
  return builder.claimPolyface();
1352
1422
  }
1353
- /** Clone the facets, inserting removing points that are simply within colinear edges.
1354
- *
1355
- */
1423
+ /** Clone the facets, removing points that are simply within colinear edges. */
1356
1424
  static cloneWithColinearEdgeFixup(polyface) {
1357
- const oldFacetVisitor = polyface.createVisitor(2); // This is to visit the existing facets.
1358
- const newFacetVisitor = polyface.createVisitor(0); // This is to build the new facets.
1425
+ const oldFacetVisitor = polyface.createVisitor(2); // this is to visit the existing facets
1426
+ const newFacetVisitor = polyface.createVisitor(0); // this is to build the new facets
1359
1427
  const builder = PolyfaceBuilder_1.PolyfaceBuilder.create();
1360
1428
  const vector01 = Point3dVector3d_1.Vector3d.create();
1361
1429
  const vector12 = Point3dVector3d_1.Vector3d.create();
1362
1430
  const numPoint = polyface.data.point.length;
1363
1431
  const pointState = new Int32Array(numPoint);
1364
- // FIRST PASS -- in each sector of each facet, determine if the sector has colinear incoming and outgoing vectors.
1365
- // Mark each point as
1366
- // 0 unvisited
1367
- // -1 incident to a non-colinear sector
1368
- // n incident to n colinear sectors
1432
+ // FIRST PASS: in each sector of each facet, determine if the sector has colinear incoming and outgoing vectors.
1433
+ // Mark each point as
1434
+ // 0 unvisited
1435
+ // -1 adjacent to a non-colinear sector
1436
+ // n adjacent to n colinear sectors
1369
1437
  for (oldFacetVisitor.reset(); oldFacetVisitor.moveToNextFacet();) {
1370
1438
  for (let i = 0; i + 2 < oldFacetVisitor.point.length; i++) {
1371
- // each base vertex is part of the result ...
1439
+ // each base vertex is part of the result
1372
1440
  oldFacetVisitor.point.vectorIndexIndex(i, i + 1, vector01);
1373
1441
  oldFacetVisitor.point.vectorIndexIndex(i + 1, i + 2, vector12);
1374
1442
  const pointIndex = oldFacetVisitor.clientPointIndex(i + 1);
@@ -1383,7 +1451,7 @@ class PolyfaceQuery {
1383
1451
  }
1384
1452
  }
1385
1453
  }
1386
- // SECOND PASS -- make copies, omitting references to points at colinear sectors
1454
+ // SECOND PASS: make copies, omitting references to points at colinear sectors.
1387
1455
  for (oldFacetVisitor.reset(); oldFacetVisitor.moveToNextFacet();) {
1388
1456
  newFacetVisitor.clearArrays();
1389
1457
  for (let i = 0; i + 2 < oldFacetVisitor.point.length; i++) {
@@ -1399,9 +1467,9 @@ class PolyfaceQuery {
1399
1467
  }
1400
1468
  /**
1401
1469
  * Set the edge visibility for specified edges in the polyface.
1402
- * @param polyface mesh to be edited
1403
- * @param clusters array of edge references
1404
- * @param value visibility value (true or false)
1470
+ * @param polyface mesh to be edited.
1471
+ * @param clusters array of edge references.
1472
+ * @param value visibility value (true or false).
1405
1473
  */
1406
1474
  static setEdgeVisibility(polyface, clusters, value) {
1407
1475
  for (const cluster of clusters) {
@@ -1416,9 +1484,9 @@ class PolyfaceQuery {
1416
1484
  }
1417
1485
  /**
1418
1486
  * Set the visibility of a particular edge of a particular facet.
1419
- * @param polyface containing polyface
1420
- * @param facetIndex facet index
1421
- * @param vertexIndex vertex index (in vertex array) at which the edge starts
1487
+ * @param polyface containing polyface.
1488
+ * @param facetIndex facet index.
1489
+ * @param vertexIndex vertex index (in vertex array) at which the edge starts.
1422
1490
  * @param value visibility value.
1423
1491
  */
1424
1492
  static setSingleEdgeVisibility(polyface, facetIndex, vertexIndex, value) {
@@ -1431,9 +1499,9 @@ class PolyfaceQuery {
1431
1499
  }
1432
1500
  /**
1433
1501
  * Get the visibility of a particular edge of a particular facet.
1434
- * @param polyface containing polyface
1435
- * @param facetIndex facet index
1436
- * @param vertexIndex vertex index (in vertex array) at which the edge starts
1502
+ * @param polyface containing polyface.
1503
+ * @param facetIndex facet index.
1504
+ * @param vertexIndex vertex index (in vertex array) at which the edge starts.
1437
1505
  */
1438
1506
  static getSingleEdgeVisibility(polyface, facetIndex, vertexIndex) {
1439
1507
  const data = polyface.data;
@@ -1444,29 +1512,13 @@ class PolyfaceQuery {
1444
1512
  return data.edgeVisible[i]; // return visibility of first edge in the face that starts at this vertex
1445
1513
  return undefined;
1446
1514
  }
1447
- /** Load all half edges from a mesh to an IndexedEdgeMatcher.
1448
- * @param polyface a mesh, or a visitor assumed to have numWrap === 1
1449
- */
1450
- static createIndexedEdges(polyface) {
1451
- if (polyface instanceof Polyface_1.Polyface)
1452
- return this.createIndexedEdges(polyface.createVisitor(1));
1453
- const edges = new IndexedEdgeMatcher_1.IndexedEdgeMatcher();
1454
- polyface.reset();
1455
- while (polyface.moveToNextFacet()) {
1456
- const numEdges = polyface.pointCount - 1;
1457
- for (let i = 0; i < numEdges; i++) {
1458
- edges.addEdge(polyface.clientPointIndex(i), polyface.clientPointIndex(i + 1), polyface.currentReadIndex());
1459
- }
1460
- }
1461
- return edges;
1462
- }
1463
1515
  /**
1464
1516
  * Return manifold edge pairs whose dihedral angle is bounded by the given angle.
1465
1517
  * * The dihedral angle of a manifold edge is measured between the normals of its two adjacent faces.
1466
1518
  * * Boundary edges are not returned as they are not manifold.
1467
- * @param mesh existing polyface or visitor
1468
- * @param maxSmoothEdgeAngle maximum dihedral angle of a smooth edge. If undefined, uses `Geometry.smallAngleRadians`.
1469
- * @param sharpEdges true to reverse the angle threshold test and return sharp edges; otherwise return smooth edges (default)
1519
+ * @param mesh existing polyface or visitor.
1520
+ * @param maxSmoothEdgeAngle maximum dihedral angle of a smooth edge. If `undefined`, uses `Geometry.smallAngleRadians`.
1521
+ * @param sharpEdges true to reverse the angle threshold test and return sharp edges; otherwise return smooth edges (default).
1470
1522
  */
1471
1523
  static collectEdgesByDihedralAngle(mesh, maxSmoothEdgeAngle, sharpEdges = false) {
1472
1524
  if (mesh instanceof Polyface_1.Polyface)
@@ -1501,13 +1553,13 @@ class PolyfaceQuery {
1501
1553
  return outEdges;
1502
1554
  }
1503
1555
  /**
1556
+ * Make paired edges invisible.
1504
1557
  * * Find mated pairs among facet edges.
1505
1558
  * * Mated pairs have the same vertex indices appearing in opposite order.
1506
1559
  * * Mark all non-mated pairs visible.
1507
1560
  * * At mated pairs
1508
1561
  * * if angle across the edge is larger than `sharpEdgeAngle`, mark visible
1509
1562
  * * otherwise mark invisible.
1510
- * @param mesh mesh to be marked
1511
1563
  */
1512
1564
  static markPairedEdgesInvisible(mesh, sharpEdgeAngle) {
1513
1565
  const visitor = mesh.createVisitor(1);
@@ -1536,7 +1588,8 @@ class PolyfaceQuery {
1536
1588
  }
1537
1589
  }
1538
1590
  }
1539
- /** Try to compute a unit normal for a facet accessible through a visitor.
1591
+ /**
1592
+ * Try to compute a unit normal for a facet accessible through a visitor.
1540
1593
  * * Unit normal is computed by `PolygonOps.unitNormal` with the points around the facet.
1541
1594
  */
1542
1595
  static computeFacetUnitNormal(visitor, facetIndex, result) {
@@ -1549,18 +1602,18 @@ class PolyfaceQuery {
1549
1602
  return undefined;
1550
1603
  }
1551
1604
  /**
1552
- * * Mark all edge visibilities in the IndexedPolyface
1553
- * @param mesh mesh to be marked
1554
- * @param value true for visible, false for hidden
1555
- */
1605
+ * * Mark all edge visibilities in the IndexedPolyface.
1606
+ * @param mesh mesh to be marked.
1607
+ * @param value true for visible, false for hidden.
1608
+ */
1556
1609
  static markAllEdgeVisibility(mesh, value) {
1557
1610
  const data = mesh.data;
1558
1611
  for (let i = 0; i < data.edgeVisible.length; i++)
1559
1612
  data.edgeVisible[i] = value;
1560
1613
  }
1561
1614
  /**
1562
- * Create a HalfEdgeGraph with a face for each facet of the IndexedPolyface
1563
- * @param mesh mesh to convert
1615
+ * Create a HalfEdgeGraph with a face for each facet of the IndexedPolyface.
1616
+ * @param mesh mesh to convert.
1564
1617
  * @internal
1565
1618
  */
1566
1619
  static convertToHalfEdgeGraph(mesh) {
@@ -1579,28 +1632,22 @@ class PolyfaceQuery {
1579
1632
  });
1580
1633
  return graph;
1581
1634
  }
1582
- /**
1583
- * * Examine adjacent facet orientations throughout the mesh
1584
- * * If possible, reverse a subset to achieve proper pairing.
1585
- * @param mesh
1586
- */
1635
+ /** Examine adjacent facet orientations throughout the mesh. If possible, reverse a subset to achieve proper pairing. */
1587
1636
  static reorientVertexOrderAroundFacetsForConsistentOrientation(mesh) {
1588
1637
  return FacetOrientation_1.FacetOrientationFixup.doFixup(mesh);
1589
1638
  }
1590
- /**
1591
- * Set up indexed normals with one normal in the plane of each facet of the mesh.
1592
- * @param polyface
1593
- */
1639
+ /** Set up indexed normals with one normal in the plane of each facet of the mesh. */
1594
1640
  static buildPerFaceNormals(polyface) {
1595
1641
  BuildAverageNormalsContext_1.BuildAverageNormalsContext.buildPerFaceNormals(polyface);
1596
1642
  }
1597
1643
  /**
1598
- * * At each vertex of the mesh
1599
- * * Find clusters of almost parallel normals
1600
- * * Compute simple average of those normals
1601
- * * Index to the averages
1644
+ * * At each vertex of the mesh:
1645
+ * * Find clusters of almost parallel normals.
1646
+ * * Compute simple average of those normals.
1647
+ * * Index to the averages.
1602
1648
  * * For typical meshes, this correctly clusters adjacent normals.
1603
- * * One can imagine a vertex with multiple "smooth cone-like" sets of incident facets such that averaging occurs among two nonadjacent cones. But this does not seem to be a problem in practice.
1649
+ * * One can imagine a vertex with multiple "smooth cone-like" sets of adjacent facets such that averaging occurs
1650
+ * among two nonadjacent cones. But this does not seem to be a problem in practice.
1604
1651
  * @param polyface polyface to update.
1605
1652
  * @param toleranceAngle averaging is done between normals up to this angle.
1606
1653
  */
@@ -1609,9 +1656,9 @@ class PolyfaceQuery {
1609
1656
  }
1610
1657
  /**
1611
1658
  * Offset the faces of the mesh.
1612
- * @param source original mesh
1659
+ * @param source original mesh.
1613
1660
  * @param signedOffsetDistance distance to offset
1614
- * @param offsetOptions angle options. The default options are recommended.
1661
+ * @param offsetOptions angle options. The default options are recommended.
1615
1662
  * @returns shifted mesh.
1616
1663
  */
1617
1664
  static cloneOffset(source, signedOffsetDistance, offsetOptions = OffsetMeshOptions.create()) {
@@ -1620,16 +1667,22 @@ class PolyfaceQuery {
1620
1667
  OffsetMeshContext_1.OffsetMeshContext.buildOffsetMeshWithEdgeChamfers(source, offsetBuilder, signedOffsetDistance, offsetOptions);
1621
1668
  return offsetBuilder.claimPolyface();
1622
1669
  }
1623
- /** Search facets for the first one that intersects the infinite line.
1624
- * * To process _all_ intersections, callers can supply an `options.acceptIntersection` callback that always returns false.
1625
- * In this case, `intersectRay3d` will return undefined, but the callback will be invoked for each intersection.
1670
+ /**
1671
+ * Search facets for the first one that intersects the infinite line.
1672
+ * * To process _all_ intersections, callers can supply an `options.acceptIntersection` callback that always
1673
+ * returns `false`.
1674
+ * In this case, `intersectRay3d` will return `undefined`, but the callback will be invoked for each intersection.
1626
1675
  * * Example callback logic:
1627
1676
  * * Accept the first found facet that intersects the half-line specified by the ray: `return detail.a >= 0.0;`
1628
- * * Collect all intersections: `myIntersections.push(detail.clone()); return false;` Then after `intersectRay3d` returns, sort along `ray` with `myIntersections.sort((d0, d1) => d0.a - d1.a);`
1629
- * @param visitor facet iterator
1630
- * @param ray infinite line parameterized as a ray. The returned `detail.a` is the intersection parameter on the ray, e.g., zero at `ray.origin` and increasing in `ray.direction`.
1631
- * @param options options for computing and populating an intersection detail, and an optional callback for accepting one
1632
- * @return detail for the (accepted) intersection with `detail.IsInsideOrOn === true`, or `undefined` if no (accepted) intersection
1677
+ * * Collect all intersections: `myIntersections.push(detail.clone()); return false;` Then after `intersectRay3d`
1678
+ * returns, sort along `ray` with `myIntersections.sort((d0, d1) => d0.a - d1.a);`
1679
+ * @param visitor facet iterator.
1680
+ * @param ray infinite line parameterized as a ray. The returned `detail.a` is the intersection parameter on the
1681
+ * ray, e.g., zero at `ray.origin` and increasing in `ray.direction`.
1682
+ * @param options options for computing and populating an intersection detail, and an optional callback for
1683
+ * accepting one.
1684
+ * @return detail for the (accepted) intersection with `detail.IsInsideOrOn === true`, or `undefined` if no
1685
+ * (accepted) intersection.
1633
1686
  * @see PolygonOps.intersectRay3d
1634
1687
  */
1635
1688
  static intersectRay3d(visitor, ray, options) {
@@ -1673,7 +1726,8 @@ class PolyfaceQuery {
1673
1726
  exports.PolyfaceQuery = PolyfaceQuery;
1674
1727
  // amount of computation to do per step of async methods.
1675
1728
  PolyfaceQuery._asyncWorkLimit = 1.e06;
1676
- /** Number of "await" steps executed in recent async calls.
1729
+ /**
1730
+ * Number of "await" steps executed in recent async calls.
1677
1731
  * @internal
1678
1732
  */
1679
1733
  PolyfaceQuery.awaitBlockCount = 0;