@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.
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +7 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js +20 -4
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts +3 -3
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.js +4 -3
- package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.d.ts +7 -0
- package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.js +19 -0
- package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
- package/lib/cjs/polyface/AuxData.d.ts +18 -10
- package/lib/cjs/polyface/AuxData.d.ts.map +1 -1
- package/lib/cjs/polyface/AuxData.js +24 -12
- package/lib/cjs/polyface/AuxData.js.map +1 -1
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts +2 -1
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.js +2 -1
- package/lib/cjs/polyface/IndexedPolyfaceVisitor.js.map +1 -1
- package/lib/cjs/polyface/Polyface.d.ts +10 -2
- package/lib/cjs/polyface/Polyface.d.ts.map +1 -1
- package/lib/cjs/polyface/Polyface.js +29 -17
- package/lib/cjs/polyface/Polyface.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceData.d.ts +10 -9
- package/lib/cjs/polyface/PolyfaceData.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceData.js +17 -2
- package/lib/cjs/polyface/PolyfaceData.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts +321 -270
- package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.js +405 -351
- package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/cjs/serialization/BGFBReader.d.ts +18 -20
- package/lib/cjs/serialization/BGFBReader.d.ts.map +1 -1
- package/lib/cjs/serialization/BGFBReader.js +119 -84
- package/lib/cjs/serialization/BGFBReader.js.map +1 -1
- package/lib/cjs/serialization/BGFBWriter.d.ts +1 -1
- package/lib/cjs/serialization/BGFBWriter.d.ts.map +1 -1
- package/lib/cjs/serialization/BGFBWriter.js +10 -15
- package/lib/cjs/serialization/BGFBWriter.js.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.d.ts +52 -7
- package/lib/cjs/serialization/IModelJsonSchema.d.ts.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.js +26 -78
- package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/cjs/serialization/SerializationHelpers.d.ts +17 -0
- package/lib/cjs/serialization/SerializationHelpers.d.ts.map +1 -1
- package/lib/cjs/serialization/SerializationHelpers.js +85 -0
- package/lib/cjs/serialization/SerializationHelpers.js.map +1 -1
- package/lib/cjs/solid/Sphere.d.ts +1 -0
- package/lib/cjs/solid/Sphere.d.ts.map +1 -1
- package/lib/cjs/solid/Sphere.js +4 -2
- package/lib/cjs/solid/Sphere.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +7 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js +20 -4
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.d.ts +3 -3
- package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.js +4 -3
- package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.d.ts +7 -0
- package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.js +19 -0
- package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
- package/lib/esm/polyface/AuxData.d.ts +18 -10
- package/lib/esm/polyface/AuxData.d.ts.map +1 -1
- package/lib/esm/polyface/AuxData.js +24 -12
- package/lib/esm/polyface/AuxData.js.map +1 -1
- package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts +2 -1
- package/lib/esm/polyface/IndexedPolyfaceVisitor.d.ts.map +1 -1
- package/lib/esm/polyface/IndexedPolyfaceVisitor.js +2 -1
- package/lib/esm/polyface/IndexedPolyfaceVisitor.js.map +1 -1
- package/lib/esm/polyface/Polyface.d.ts +10 -2
- package/lib/esm/polyface/Polyface.d.ts.map +1 -1
- package/lib/esm/polyface/Polyface.js +29 -17
- package/lib/esm/polyface/Polyface.js.map +1 -1
- package/lib/esm/polyface/PolyfaceData.d.ts +10 -9
- package/lib/esm/polyface/PolyfaceData.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceData.js +17 -2
- package/lib/esm/polyface/PolyfaceData.js.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts +321 -270
- package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.js +405 -351
- package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/esm/serialization/BGFBReader.d.ts +18 -20
- package/lib/esm/serialization/BGFBReader.d.ts.map +1 -1
- package/lib/esm/serialization/BGFBReader.js +119 -84
- package/lib/esm/serialization/BGFBReader.js.map +1 -1
- package/lib/esm/serialization/BGFBWriter.d.ts +1 -1
- package/lib/esm/serialization/BGFBWriter.d.ts.map +1 -1
- package/lib/esm/serialization/BGFBWriter.js +10 -15
- package/lib/esm/serialization/BGFBWriter.js.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.d.ts +52 -7
- package/lib/esm/serialization/IModelJsonSchema.d.ts.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.js +26 -78
- package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/esm/serialization/SerializationHelpers.d.ts +17 -0
- package/lib/esm/serialization/SerializationHelpers.d.ts.map +1 -1
- package/lib/esm/serialization/SerializationHelpers.js +85 -0
- package/lib/esm/serialization/SerializationHelpers.js.map +1 -1
- package/lib/esm/solid/Sphere.d.ts +1 -0
- package/lib/esm/solid/Sphere.d.ts.map +1 -1
- package/lib/esm/solid/Sphere.js +4 -2
- package/lib/esm/solid/Sphere.js.map +1 -1
- 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
|
-
/**
|
|
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
|
-
/**
|
|
65
|
-
*
|
|
66
|
-
* * Default
|
|
67
|
-
* * Default
|
|
68
|
-
* * Default
|
|
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() {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
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),
|
|
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 =
|
|
107
|
+
this.chamferAngleBetweenNormals = chamferAngleBetweenNormals.clone();
|
|
101
108
|
}
|
|
102
|
-
/**
|
|
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
|
-
*
|
|
107
|
-
* @param
|
|
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
|
-
/**
|
|
141
|
+
/** Retain none of the duplicates. */
|
|
130
142
|
DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectNone"] = 0] = "SelectNone";
|
|
131
|
-
/**
|
|
143
|
+
/** Retain any one member among duplicates. */
|
|
132
144
|
DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectAny"] = 1] = "SelectAny";
|
|
133
|
-
/**
|
|
145
|
+
/** Retain all members among duplicates. */
|
|
134
146
|
DuplicateFacetClusterSelector[DuplicateFacetClusterSelector["SelectAll"] = 2] = "SelectAll";
|
|
135
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
158
|
-
*
|
|
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
|
|
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
|
|
171
|
-
|
|
188
|
+
const areaNormal = PolygonOps_1.PolygonOps.areaNormal(source.point.getPoint3dArray());
|
|
189
|
+
sum += unitVectorToEye ? areaNormal.dotProduct(unitVectorToEye) : areaNormal.magnitude();
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
|
-
return
|
|
192
|
+
return sum;
|
|
175
193
|
}
|
|
176
|
-
/**
|
|
177
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
217
|
+
sum += myOrigin.tripleProductToPoints(facetOrigin, targetA, targetB);
|
|
199
218
|
}
|
|
200
219
|
}
|
|
201
|
-
return
|
|
220
|
+
return sum / 6.0;
|
|
202
221
|
}
|
|
203
|
-
/**
|
|
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
|
-
* *
|
|
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
|
-
//
|
|
228
|
-
//
|
|
229
|
-
// each
|
|
230
|
-
// each volume contribution is
|
|
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
|
-
//
|
|
237
|
-
//
|
|
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,
|
|
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,
|
|
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
|
-
/**
|
|
291
|
-
*
|
|
292
|
-
* *
|
|
293
|
-
* *
|
|
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
|
-
/**
|
|
303
|
-
*
|
|
304
|
-
* *
|
|
305
|
-
* *
|
|
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.
|
|
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
|
-
/**
|
|
317
|
-
*
|
|
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
|
-
*
|
|
334
|
-
* *
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
return 0;
|
|
424
|
+
if (numPlanar > 0 && numPositive === 0 && numNegative === 0) // planar mesh
|
|
425
|
+
return 0;
|
|
426
|
+
return -2;
|
|
410
427
|
}
|
|
411
428
|
/**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
/**
|
|
418
|
-
*
|
|
419
|
-
* *
|
|
420
|
-
* *
|
|
421
|
-
*
|
|
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
|
-
|
|
439
|
-
|
|
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,
|
|
473
|
-
*
|
|
474
|
-
* @param
|
|
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
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
edges.sortAndCollectClusters(undefined,
|
|
495
|
+
const boundaryEdges = [];
|
|
496
|
+
const nullEdges = [];
|
|
497
|
+
const allOtherEdges = [];
|
|
498
|
+
edges.sortAndCollectClusters(undefined, boundaryEdges, nullEdges, allOtherEdges);
|
|
494
499
|
const badList = [];
|
|
495
|
-
if (includeTypical &&
|
|
496
|
-
badList.push(
|
|
497
|
-
if (
|
|
498
|
-
badList.push(
|
|
499
|
-
if (
|
|
500
|
-
badList.push(
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
603
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
* *
|
|
670
|
-
* *
|
|
671
|
-
* *
|
|
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
|
|
723
|
-
* *
|
|
724
|
-
* *
|
|
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,
|
|
738
|
-
*
|
|
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
|
|
801
|
-
*
|
|
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
|
-
/**
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
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
|
-
|
|
1004
|
+
source.reset();
|
|
945
1005
|
let maxIndex = -1;
|
|
946
|
-
while (
|
|
947
|
-
for (const pointIndex of
|
|
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
|
-
/**
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
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 =
|
|
1020
|
+
const polyface = source.clientPolyface();
|
|
963
1021
|
if (polyface !== undefined && polyface.facetCount !== undefined)
|
|
964
1022
|
return polyface.facetCount;
|
|
965
1023
|
let facetCount = 0;
|
|
966
|
-
|
|
967
|
-
while (
|
|
1024
|
+
source.reset();
|
|
1025
|
+
while (source.moveToNextFacet())
|
|
968
1026
|
++facetCount;
|
|
969
1027
|
return facetCount;
|
|
970
1028
|
}
|
|
971
|
-
/**
|
|
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
|
-
/**
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
* @
|
|
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
|
-
/**
|
|
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,
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
1166
|
-
*
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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); //
|
|
1198
|
-
const newFacetVisitor = polyface.createVisitor(0); //
|
|
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
|
|
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) &&
|
|
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
|
|
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
|
-
* *
|
|
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
|
-
*
|
|
1267
|
-
* @
|
|
1268
|
-
*
|
|
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); //
|
|
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
|
|
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
|
-
/**
|
|
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,
|
|
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); //
|
|
1358
|
-
const newFacetVisitor = polyface.createVisitor(0); //
|
|
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
|
|
1365
|
-
//
|
|
1366
|
-
//
|
|
1367
|
-
//
|
|
1368
|
-
//
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
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
|
|
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.
|
|
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
|
-
/**
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
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`
|
|
1629
|
-
*
|
|
1630
|
-
* @param
|
|
1631
|
-
* @param
|
|
1632
|
-
*
|
|
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
|
-
/**
|
|
1729
|
+
/**
|
|
1730
|
+
* Number of "await" steps executed in recent async calls.
|
|
1677
1731
|
* @internal
|
|
1678
1732
|
*/
|
|
1679
1733
|
PolyfaceQuery.awaitBlockCount = 0;
|