@kitware/vtk.js 25.4.0 → 25.7.0
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/Common/Core/CellArray.d.ts +20 -8
- package/Common/Core/CellArray.js +25 -6
- package/Common/Core/DataArray.d.ts +159 -15
- package/Common/Core/DataArray.js +181 -26
- package/Common/Core/Math/index.js +1 -1
- package/Common/Core/Math.js +1 -1
- package/Common/Core/Points.d.ts +6 -5
- package/Common/Core/Points.js +8 -4
- package/Common/DataModel/AbstractPointLocator.d.ts +54 -0
- package/Common/DataModel/AbstractPointLocator.js +40 -0
- package/Common/DataModel/DataSetAttributes/FieldData.d.ts +33 -22
- package/Common/DataModel/DataSetAttributes/FieldData.js +91 -9
- package/Common/DataModel/DataSetAttributes.d.ts +44 -0
- package/Common/DataModel/DataSetAttributes.js +11 -0
- package/Common/DataModel/IncrementalOctreeNode.d.ts +282 -0
- package/Common/DataModel/IncrementalOctreeNode.js +621 -0
- package/Common/DataModel/IncrementalOctreePointLocator.d.ts +61 -0
- package/Common/DataModel/IncrementalOctreePointLocator.js +342 -0
- package/Common/DataModel/Line.js +1 -1
- package/Common/DataModel/Locator.d.ts +43 -0
- package/Common/DataModel/Locator.js +37 -0
- package/Common/DataModel/Plane.js +1 -1
- package/Common/DataModel/Polygon/Constants.js +12 -0
- package/Common/DataModel/Polygon.d.ts +38 -4
- package/Common/DataModel/Polygon.js +210 -6
- package/Common/DataModel/Quad/Constants.js +9 -0
- package/Common/DataModel/Quad.d.ts +91 -0
- package/Common/DataModel/Quad.js +235 -0
- package/Common/DataModel/Triangle.d.ts +106 -81
- package/Common/DataModel/Triangle.js +1 -1
- package/Common/Transform/LandmarkTransform.js +1 -1
- package/Filters/Core/PolyDataNormals.js +1 -1
- package/Filters/General/ClipClosedSurface/Constants.js +10 -0
- package/Filters/General/ClipClosedSurface/ccsEdgeLocator.js +40 -0
- package/Filters/General/ClipClosedSurface.d.ts +95 -0
- package/Filters/General/ClipClosedSurface.js +970 -0
- package/Filters/General/ContourTriangulator/Constants.js +6 -0
- package/Filters/General/ContourTriangulator/helper.js +1951 -0
- package/Filters/General/ContourTriangulator.d.ts +122 -0
- package/Filters/General/ContourTriangulator.js +200 -0
- package/Filters/General/ImageMarchingCubes.js +1 -1
- package/Filters/General/MoleculeToRepresentation.js +1 -1
- package/Filters/General/OBBTree.js +1 -1
- package/Filters/General/TubeFilter.js +1 -1
- package/Filters/General/WindowedSincPolyDataFilter.js +1 -1
- package/Filters/Sources/PlaneSource.js +1 -1
- package/Filters/Texture/TextureMapToPlane.js +1 -1
- package/IO/Core/DataAccessHelper/HtmlDataAccessHelper.js +4 -8
- package/IO/Core/DataAccessHelper/HttpDataAccessHelper.js +6 -14
- package/IO/Core/DataAccessHelper/JSZipDataAccessHelper.js +60 -57
- package/IO/Core/ZipMultiDataSetReader.js +19 -29
- package/IO/Core/ZipMultiDataSetWriter.js +7 -23
- package/IO/Misc/SkyboxReader.js +67 -75
- package/IO/XML/XMLReader.js +2 -2
- package/IO/XML/XMLWriter.js +2 -2
- package/Interaction/Manipulators/KeyboardCameraManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraAxisRotateManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraTrackballPanManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraTrackballRotateManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraUnicamManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraUnicamRotateManipulator.js +1 -1
- package/Interaction/Style/InteractorStyleMPRSlice.js +1 -1
- package/Interaction/Style/InteractorStyleTrackballCamera.js +16 -0
- package/Proxy/Core/View2DProxy.js +1 -1
- package/Rendering/Core/Actor2D.d.ts +12 -6
- package/Rendering/Core/Camera.js +1 -1
- package/Rendering/Core/CellPicker.js +3 -2
- package/Rendering/Core/ColorTransferFunction/ColorMaps.d.ts +24 -0
- package/Rendering/Core/Light.js +1 -1
- package/Rendering/Core/Picker.js +1 -1
- package/Rendering/Core/Property2D.d.ts +1 -1
- package/Rendering/Core/VolumeProperty.d.ts +4 -4
- package/Rendering/Core/VolumeProperty.js +1 -1
- package/Rendering/OpenGL/PolyDataMapper.js +1 -1
- package/Rendering/OpenGL/RenderWindow/ContextProxy.js +65 -0
- package/Rendering/OpenGL/RenderWindow.js +3 -1
- package/Rendering/WebGPU/BufferManager.js +1 -1
- package/Rendering/WebGPU/ForwardPass.js +93 -15
- package/Rendering/WebGPU/OpaquePass.js +1 -1
- package/Rendering/WebGPU/OrderIndependentTranslucentPass.js +1 -1
- package/Rendering/WebGPU/RenderEncoder.js +9 -5
- package/Rendering/WebGPU/RenderWindow.js +15 -13
- package/Rendering/WebGPU/TextureView.js +15 -2
- package/Rendering/WebGPU/VolumePass.js +1 -1
- package/Widgets/Core/StateBuilder/orientationMixin.js +1 -1
- package/Widgets/Manipulators/LineManipulator.js +1 -1
- package/Widgets/Manipulators/TrackballManipulator.js +1 -1
- package/Widgets/Representations/ResliceCursorContextRepresentation.js +1 -1
- package/Widgets/Widgets3D/LineWidget/behavior.js +1 -1
- package/Widgets/Widgets3D/LineWidget/helpers.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget.js +1 -1
- package/Widgets/Widgets3D/ShapeWidget/behavior.js +1 -1
- package/index.d.ts +8 -0
- package/index.js +0 -2
- package/package.json +3 -4
- package/ThirdParty/index.js +0 -9
|
@@ -0,0 +1,1951 @@
|
|
|
1
|
+
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
2
|
+
import macro from '../../../macros.js';
|
|
3
|
+
import vtkPoints from '../../../Common/Core/Points.js';
|
|
4
|
+
import { s as subtract, k as cross, d as dot, n as norm, f as distance2BetweenPoints, l as add, m as normalize } from '../../../Common/Core/Math/index.js';
|
|
5
|
+
import vtkLine from '../../../Common/DataModel/Line.js';
|
|
6
|
+
import vtkPolygon from '../../../Common/DataModel/Polygon.js';
|
|
7
|
+
import vtkIncrementalOctreePointLocator from '../../../Common/DataModel/IncrementalOctreePointLocator.js';
|
|
8
|
+
import { VtkDataTypes } from '../../../Common/Core/DataArray/Constants.js';
|
|
9
|
+
import { CCS_POLYGON_TOLERANCE } from './Constants.js';
|
|
10
|
+
import { PolygonWithPointIntersectionState } from '../../../Common/DataModel/Polygon/Constants.js';
|
|
11
|
+
|
|
12
|
+
var vtkErrorMacro = macro.vtkErrorMacro;
|
|
13
|
+
/**
|
|
14
|
+
* Reverse the elements between the indices firstIdx and lastIdx of the given array arr.
|
|
15
|
+
*
|
|
16
|
+
* @param {Array|TypedArray} arr
|
|
17
|
+
* @param {Number} firstIdx
|
|
18
|
+
* @param {Number} lastIdx
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
function reverseElements(arr) {
|
|
22
|
+
var firstIdx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
|
|
23
|
+
var lastIdx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
|
|
24
|
+
var first = firstIdx !== null && firstIdx !== void 0 ? firstIdx : 0;
|
|
25
|
+
var last = lastIdx !== null && lastIdx !== void 0 ? lastIdx : arr.length - 1;
|
|
26
|
+
var mid = first + Math.floor((last - first) / 2);
|
|
27
|
+
|
|
28
|
+
for (var i = first; i <= mid; ++i) {
|
|
29
|
+
var _ref = [arr[last - (i - first)], arr[i]];
|
|
30
|
+
arr[i] = _ref[0];
|
|
31
|
+
arr[last - (i - first)] = _ref[1];
|
|
32
|
+
}
|
|
33
|
+
} // ---------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compute the quality of a triangle.
|
|
37
|
+
*
|
|
38
|
+
* @param {Vector3} p0
|
|
39
|
+
* @param {Vector3} p1
|
|
40
|
+
* @param {Vector3} p2
|
|
41
|
+
* @param {Vector3} normal
|
|
42
|
+
* @returns {Number}
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
function vtkCCSTriangleQuality(p0, p1, p2, normal) {
|
|
46
|
+
var u = [];
|
|
47
|
+
var v = [];
|
|
48
|
+
var w = [];
|
|
49
|
+
subtract(p1, p0, u);
|
|
50
|
+
subtract(p2, p1, v);
|
|
51
|
+
subtract(p0, p2, w);
|
|
52
|
+
var area2 = (u[1] * v[2] - u[2] * v[1]) * normal[0] + (u[2] * v[0] - u[0] * v[2]) * normal[1] + (u[0] * v[1] - u[1] * v[0]) * normal[2];
|
|
53
|
+
var perim = Math.sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]) + Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) + Math.sqrt(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]);
|
|
54
|
+
perim *= perim; // square the perimeter
|
|
55
|
+
|
|
56
|
+
perim = perim !== 0 ? perim : 1.0; // use a normalization factor so equilateral quality is 1.0
|
|
57
|
+
|
|
58
|
+
return area2 / perim * 10.392304845413264;
|
|
59
|
+
} // ---------------------------------------------------
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Insert a triangle, and subdivide that triangle if one of
|
|
63
|
+
* its edges originally had more than two points before
|
|
64
|
+
* vtkCCSFindTrueEdges was called. Is called by vtkCCSTriangulate.
|
|
65
|
+
*
|
|
66
|
+
* @param {vtkCellArray} polys
|
|
67
|
+
* @param {Array|TypedArray} poly
|
|
68
|
+
* @param {Vector3} trids
|
|
69
|
+
* @param {Array|TypedArray} polyEdges
|
|
70
|
+
* @param {Array|TypedArray} originalEdges
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
function vtkCCSInsertTriangle(polys, poly, trids, polyEdges, originalEdges) {
|
|
74
|
+
var nextVert = [1, 2, 0]; // To store how many of originalEdges match
|
|
75
|
+
|
|
76
|
+
var edgeCount = 0;
|
|
77
|
+
var edgeLocs = [-1, -1, -1]; // Check for original edge matches
|
|
78
|
+
|
|
79
|
+
for (var vert = 0; vert < 3; vert++) {
|
|
80
|
+
var currId = trids[vert];
|
|
81
|
+
var edgeLoc = polyEdges[currId];
|
|
82
|
+
|
|
83
|
+
if (edgeLoc >= 0) {
|
|
84
|
+
var nextId = currId + 1;
|
|
85
|
+
|
|
86
|
+
if (nextId === poly.length) {
|
|
87
|
+
nextId = 0;
|
|
88
|
+
} // Is the triangle edge a polygon edge?
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if (nextId === trids[nextVert[vert]]) {
|
|
92
|
+
edgeLocs[vert] = edgeLoc;
|
|
93
|
+
edgeCount++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (edgeCount === 0) {
|
|
99
|
+
// No special edge handling, so just do one triangle
|
|
100
|
+
polys.insertNextCell([poly[trids[0]], poly[trids[1]], poly[trids[2]]]);
|
|
101
|
+
} else {
|
|
102
|
+
// Make triangle fans for edges with extra points
|
|
103
|
+
var edgePts = [[poly[trids[0]], poly[trids[1]]], [poly[trids[1]], poly[trids[2]]], [poly[trids[2]], poly[trids[0]]]]; // Find out which edge has the most extra points
|
|
104
|
+
|
|
105
|
+
var maxPoints = 0;
|
|
106
|
+
var currSide = 0;
|
|
107
|
+
|
|
108
|
+
for (var i = 0; i < 3; i++) {
|
|
109
|
+
if (edgeLocs[i] >= 0) {
|
|
110
|
+
var _edgeLoc = edgeLocs[i];
|
|
111
|
+
var npts = originalEdges[_edgeLoc];
|
|
112
|
+
var pts = originalEdges.slice(_edgeLoc + 1, _edgeLoc + 1 + npts);
|
|
113
|
+
|
|
114
|
+
if (!(edgePts[i][0] === pts[0] || edgePts[i][1] === pts[npts - 1])) {
|
|
115
|
+
vtkErrorMacro('assertion error in vtkCCSInsertTriangle');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (npts > maxPoints) {
|
|
119
|
+
maxPoints = npts;
|
|
120
|
+
currSide = i;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
edgePts[i] = pts;
|
|
124
|
+
}
|
|
125
|
+
} // Find the edges before/after the edge with most points
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
var prevSide = (currSide + 2) % 3;
|
|
129
|
+
var nextSide = (currSide + 1) % 3; // If other edges have only 2 points, nothing to do with them
|
|
130
|
+
|
|
131
|
+
var prevNeeded = edgePts[prevSide].length > 2;
|
|
132
|
+
var nextNeeded = edgePts[nextSide].length > 2; // The tail is the common point in the triangle fan
|
|
133
|
+
|
|
134
|
+
var tailPtIds = [];
|
|
135
|
+
tailPtIds[prevSide] = edgePts[currSide][1];
|
|
136
|
+
tailPtIds[currSide] = edgePts[prevSide][0];
|
|
137
|
+
tailPtIds[nextSide] = edgePts[currSide][edgePts[currSide].length - 2]; // Go through the sides and make the fans
|
|
138
|
+
|
|
139
|
+
for (var side = 0; side < 3; side++) {
|
|
140
|
+
if ((side !== prevSide || prevNeeded) && (side !== nextSide || nextNeeded)) {
|
|
141
|
+
var m = 0;
|
|
142
|
+
var n = edgePts[side].length - 1;
|
|
143
|
+
|
|
144
|
+
if (side === currSide) {
|
|
145
|
+
m += prevNeeded;
|
|
146
|
+
n -= nextNeeded;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (var k = m; k < n; k++) {
|
|
150
|
+
polys.insertNextCell([edgePts[side][k], edgePts[side][k + 1], tailPtIds[side]]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} // ---------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Triangulate a polygon that has been simplified by FindTrueEdges.
|
|
159
|
+
* This will re-insert the original edges. The output triangles are
|
|
160
|
+
* appended to "polys" and, for each stored triangle, "color" will
|
|
161
|
+
* be added to "scalars". The final two arguments (polygon and
|
|
162
|
+
* triangles) are only for temporary storage.
|
|
163
|
+
* The return value is true if triangulation was successful.
|
|
164
|
+
*
|
|
165
|
+
* @param {Array} poly
|
|
166
|
+
* @param {vtkPoints} points
|
|
167
|
+
* @param {Array} polyEdges
|
|
168
|
+
* @param {Array} originalEdges
|
|
169
|
+
* @param {vtkCellArray} polys
|
|
170
|
+
* @param {Vector3} normal
|
|
171
|
+
* @returns {boolean}
|
|
172
|
+
*/
|
|
173
|
+
|
|
174
|
+
function vtkCCSTriangulate(poly, points, polyEdges, originalEdges, polys, normal) {
|
|
175
|
+
var n = poly.length; // If the poly is a line, then skip it
|
|
176
|
+
|
|
177
|
+
if (n < 3) {
|
|
178
|
+
return true;
|
|
179
|
+
} // If the poly is a triangle, then pass it
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if (n === 3) {
|
|
183
|
+
var trids = [0, 1, 2];
|
|
184
|
+
vtkCCSInsertTriangle(polys, poly, trids, polyEdges, originalEdges);
|
|
185
|
+
return true;
|
|
186
|
+
} // If the poly has 4 or more points, triangulate it
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
var triangulationFailure = false;
|
|
190
|
+
var ppoint = [];
|
|
191
|
+
var point = [];
|
|
192
|
+
var npoint = [];
|
|
193
|
+
var i = 0;
|
|
194
|
+
var j = 0;
|
|
195
|
+
var k = 0;
|
|
196
|
+
var verts = [];
|
|
197
|
+
verts.length = n;
|
|
198
|
+
|
|
199
|
+
for (i = 0; i < n; i++) {
|
|
200
|
+
verts[i] = [i, 0];
|
|
201
|
+
} // compute the triangle quality for each vert
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
k = n - 2;
|
|
205
|
+
points.getPoint(poly[verts[k][0]], point);
|
|
206
|
+
i = n - 1;
|
|
207
|
+
points.getPoint(poly[verts[i][0]], npoint);
|
|
208
|
+
var concave = 0;
|
|
209
|
+
var maxq = 0;
|
|
210
|
+
var maxi = 0;
|
|
211
|
+
|
|
212
|
+
for (j = 0; j < n; j++) {
|
|
213
|
+
var _ref2 = [point, npoint, ppoint];
|
|
214
|
+
ppoint = _ref2[0];
|
|
215
|
+
point = _ref2[1];
|
|
216
|
+
npoint = _ref2[2];
|
|
217
|
+
points.getPoint(poly[verts[j][0]], npoint);
|
|
218
|
+
var q = vtkCCSTriangleQuality(ppoint, point, npoint, normal);
|
|
219
|
+
|
|
220
|
+
if (q > maxq) {
|
|
221
|
+
maxi = i;
|
|
222
|
+
maxq = q;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
concave += q < 0;
|
|
226
|
+
verts[i][1] = q;
|
|
227
|
+
i = j;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
var foundEar; // perform the ear-cut triangulation
|
|
231
|
+
|
|
232
|
+
for (;;) {
|
|
233
|
+
// if no potential ears were found, then fail
|
|
234
|
+
if (maxq <= Number.MIN_VALUE) {
|
|
235
|
+
triangulationFailure = true;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
i = maxi;
|
|
240
|
+
j = i + 1 !== n ? i + 1 : 0;
|
|
241
|
+
k = i !== 0 ? i - 1 : n - 1;
|
|
242
|
+
|
|
243
|
+
if (verts[i][1] > 0) {
|
|
244
|
+
foundEar = true;
|
|
245
|
+
points.getPoint(poly[verts[j][0]], npoint);
|
|
246
|
+
points.getPoint(poly[verts[k][0]], ppoint); // only do ear check if there are concave vertices
|
|
247
|
+
|
|
248
|
+
if (concave) {
|
|
249
|
+
// get the normal of the split plane
|
|
250
|
+
var v = [];
|
|
251
|
+
var u = [];
|
|
252
|
+
subtract(npoint, ppoint, v);
|
|
253
|
+
cross(v, normal, u);
|
|
254
|
+
var d = dot(ppoint, u);
|
|
255
|
+
var jj = j + 1 !== n ? j + 1 : 0;
|
|
256
|
+
var x = [];
|
|
257
|
+
points.getPoint(poly[verts[jj][0]], x);
|
|
258
|
+
var side = dot(x, u) < d;
|
|
259
|
+
var foundNegative = side; // check for crossings of the split plane
|
|
260
|
+
|
|
261
|
+
jj = jj + 1 !== n ? jj + 1 : 0;
|
|
262
|
+
var y = [];
|
|
263
|
+
var s = [];
|
|
264
|
+
var t = [];
|
|
265
|
+
|
|
266
|
+
for (; foundEar && jj !== k; jj = jj + 1 !== n ? jj + 1 : 0) {
|
|
267
|
+
var _ref3 = [y, x];
|
|
268
|
+
x = _ref3[0];
|
|
269
|
+
y = _ref3[1];
|
|
270
|
+
points.getPoint(poly[verts[jj][0]], x);
|
|
271
|
+
var sside = dot(x, u) < d; // XOR
|
|
272
|
+
|
|
273
|
+
if (side ? !sside : sside) {
|
|
274
|
+
side = !side;
|
|
275
|
+
foundNegative = true;
|
|
276
|
+
foundEar = vtkLine.intersection(ppoint, npoint, x, y, s, t) === vtkLine.IntersectionState.NO_INTERSECTION;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
foundEar && (foundEar = foundNegative);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!foundEar) {
|
|
284
|
+
// don't try again until it is split
|
|
285
|
+
verts[i][1] = Number.MIN_VALUE;
|
|
286
|
+
} else {
|
|
287
|
+
// create a triangle from vertex and neighbors
|
|
288
|
+
var _trids = [verts[i][0], verts[j][0], verts[k][0]];
|
|
289
|
+
vtkCCSInsertTriangle(polys, poly, _trids, polyEdges, originalEdges); // remove the vertex i
|
|
290
|
+
|
|
291
|
+
verts.splice(i, 1);
|
|
292
|
+
k -= i === 0;
|
|
293
|
+
j -= j !== 0; // break if this was final triangle
|
|
294
|
+
|
|
295
|
+
if (--n < 3) {
|
|
296
|
+
break;
|
|
297
|
+
} // re-compute quality of previous point
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
var kk = k !== 0 ? k - 1 : n - 1;
|
|
301
|
+
points.getPoint(poly[verts[kk][0]], point);
|
|
302
|
+
var kq = vtkCCSTriangleQuality(point, ppoint, npoint, normal);
|
|
303
|
+
concave -= verts[k][1] < 0 && kq >= 0;
|
|
304
|
+
verts[k][1] = kq; // re-compute quality of next point
|
|
305
|
+
|
|
306
|
+
var _jj = j + 1 !== n ? j + 1 : 0;
|
|
307
|
+
|
|
308
|
+
points.getPoint(poly[verts[_jj][0]], point);
|
|
309
|
+
var jq = vtkCCSTriangleQuality(ppoint, npoint, point, normal);
|
|
310
|
+
concave -= verts[j][1] < 0 && jq >= 0;
|
|
311
|
+
verts[j][1] = jq;
|
|
312
|
+
}
|
|
313
|
+
} // find the highest-quality ear candidate
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
maxi = 0;
|
|
317
|
+
maxq = verts[0][1];
|
|
318
|
+
|
|
319
|
+
for (i = 1; i < n; i++) {
|
|
320
|
+
var _q = verts[i][1];
|
|
321
|
+
|
|
322
|
+
if (_q > maxq) {
|
|
323
|
+
maxi = i;
|
|
324
|
+
maxq = _q;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return !triangulationFailure;
|
|
330
|
+
} // ---------------------------------------------------
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Create polygons from line segments.
|
|
334
|
+
*
|
|
335
|
+
* @param {vtkPolyData} polyData
|
|
336
|
+
* @param {Number} firstLine
|
|
337
|
+
* @param {Number} endLine
|
|
338
|
+
* @param {Boolean} oriented
|
|
339
|
+
* @param {Array} newPolys
|
|
340
|
+
* @param {Array} incompletePolys
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
function vtkCCSMakePolysFromLines(polyData, firstLine, endLine, oriented, newPolys, incompletePolys) {
|
|
344
|
+
var npts = 0;
|
|
345
|
+
var pts = []; // Bitfield for marking lines as used
|
|
346
|
+
|
|
347
|
+
var usedLines = new Uint8Array(endLine - firstLine); // defaults to 0
|
|
348
|
+
// Require cell links to get lines from pointIds
|
|
349
|
+
|
|
350
|
+
polyData.buildLinks(polyData.getPoints().getNumberOfPoints());
|
|
351
|
+
var numNewPolys = 0;
|
|
352
|
+
var remainingLines = endLine - firstLine;
|
|
353
|
+
|
|
354
|
+
while (remainingLines > 0) {
|
|
355
|
+
// Create a new poly
|
|
356
|
+
var polyId = numNewPolys++;
|
|
357
|
+
var poly = [];
|
|
358
|
+
newPolys.push(poly);
|
|
359
|
+
var lineId = 0;
|
|
360
|
+
var completePoly = false; // start the poly
|
|
361
|
+
|
|
362
|
+
for (lineId = firstLine; lineId < endLine; lineId++) {
|
|
363
|
+
if (!usedLines[lineId - firstLine]) {
|
|
364
|
+
pts = polyData.getCellPoints(lineId).cellPointIds;
|
|
365
|
+
npts = pts.length;
|
|
366
|
+
var n = npts;
|
|
367
|
+
|
|
368
|
+
if (npts > 2 && pts[0] === pts[npts - 1]) {
|
|
369
|
+
n = npts - 1;
|
|
370
|
+
completePoly = true;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
poly.length = n;
|
|
374
|
+
|
|
375
|
+
for (var i = 0; i < n; i++) {
|
|
376
|
+
poly[i] = pts[i];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
usedLines[lineId - firstLine] = 1;
|
|
384
|
+
remainingLines--;
|
|
385
|
+
var noLinesMatch = remainingLines === 0 && !completePoly;
|
|
386
|
+
|
|
387
|
+
while (!completePoly && !noLinesMatch && remainingLines > 0) {
|
|
388
|
+
// This is cleared if a match is found
|
|
389
|
+
noLinesMatch = true; // Number of points in the poly
|
|
390
|
+
|
|
391
|
+
var npoly = poly.length;
|
|
392
|
+
var lineEndPts = [];
|
|
393
|
+
var endPts = [poly[npoly - 1], poly[0]]; // For both open ends of the polygon
|
|
394
|
+
|
|
395
|
+
for (var endIdx = 0; endIdx < 2; endIdx++) {
|
|
396
|
+
var matches = [];
|
|
397
|
+
var cells = polyData.getPointCells(endPts[endIdx]); // Go through all lines that contain this endpoint
|
|
398
|
+
|
|
399
|
+
for (var icell = 0; icell < cells.length; icell++) {
|
|
400
|
+
lineId = cells[icell];
|
|
401
|
+
|
|
402
|
+
if (lineId >= firstLine && lineId < endLine && !usedLines[lineId - firstLine]) {
|
|
403
|
+
pts = polyData.getCellPoints(lineId).cellPointIds;
|
|
404
|
+
npts = pts.length;
|
|
405
|
+
lineEndPts[0] = pts[0];
|
|
406
|
+
lineEndPts[1] = pts[npts - 1]; // Check that poly end matches line end
|
|
407
|
+
|
|
408
|
+
if (endPts[endIdx] === lineEndPts[endIdx] || !oriented && endPts[endIdx] === lineEndPts[1 - endIdx]) {
|
|
409
|
+
matches.push(lineId);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (!matches.length === 0) {
|
|
415
|
+
// Multiple matches mean we need to decide which path to take
|
|
416
|
+
if (matches.length > 1) {
|
|
417
|
+
// Remove double-backs
|
|
418
|
+
var k = matches.length;
|
|
419
|
+
|
|
420
|
+
do {
|
|
421
|
+
lineId = matches[--k];
|
|
422
|
+
pts = polyData.getCellPoints(lineId).cellPointIds;
|
|
423
|
+
npts = pts.length;
|
|
424
|
+
lineEndPts[0] = pts[0];
|
|
425
|
+
lineEndPts[1] = pts[npts - 1]; // check if line is reversed
|
|
426
|
+
|
|
427
|
+
var r = endPts[endIdx] !== lineEndPts[endIdx];
|
|
428
|
+
|
|
429
|
+
if (!r && (endIdx === 0 && poly[npoly - 2] === pts[1] || endIdx === 1 && poly[1] === pts[npts - 2]) || r && (endIdx === 0 && poly[npoly - 2] === pts[npts - 2] || endIdx === 1 && poly[1] === pts[1])) {
|
|
430
|
+
matches.splice(k, 1);
|
|
431
|
+
}
|
|
432
|
+
} while (k > 0 && matches.length > 1); // If there are multiple matches due to intersections,
|
|
433
|
+
// they should be dealt with here.
|
|
434
|
+
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
lineId = matches[0];
|
|
438
|
+
pts = polyData.getCellPoints(lineId).cellPointIds;
|
|
439
|
+
npts = pts.length;
|
|
440
|
+
lineEndPts[0] = pts[0];
|
|
441
|
+
lineEndPts[1] = pts[npts - 1]; // Do both ends match?
|
|
442
|
+
|
|
443
|
+
if (endPts[endIdx] === lineEndPts[endIdx]) {
|
|
444
|
+
completePoly = endPts[1 - endIdx] === lineEndPts[1 - endIdx];
|
|
445
|
+
} else {
|
|
446
|
+
completePoly = endPts[1 - endIdx] === lineEndPts[endIdx];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (endIdx === 0) {
|
|
450
|
+
for (var _i = 1; _i < npts - (completePoly ? 1 : 0); _i++) {
|
|
451
|
+
poly.push(pts[_i]);
|
|
452
|
+
}
|
|
453
|
+
} else {
|
|
454
|
+
for (var _i2 = completePoly ? 1 : 0; _i2 < npts - 1; _i2++) {
|
|
455
|
+
poly.unshift(pts[_i2]);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (endPts[endIdx] !== lineEndPts[endIdx]) {
|
|
460
|
+
// reverse the ids in the added line
|
|
461
|
+
var pit = poly.length;
|
|
462
|
+
var ptsIt = completePoly ? 1 : 0;
|
|
463
|
+
var ptsEnd = npts - 1;
|
|
464
|
+
|
|
465
|
+
if (endIdx === 1) {
|
|
466
|
+
pit = npts - 1 - (completePoly ? 1 : 0);
|
|
467
|
+
ptsIt = pts + 1;
|
|
468
|
+
ptsEnd = pts + npts - (completePoly ? 1 : 0);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
while (ptsIt !== ptsEnd) {
|
|
472
|
+
poly[--pit] = poly[ptsIt++];
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
usedLines[lineId - firstLine] = 1;
|
|
477
|
+
remainingLines--;
|
|
478
|
+
noLinesMatch = false;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
} // Check for incomplete polygons
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
if (noLinesMatch) {
|
|
485
|
+
incompletePolys.push(polyId);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
} // ---------------------------------------------------
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Join polys that have loose ends, as indicated by incompletePolys.
|
|
492
|
+
* Any polys created will have a normal opposite to the supplied normal,
|
|
493
|
+
* and any new edges that are created will be on the hull of the point set.
|
|
494
|
+
* Shorter edges will be preferred over long edges.
|
|
495
|
+
*
|
|
496
|
+
* @param {Array[]} polys
|
|
497
|
+
* @param {Array} incompletePolys
|
|
498
|
+
* @param {vtkPoints} points
|
|
499
|
+
* @param {Vector3} normal
|
|
500
|
+
*/
|
|
501
|
+
|
|
502
|
+
function vtkCCSJoinLooseEnds(polys, incompletePolys, points, normal) {
|
|
503
|
+
// Relative tolerance for checking whether an edge is on the hull
|
|
504
|
+
var tol = CCS_POLYGON_TOLERANCE; // A list of polys to remove when everything is done
|
|
505
|
+
|
|
506
|
+
var removePolys = [];
|
|
507
|
+
var p1 = [];
|
|
508
|
+
var p2 = [];
|
|
509
|
+
var poly1;
|
|
510
|
+
var poly2;
|
|
511
|
+
var pt1;
|
|
512
|
+
var pt2;
|
|
513
|
+
var dMin;
|
|
514
|
+
var iMin;
|
|
515
|
+
var v;
|
|
516
|
+
var d;
|
|
517
|
+
var n = incompletePolys.length;
|
|
518
|
+
|
|
519
|
+
while (n !== 0) {
|
|
520
|
+
poly1 = polys[incompletePolys[n - 1]];
|
|
521
|
+
pt1 = poly1[poly1.length - 1];
|
|
522
|
+
points.getPoint(pt1, p1);
|
|
523
|
+
dMin = Number.MAX_VALUE;
|
|
524
|
+
iMin = 0;
|
|
525
|
+
|
|
526
|
+
for (var _i3 = 0; _i3 < n; _i3++) {
|
|
527
|
+
poly2 = polys[incompletePolys[_i3]];
|
|
528
|
+
pt2 = poly2[0];
|
|
529
|
+
points.getPoint(pt2, p2); // The next few steps verify that edge [p1, p2] is on the hull
|
|
530
|
+
|
|
531
|
+
v = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
|
|
532
|
+
d = norm(v);
|
|
533
|
+
|
|
534
|
+
if (d !== 0) {
|
|
535
|
+
v[0] /= d;
|
|
536
|
+
v[1] /= d;
|
|
537
|
+
v[2] /= d;
|
|
538
|
+
} // Compute the midpoint of the edge
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
var pm = [0.5 * (p1[0] + p2[0]), 0.5 * (p1[1] + p2[1]), 0.5 * (p1[2] + p2[2])]; // Create a plane equation
|
|
542
|
+
|
|
543
|
+
var pc = [];
|
|
544
|
+
cross(normal, v, pc);
|
|
545
|
+
pc[3] = -dot(pc, pm); // Check that all points are inside the plane. If they aren't, then
|
|
546
|
+
// the edge is not on the hull of the pointset.
|
|
547
|
+
|
|
548
|
+
var badPoint = false;
|
|
549
|
+
var m = polys.length;
|
|
550
|
+
var p = [];
|
|
551
|
+
|
|
552
|
+
for (var j = 0; j < m && !badPoint; j++) {
|
|
553
|
+
var poly = polys[j];
|
|
554
|
+
var npts = poly.length;
|
|
555
|
+
|
|
556
|
+
for (var k = 0; k < npts; k++) {
|
|
557
|
+
var ptId = poly[k];
|
|
558
|
+
|
|
559
|
+
if (ptId !== pt1 && ptId !== pt2) {
|
|
560
|
+
points.getPoint(ptId, p);
|
|
561
|
+
var val = p[0] * pc[0] + p[1] * pc[1] + p[2] * pc[2] + pc[3];
|
|
562
|
+
var r2 = distance2BetweenPoints(p, pm); // Check distance from plane against the tolerance
|
|
563
|
+
|
|
564
|
+
if (val < 0 && val * val > tol * tol * r2) {
|
|
565
|
+
badPoint = true;
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
} // If no bad points, then this edge is a candidate
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
if (!badPoint && d < dMin) {
|
|
573
|
+
dMin = d;
|
|
574
|
+
iMin = _i3;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
} // If a match was found, append the polys
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
if (dMin < Number.MAX_VALUE) {
|
|
581
|
+
// Did the poly match with itself?
|
|
582
|
+
if (iMin === n - 1) {
|
|
583
|
+
// Mark the poly as closed
|
|
584
|
+
incompletePolys.pop();
|
|
585
|
+
} else {
|
|
586
|
+
var _poly;
|
|
587
|
+
|
|
588
|
+
var id2 = incompletePolys[iMin]; // Combine the polys
|
|
589
|
+
// for (let i = 1; i < polys[id2].length; i++) {
|
|
590
|
+
// poly1.push(polys[id2][i]);
|
|
591
|
+
// }
|
|
592
|
+
|
|
593
|
+
(_poly = poly1).push.apply(_poly, _toConsumableArray(polys[id2])); // Erase the second poly
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
removePolys.push(id2);
|
|
597
|
+
incompletePolys.splice(iMin, 1);
|
|
598
|
+
}
|
|
599
|
+
} else {
|
|
600
|
+
// If no match, erase this poly from consideration
|
|
601
|
+
removePolys.push(incompletePolys[n - 1]);
|
|
602
|
+
incompletePolys.pop();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
n = incompletePolys.length;
|
|
606
|
+
} // Remove polys that couldn't be completed
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
removePolys.sort(function (a, b) {
|
|
610
|
+
return a - b;
|
|
611
|
+
});
|
|
612
|
+
var i = removePolys.length;
|
|
613
|
+
|
|
614
|
+
while (i > 0) {
|
|
615
|
+
// Remove items in reverse order
|
|
616
|
+
polys.splice(removePolys[--i], 1);
|
|
617
|
+
} // Clear the incompletePolys vector, it's indices are no longer valid
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
incompletePolys.length = 0;
|
|
621
|
+
} // ---------------------------------------------------
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Given three vectors p.p1, p.p2, and p.p3, this routine
|
|
625
|
+
* checks to see if progressing from p1 to p2 to p3 is a clockwise
|
|
626
|
+
* or counterclockwise progression with respect to the normal.
|
|
627
|
+
* The return value is -1 for clockwise, +1 for counterclockwise,
|
|
628
|
+
* and 0 if any two of the vectors are coincident.
|
|
629
|
+
*
|
|
630
|
+
* @param {Vector3} p
|
|
631
|
+
* @param {Vector3} p1
|
|
632
|
+
* @param {Vector3} p2
|
|
633
|
+
* @param {Vector3} p3
|
|
634
|
+
* @param {Vector3} normal
|
|
635
|
+
* @returns {Number}
|
|
636
|
+
*/
|
|
637
|
+
|
|
638
|
+
function vtkCCSVectorProgression(p, p1, p2, p3, normal) {
|
|
639
|
+
var v1 = [p1[0] - p[0], p1[1] - p[1], p1[2] - p[2]];
|
|
640
|
+
var v2 = [p2[0] - p[0], p2[1] - p[1], p2[2] - p[2]];
|
|
641
|
+
var v3 = [p3[0] - p[0], p3[1] - p[1], p3[2] - p[2]];
|
|
642
|
+
var w1 = [];
|
|
643
|
+
var w2 = [];
|
|
644
|
+
cross(v2, v1, w1);
|
|
645
|
+
cross(v2, v3, w2);
|
|
646
|
+
var s1 = dot(w1, normal);
|
|
647
|
+
var s2 = dot(w2, normal);
|
|
648
|
+
|
|
649
|
+
if (s1 !== 0 && s2 !== 0) {
|
|
650
|
+
var sb1 = s1 < 0;
|
|
651
|
+
var sb2 = s2 < 0; // if sines have different signs
|
|
652
|
+
// XOR
|
|
653
|
+
|
|
654
|
+
if (sb1 ? !sb2 : sb2) {
|
|
655
|
+
// return -1 if s2 is -ve
|
|
656
|
+
return 1 - 2 * sb2;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
var c1 = dot(v2, v1);
|
|
660
|
+
var l1 = norm(v1);
|
|
661
|
+
var c2 = dot(v2, v3);
|
|
662
|
+
var l2 = norm(v3); // ck is the difference of the cosines, flipped in sign if sines are +ve
|
|
663
|
+
|
|
664
|
+
var ck = (c2 * l2 - c1 * l1) * (1 - sb1 * 2);
|
|
665
|
+
|
|
666
|
+
if (ck !== 0) {
|
|
667
|
+
// return the sign of ck
|
|
668
|
+
return 1 - 2 * (ck < 0);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return 0;
|
|
673
|
+
} // ---------------------------------------------------
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Check for self-intersection. Split the figure-eights.
|
|
677
|
+
* This assumes that all intersections occur at existing
|
|
678
|
+
* vertices, i.e. no new vertices will be created. Returns
|
|
679
|
+
* the number of splits made.
|
|
680
|
+
*
|
|
681
|
+
* @param {Array[]} polys
|
|
682
|
+
* @param {vtkPoints} points
|
|
683
|
+
* @param {Array} polyGroups
|
|
684
|
+
* @param {Array} polyEdges
|
|
685
|
+
* @param {Vector3} normal
|
|
686
|
+
* @param {Boolean} oriented
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
function vtkCCSSplitAtPinchPoints(polys, points, polyGroups, polyEdges, normal, oriented) {
|
|
690
|
+
var tryPoints = vtkPoints.newInstance({
|
|
691
|
+
dataType: VtkDataTypes.DOUBLE,
|
|
692
|
+
empty: true
|
|
693
|
+
});
|
|
694
|
+
var locator = vtkIncrementalOctreePointLocator.newInstance();
|
|
695
|
+
var splitCount = 0;
|
|
696
|
+
var poly;
|
|
697
|
+
var n;
|
|
698
|
+
var bounds;
|
|
699
|
+
var tol;
|
|
700
|
+
|
|
701
|
+
for (var i = 0; i < polys.length; i++) {
|
|
702
|
+
poly = polys[i];
|
|
703
|
+
n = poly.length;
|
|
704
|
+
bounds = [];
|
|
705
|
+
tol = CCS_POLYGON_TOLERANCE * Math.sqrt(vtkPolygon.getBounds(poly, points, bounds));
|
|
706
|
+
|
|
707
|
+
if (tol === 0) {
|
|
708
|
+
// eslint-disable-next-line no-continue
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
tryPoints.initialize();
|
|
713
|
+
locator.setTolerance(tol);
|
|
714
|
+
locator.initPointInsertion(tryPoints, bounds);
|
|
715
|
+
var foundMatch = false;
|
|
716
|
+
var idx1 = 0;
|
|
717
|
+
var idx2 = 0;
|
|
718
|
+
var unique = 0;
|
|
719
|
+
var point = [];
|
|
720
|
+
var p1 = [];
|
|
721
|
+
var p2 = [];
|
|
722
|
+
var p3 = [];
|
|
723
|
+
|
|
724
|
+
for (idx2 = 0; idx2 < n; idx2++) {
|
|
725
|
+
points.getPoint(poly[idx2], point);
|
|
726
|
+
|
|
727
|
+
var _locator$insertUnique = locator.insertUniquePoint(point, 0),
|
|
728
|
+
success = _locator$insertUnique.success,
|
|
729
|
+
pointIdx = _locator$insertUnique.pointIdx;
|
|
730
|
+
|
|
731
|
+
if (!success) {
|
|
732
|
+
// Need vertIdx to match poly indices, so force point insertion
|
|
733
|
+
locator.insertNextPoint(point); // Do the points have different pointIds?
|
|
734
|
+
|
|
735
|
+
idx1 = pointIdx;
|
|
736
|
+
unique = poly[idx2] !== poly[idx1];
|
|
737
|
+
|
|
738
|
+
if (idx2 > idx1 + 2 - unique && n + idx1 > idx2 + 2 - unique) {
|
|
739
|
+
if (oriented) {
|
|
740
|
+
// Make sure that splitting this poly won't create a hole poly
|
|
741
|
+
var prevIdx = n + idx1 - 1;
|
|
742
|
+
var midIdx = idx1 + 1;
|
|
743
|
+
var nextIdx = idx2 + 1;
|
|
744
|
+
|
|
745
|
+
if (prevIdx >= n) {
|
|
746
|
+
prevIdx -= n;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (midIdx >= n) {
|
|
750
|
+
midIdx -= n;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (nextIdx >= n) {
|
|
754
|
+
nextIdx -= n;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
points.getPoint(poly[prevIdx], p1);
|
|
758
|
+
points.getPoint(poly[midIdx], p2);
|
|
759
|
+
points.getPoint(poly[nextIdx], p3);
|
|
760
|
+
|
|
761
|
+
if (vtkCCSVectorProgression(point, p1, p2, p3, normal) > 0) {
|
|
762
|
+
foundMatch = true;
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
foundMatch = true;
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (foundMatch) {
|
|
774
|
+
splitCount++; // Split off a new poly
|
|
775
|
+
|
|
776
|
+
var m = idx2 - idx1;
|
|
777
|
+
var oldPoly = polys[i];
|
|
778
|
+
var oldEdges = polyEdges[i];
|
|
779
|
+
var newPoly1 = oldPoly.slice(idx1, idx1 + m + unique);
|
|
780
|
+
var newEdges1 = oldEdges.slice(idx1, idx1 + m + unique);
|
|
781
|
+
var newPoly2 = new Array(n - m + unique);
|
|
782
|
+
var newEdges2 = new Array(n - m + unique);
|
|
783
|
+
|
|
784
|
+
if (unique) {
|
|
785
|
+
newEdges1[m] = -1;
|
|
786
|
+
} // The poly that is split off, which might have more intersections
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
for (var j = 0; j < idx1 + unique; j++) {
|
|
790
|
+
newPoly2[j] = oldPoly[j];
|
|
791
|
+
newEdges2[j] = oldEdges[j];
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (unique) {
|
|
795
|
+
newEdges2[idx1] = -1;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
for (var k = idx2; k < n; k++) {
|
|
799
|
+
newPoly2[k - m + unique] = oldPoly[k];
|
|
800
|
+
newEdges2[k - m + unique] = oldEdges[k];
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
polys[i] = newPoly1;
|
|
804
|
+
polyEdges[i] = newEdges1;
|
|
805
|
+
polys.push(newPoly2);
|
|
806
|
+
polyEdges.push(newEdges2); // Unless polygroup was clear (because poly was reversed),
|
|
807
|
+
// make a group with one entry for the new poly
|
|
808
|
+
|
|
809
|
+
polyGroups.length = polys.length;
|
|
810
|
+
|
|
811
|
+
if (polyGroups[i].length > 0) {
|
|
812
|
+
polyGroups[polys.length - 1].push(polys.length - 1);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return splitCount;
|
|
818
|
+
} // ---------------------------------------------------
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* The polygons might have a lot of extra points, i.e. points
|
|
822
|
+
* in the middle of the edges. Remove those points, but keep
|
|
823
|
+
* the original edges as polylines in the originalEdges array.
|
|
824
|
+
* Only original edges with more than two points will be kept.
|
|
825
|
+
*
|
|
826
|
+
* @param {Array[]} polys
|
|
827
|
+
* @param {vtkPoints} points
|
|
828
|
+
* @param {Array} polyEdges
|
|
829
|
+
* @param {Array} originalEdges
|
|
830
|
+
*/
|
|
831
|
+
|
|
832
|
+
function vtkCCSFindTrueEdges(polys, points, polyEdges, originalEdges) {
|
|
833
|
+
// Tolerance^2 for angle to see if line segments are parallel
|
|
834
|
+
var atol2 = CCS_POLYGON_TOLERANCE * CCS_POLYGON_TOLERANCE;
|
|
835
|
+
var p0 = [];
|
|
836
|
+
var p1 = [];
|
|
837
|
+
var p2 = [];
|
|
838
|
+
var v1 = [];
|
|
839
|
+
var v2 = [];
|
|
840
|
+
var l1;
|
|
841
|
+
var l2;
|
|
842
|
+
|
|
843
|
+
for (var polyId = 0; polyId < polys.length; polyId++) {
|
|
844
|
+
var oldPoly = polys[polyId];
|
|
845
|
+
var n = oldPoly.length;
|
|
846
|
+
var newEdges = [];
|
|
847
|
+
polyEdges.push(newEdges); // Only useful if poly has more than three sides
|
|
848
|
+
|
|
849
|
+
if (n < 4) {
|
|
850
|
+
newEdges[0] = -1;
|
|
851
|
+
newEdges[1] = -1;
|
|
852
|
+
newEdges[2] = -1; // eslint-disable-next-line no-continue
|
|
853
|
+
|
|
854
|
+
continue;
|
|
855
|
+
} // While we remove points, m keeps track of how many points are left
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
var m = n; // Compute bounds for tolerance
|
|
859
|
+
|
|
860
|
+
var bounds = [];
|
|
861
|
+
var tol2 = vtkPolygon.getBounds(oldPoly, points, bounds) * atol2; // The new poly
|
|
862
|
+
|
|
863
|
+
var newPoly = [];
|
|
864
|
+
var cornerPointId = 0;
|
|
865
|
+
var oldOriginalId = -1; // Keep the partial edge from before the first corner is found
|
|
866
|
+
|
|
867
|
+
var partialEdge = [];
|
|
868
|
+
var cellCount = 0;
|
|
869
|
+
points.getPoint(oldPoly[n - 1], p0);
|
|
870
|
+
points.getPoint(oldPoly[0], p1);
|
|
871
|
+
subtract(p1, p0, v1);
|
|
872
|
+
l1 = dot(v1, v1);
|
|
873
|
+
|
|
874
|
+
for (var j = 0; j < n; j++) {
|
|
875
|
+
var k = j + 1;
|
|
876
|
+
|
|
877
|
+
if (k >= n) {
|
|
878
|
+
k -= n;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
points.getPoint(oldPoly[k], p2);
|
|
882
|
+
subtract(p2, p1, v2);
|
|
883
|
+
l2 = dot(v2, v2); // Dot product is |v1||v2|cos(theta)
|
|
884
|
+
|
|
885
|
+
var c = dot(v1, v2); // sin^2(theta) = (1 - cos^2(theta))
|
|
886
|
+
// and c*c = l1*l2*cos^2(theta)
|
|
887
|
+
|
|
888
|
+
var s2 = l1 * l2 - c * c; // In the small angle approximation, sin(theta) == theta, so
|
|
889
|
+
// s2/(l1*l2) is the angle that we want to check, but it's not
|
|
890
|
+
// a valid check if l1 or l2 is very close to zero.
|
|
891
|
+
|
|
892
|
+
var pointId = oldPoly[j]; // Keep the point if:
|
|
893
|
+
// 1) removing it would create a 2-point poly OR
|
|
894
|
+
// 2) it's more than "tol" distance from the prev point AND
|
|
895
|
+
// 3) the angle is greater than atol:
|
|
896
|
+
|
|
897
|
+
if (m <= 3 || l1 > tol2 && (c < 0 || l1 < tol2 || l2 < tol2 || s2 > l1 * l2 * atol2)) {
|
|
898
|
+
// Complete the previous edge only if the final point count
|
|
899
|
+
// will be greater than two
|
|
900
|
+
if (cellCount > 1) {
|
|
901
|
+
if (pointId !== oldOriginalId) {
|
|
902
|
+
originalEdges.push(pointId);
|
|
903
|
+
cellCount++;
|
|
904
|
+
} // Update the number of segments in the edge
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
var countLocation = originalEdges.length - cellCount - 1;
|
|
908
|
+
originalEdges[countLocation] = cellCount;
|
|
909
|
+
newEdges.push(countLocation);
|
|
910
|
+
} else if (cellCount === 0) {
|
|
911
|
+
partialEdge.push(pointId);
|
|
912
|
+
} else {
|
|
913
|
+
newEdges.push(-1);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
newPoly.push(pointId); // Start a new edge with cornerPointId as a "virtual" point
|
|
917
|
+
|
|
918
|
+
cornerPointId = pointId;
|
|
919
|
+
oldOriginalId = pointId;
|
|
920
|
+
cellCount = 1; // Rotate to the next point
|
|
921
|
+
|
|
922
|
+
p0[0] = p2[0];
|
|
923
|
+
p0[1] = p2[1];
|
|
924
|
+
p0[2] = p2[2];
|
|
925
|
+
p1[0] = p2[0];
|
|
926
|
+
p1[1] = p2[1];
|
|
927
|
+
p1[2] = p2[2];
|
|
928
|
+
v1[0] = v2[0];
|
|
929
|
+
v1[1] = v2[1];
|
|
930
|
+
v1[2] = v2[2];
|
|
931
|
+
l1 = l2;
|
|
932
|
+
} else {
|
|
933
|
+
if (cellCount > 0 && pointId !== oldOriginalId) {
|
|
934
|
+
// First check to see if we have to add cornerPointId
|
|
935
|
+
if (cellCount === 1) {
|
|
936
|
+
originalEdges.push(1); // new edge
|
|
937
|
+
|
|
938
|
+
originalEdges.push(cornerPointId);
|
|
939
|
+
} // Then add the new point
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
originalEdges.push(pointId);
|
|
943
|
+
oldOriginalId = pointId;
|
|
944
|
+
cellCount++;
|
|
945
|
+
} else {
|
|
946
|
+
// No corner yet, so save the point
|
|
947
|
+
partialEdge.push(pointId);
|
|
948
|
+
} // Reduce the count
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
m--; // Join the previous two segments, since the point was removed
|
|
952
|
+
|
|
953
|
+
p1[0] = p2[0];
|
|
954
|
+
p1[1] = p2[1];
|
|
955
|
+
p1[2] = p2[2];
|
|
956
|
+
subtract(p2, p0, v1);
|
|
957
|
+
l1 = dot(v1, v1);
|
|
958
|
+
}
|
|
959
|
+
} // Add the partial edge to the end
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
for (var ii = 0; ii < partialEdge.length; ii++) {
|
|
963
|
+
var _pointId = partialEdge[ii];
|
|
964
|
+
|
|
965
|
+
if (_pointId !== oldOriginalId) {
|
|
966
|
+
if (cellCount === 1) {
|
|
967
|
+
originalEdges.push(1); // new edge
|
|
968
|
+
|
|
969
|
+
originalEdges.push(cornerPointId);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
originalEdges.push(_pointId);
|
|
973
|
+
oldOriginalId = _pointId;
|
|
974
|
+
cellCount++;
|
|
975
|
+
}
|
|
976
|
+
} // Finalize
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
if (cellCount > 1) {
|
|
980
|
+
// Update the number of segments in the edge
|
|
981
|
+
var _countLocation = originalEdges.length - cellCount - 1;
|
|
982
|
+
|
|
983
|
+
originalEdges[_countLocation] = cellCount;
|
|
984
|
+
newEdges.push(_countLocation);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
polys[polyId] = newPoly;
|
|
988
|
+
}
|
|
989
|
+
} // ---------------------------------------------------
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Reverse a cleaned-up polygon along with the info about
|
|
993
|
+
* all of its original vertices.
|
|
994
|
+
*
|
|
995
|
+
* @param {Array} poly
|
|
996
|
+
* @param {Array} edges
|
|
997
|
+
* @param {Array} originalEdges
|
|
998
|
+
*/
|
|
999
|
+
|
|
1000
|
+
function vtkCCSReversePoly(poly, edges, originalEdges) {
|
|
1001
|
+
reverseElements(poly, 1, poly.length - 1);
|
|
1002
|
+
edges.reverse();
|
|
1003
|
+
|
|
1004
|
+
for (var i = 0; i < edges.length; i++) {
|
|
1005
|
+
if (edges[i] >= 0) {
|
|
1006
|
+
var firstPtsIdx = edges[i] + 1;
|
|
1007
|
+
var npts = originalEdges[edges[i]];
|
|
1008
|
+
reverseElements(originalEdges, firstPtsIdx, firstPtsIdx + npts - 1);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
} // ---------------------------------------------------
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Check the sense of the polygon against the given normal. Returns
|
|
1015
|
+
* zero if the normal is zero.
|
|
1016
|
+
*
|
|
1017
|
+
* @param {Array} poly
|
|
1018
|
+
* @param {vtkPoints} points
|
|
1019
|
+
* @param {Vector3} normal
|
|
1020
|
+
*/
|
|
1021
|
+
|
|
1022
|
+
function vtkCCSCheckPolygonSense(poly, points, normal) {
|
|
1023
|
+
// Compute the normal
|
|
1024
|
+
var pnormal = [0.0, 0.0, 0.0];
|
|
1025
|
+
var p0 = [];
|
|
1026
|
+
var p1 = [];
|
|
1027
|
+
var p2 = [];
|
|
1028
|
+
var v1 = [];
|
|
1029
|
+
var v2 = [];
|
|
1030
|
+
var v = [];
|
|
1031
|
+
points.getPoint(poly[0], p0);
|
|
1032
|
+
points.getPoint(poly[1], p1);
|
|
1033
|
+
subtract(p1, p0, v1);
|
|
1034
|
+
|
|
1035
|
+
for (var jj = 2; jj < poly.length; jj++) {
|
|
1036
|
+
points.getPoint(poly[jj], p2);
|
|
1037
|
+
subtract(p2, p0, v2);
|
|
1038
|
+
cross(v1, v2, v);
|
|
1039
|
+
add(pnormal, v, pnormal);
|
|
1040
|
+
p1[0] = p2[0];
|
|
1041
|
+
p1[1] = p2[1];
|
|
1042
|
+
p1[2] = p2[2];
|
|
1043
|
+
v1[0] = v2[0];
|
|
1044
|
+
v1[1] = v2[1];
|
|
1045
|
+
v1[2] = v2[2];
|
|
1046
|
+
} // Check the normal
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
var d = dot(pnormal, normal);
|
|
1050
|
+
return {
|
|
1051
|
+
isNormalNotZero: d !== 0,
|
|
1052
|
+
sense: d > 0
|
|
1053
|
+
};
|
|
1054
|
+
} // ---------------------------------------------------
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Check whether innerPoly is inside outerPoly.
|
|
1058
|
+
* The normal is needed to verify the polygon orientation.
|
|
1059
|
+
* The values of pp, bounds, and tol2 must be precomputed
|
|
1060
|
+
* by calling vtkCCSPrepareForPolyInPoly() on outerPoly.
|
|
1061
|
+
*
|
|
1062
|
+
* @param {Array} outerPoly
|
|
1063
|
+
* @param {Array} innerPoly
|
|
1064
|
+
* @param {vtkPoints} points
|
|
1065
|
+
* @param {Vector3} normal
|
|
1066
|
+
* @param {Float64Array} pp
|
|
1067
|
+
* @param {Bounds} bounds
|
|
1068
|
+
* @param {Number} tol2
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
function vtkCCSPolyInPoly(outerPoly, innerPoly, points, normal, pp, bounds, tol2) {
|
|
1072
|
+
// Find a vertex of poly "j" that isn't on the edge of poly "i".
|
|
1073
|
+
// This is necessary or the PointInPolygon might return "true"
|
|
1074
|
+
// based only on roundoff error.
|
|
1075
|
+
var n = outerPoly.length;
|
|
1076
|
+
var m = innerPoly.length;
|
|
1077
|
+
var p = [];
|
|
1078
|
+
var q1 = [];
|
|
1079
|
+
var q2 = [];
|
|
1080
|
+
|
|
1081
|
+
for (var jj = 0; jj < m; jj++) {
|
|
1082
|
+
// Semi-randomize the point order
|
|
1083
|
+
// eslint-disable-next-line no-bitwise
|
|
1084
|
+
var kk = (jj >> 1) + (jj & 1) * (m + 1 >> 1);
|
|
1085
|
+
points.getPoint(innerPoly[kk], p);
|
|
1086
|
+
var intersectionState = vtkPolygon.pointInPolygon(p, pp, bounds, normal);
|
|
1087
|
+
|
|
1088
|
+
if (intersectionState === PolygonWithPointIntersectionState.FAILURE) {
|
|
1089
|
+
vtkErrorMacro('Error finding point in polygon in vtkCCSPolyInPoly');
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
if (intersectionState !== PolygonWithPointIntersectionState.OUTSIDE) {
|
|
1093
|
+
var pointOnEdge = 0;
|
|
1094
|
+
points.getPoint(outerPoly[n - 1], q1);
|
|
1095
|
+
|
|
1096
|
+
for (var ii = 0; ii < n; ii++) {
|
|
1097
|
+
points.getPoint(outerPoly[ii], q2); // This method returns distance squared
|
|
1098
|
+
|
|
1099
|
+
var _vtkLine$distanceToLi = vtkLine.distanceToLine(p, q1, q2),
|
|
1100
|
+
distance = _vtkLine$distanceToLi.distance;
|
|
1101
|
+
|
|
1102
|
+
if (distance < tol2) {
|
|
1103
|
+
pointOnEdge = 1;
|
|
1104
|
+
break;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
q1[0] = q2[0];
|
|
1108
|
+
q1[1] = q2[1];
|
|
1109
|
+
q1[2] = q2[2];
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (!pointOnEdge) {
|
|
1113
|
+
// Good result, point is in polygon
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
} // No matches found
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
return false;
|
|
1121
|
+
} // ---------------------------------------------------
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Precompute values needed for the PolyInPoly check.
|
|
1125
|
+
* The values that are returned are as follows:
|
|
1126
|
+
* pp: an array of the polygon vertices
|
|
1127
|
+
* bounds: the polygon bounds
|
|
1128
|
+
* tol2: a tolerance value based on the size of the polygon
|
|
1129
|
+
* (note: pp must be pre-allocated to the 3*outerPoly.length)
|
|
1130
|
+
*
|
|
1131
|
+
* @param {Array} outerPoly
|
|
1132
|
+
* @param {vtkPoints} points
|
|
1133
|
+
* @param {Float64Array} pp
|
|
1134
|
+
* @param {Bounds} bounds
|
|
1135
|
+
*/
|
|
1136
|
+
|
|
1137
|
+
function vtkCCSPrepareForPolyInPoly(outerPoly, points, pp, bounds) {
|
|
1138
|
+
var n = outerPoly.length;
|
|
1139
|
+
|
|
1140
|
+
if (n === 0) {
|
|
1141
|
+
return 0.0; // to avoid false positive warning about uninitialized value
|
|
1142
|
+
} // Pull out the points
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
var point = [];
|
|
1146
|
+
var j = 0;
|
|
1147
|
+
|
|
1148
|
+
for (var i = 0; i < n; i++) {
|
|
1149
|
+
points.getPoint(outerPoly[i], point);
|
|
1150
|
+
pp[j++] = point[0];
|
|
1151
|
+
pp[j++] = point[1];
|
|
1152
|
+
pp[j++] = point[2];
|
|
1153
|
+
} // Find the bounding box and tolerance for the polygon
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
return vtkPolygon.getBounds(outerPoly, points, bounds) * (CCS_POLYGON_TOLERANCE * CCS_POLYGON_TOLERANCE);
|
|
1157
|
+
} // ---------------------------------------------------
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Check for polygons within polygons. Group the polygons
|
|
1161
|
+
* if they are within each other. Reverse the sense of
|
|
1162
|
+
* the interior "hole" polygons. A hole within a hole
|
|
1163
|
+
* will be reversed twice and will become its own group.
|
|
1164
|
+
*
|
|
1165
|
+
* @param {Array} newPolys
|
|
1166
|
+
* @param {vtkPoints} points
|
|
1167
|
+
* @param {Array} polyGroups
|
|
1168
|
+
* @param {Array} polyEdges
|
|
1169
|
+
* @param {Array} originalEdges
|
|
1170
|
+
* @param {Vector3} normal
|
|
1171
|
+
* @param {Boolean} oriented
|
|
1172
|
+
*/
|
|
1173
|
+
|
|
1174
|
+
function vtkCCSMakeHoleyPolys(newPolys, points, polyGroups, polyEdges, originalEdges, normal, oriented) {
|
|
1175
|
+
var numNewPolys = newPolys.length;
|
|
1176
|
+
|
|
1177
|
+
if (numNewPolys <= 1) {
|
|
1178
|
+
return;
|
|
1179
|
+
} // Use bit arrays to keep track of inner polys
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
var polyReversed = [];
|
|
1183
|
+
var innerPolys = []; // GroupCount is an array only needed for unoriented polys
|
|
1184
|
+
|
|
1185
|
+
var groupCount;
|
|
1186
|
+
|
|
1187
|
+
if (!oriented) {
|
|
1188
|
+
groupCount = new Int32Array(numNewPolys);
|
|
1189
|
+
} // Find the maximum poly size
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
var nmax = 1;
|
|
1193
|
+
|
|
1194
|
+
for (var kk = 0; kk < numNewPolys; kk++) {
|
|
1195
|
+
nmax = Math.max(nmax, newPolys[kk].length);
|
|
1196
|
+
} // These are some values needed for poly-in-poly checks
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
var pp = new Float64Array(3 * nmax);
|
|
1200
|
+
var bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
|
1201
|
+
var tol2; // Go through all polys
|
|
1202
|
+
|
|
1203
|
+
for (var i = 0; i < numNewPolys; i++) {
|
|
1204
|
+
var n = newPolys[i].length;
|
|
1205
|
+
|
|
1206
|
+
if (n < 3) {
|
|
1207
|
+
// eslint-disable-next-line no-continue
|
|
1208
|
+
continue;
|
|
1209
|
+
} // Check if poly is reversed
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
var _vtkCCSCheckPolygonSe = vtkCCSCheckPolygonSense(newPolys[i], points, normal),
|
|
1213
|
+
isNormalNotZero = _vtkCCSCheckPolygonSe.isNormalNotZero,
|
|
1214
|
+
sense = _vtkCCSCheckPolygonSe.sense;
|
|
1215
|
+
|
|
1216
|
+
if (isNormalNotZero) {
|
|
1217
|
+
polyReversed[i] = !sense;
|
|
1218
|
+
} // Precompute some values needed for poly-in-poly checks
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
tol2 = vtkCCSPrepareForPolyInPoly(newPolys[i], points, pp, bounds); // Look for polygons inside of this one
|
|
1222
|
+
|
|
1223
|
+
for (var j = 0; j < numNewPolys; j++) {
|
|
1224
|
+
if (j !== i && newPolys[j].length >= 3) {
|
|
1225
|
+
// Make sure polygon i is not in polygon j
|
|
1226
|
+
var pg = polyGroups[j];
|
|
1227
|
+
|
|
1228
|
+
if (!pg.includes(i)) {
|
|
1229
|
+
if (vtkCCSPolyInPoly(newPolys[i], newPolys[j], points, normal, pp.subarray(3 * n), bounds, tol2)) {
|
|
1230
|
+
// Add to group
|
|
1231
|
+
polyGroups[i].push(j);
|
|
1232
|
+
|
|
1233
|
+
if (groupCount) {
|
|
1234
|
+
groupCount[j] += 1;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (!oriented) {
|
|
1243
|
+
// build a stack of polys that aren't inside other polys=
|
|
1244
|
+
var outerPolyStack = [];
|
|
1245
|
+
|
|
1246
|
+
for (var ll = 0; ll < numNewPolys; ll++) {
|
|
1247
|
+
if (groupCount[ll] === 0) {
|
|
1248
|
+
outerPolyStack.push(ll);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
var _j;
|
|
1253
|
+
|
|
1254
|
+
while (outerPolyStack.length > 0) {
|
|
1255
|
+
_j = outerPolyStack.length - 1;
|
|
1256
|
+
outerPolyStack.pop();
|
|
1257
|
+
|
|
1258
|
+
if (polyReversed[_j]) {
|
|
1259
|
+
vtkCCSReversePoly(newPolys[_j], polyEdges[_j], originalEdges);
|
|
1260
|
+
polyReversed[_j] = false;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (polyGroups[_j].length > 1) {
|
|
1264
|
+
// Convert the group into a bit array, to make manipulation easier
|
|
1265
|
+
innerPolys.length = 0;
|
|
1266
|
+
|
|
1267
|
+
for (var k = 1; k < polyGroups[_j].length; k++) {
|
|
1268
|
+
var jj = polyGroups[_j][k];
|
|
1269
|
+
|
|
1270
|
+
if (groupCount[jj] > 1) {
|
|
1271
|
+
groupCount[jj] -= 2;
|
|
1272
|
+
|
|
1273
|
+
if (groupCount[jj] === 0) {
|
|
1274
|
+
outerPolyStack.push(jj);
|
|
1275
|
+
}
|
|
1276
|
+
} else {
|
|
1277
|
+
innerPolys[jj] = 1;
|
|
1278
|
+
polyGroups[jj].length = 0;
|
|
1279
|
+
|
|
1280
|
+
if (!polyReversed[jj]) {
|
|
1281
|
+
vtkCCSReversePoly(newPolys[jj], polyEdges[jj], originalEdges);
|
|
1282
|
+
polyReversed[jj] = false;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
} // Use the bit array to recreate the polyGroup
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
polyGroups[_j].length = 0;
|
|
1289
|
+
|
|
1290
|
+
polyGroups[_j].push(_j);
|
|
1291
|
+
|
|
1292
|
+
for (var _jj2 = 0; _jj2 < numNewPolys; _jj2++) {
|
|
1293
|
+
if (innerPolys[_jj2]) {
|
|
1294
|
+
polyGroups[_j].push(_jj2);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
// oriented
|
|
1301
|
+
for (var _j2 = 0; _j2 < numNewPolys; _j2++) {
|
|
1302
|
+
// Remove the groups for reversed polys
|
|
1303
|
+
if (polyReversed[_j2]) {
|
|
1304
|
+
polyGroups[_j2].length = 0;
|
|
1305
|
+
} // Polys inside the interior polys have their own groups, so remove
|
|
1306
|
+
// them from this group
|
|
1307
|
+
else if (polyGroups[_j2].length > 1) {
|
|
1308
|
+
// Convert the group into a bit array, to make manipulation easier
|
|
1309
|
+
innerPolys.length = 0;
|
|
1310
|
+
|
|
1311
|
+
for (var _k = 1; _k < polyGroups[_j2].length; _k++) {
|
|
1312
|
+
innerPolys[polyGroups[_j2][_k]] = true;
|
|
1313
|
+
} // Look for non-reversed polys inside this one
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
for (var _kk = 1; _kk < polyGroups[_j2].length; _kk++) {
|
|
1317
|
+
// jj is the index of the inner poly
|
|
1318
|
+
var _jj3 = polyGroups[_j2][_kk]; // If inner poly is not reversed then
|
|
1319
|
+
|
|
1320
|
+
if (!polyReversed[_jj3]) {
|
|
1321
|
+
// Remove that poly and all polys inside of it from the group
|
|
1322
|
+
for (var ii = 0; ii < polyGroups[_jj3].length; ii++) {
|
|
1323
|
+
innerPolys[polyGroups[_jj3][ii]] = false;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
} // Use the bit array to recreate the polyGroup
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
polyGroups[_j2].length = 0;
|
|
1330
|
+
|
|
1331
|
+
polyGroups[_j2].push(_j2);
|
|
1332
|
+
|
|
1333
|
+
for (var _jj4 = 0; _jj4 < numNewPolys; _jj4++) {
|
|
1334
|
+
if (innerPolys[_jj4]) {
|
|
1335
|
+
polyGroups[_j2].push(_jj4);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
} // delete[] groupCount;
|
|
1341
|
+
|
|
1342
|
+
} // ---------------------------------------------------
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Check line segment with point Ids (i, j) to make sure that it
|
|
1346
|
+
* doesn't cut through the edges of any polys in the group.
|
|
1347
|
+
* Return value of zero means check failed and the cut is not
|
|
1348
|
+
* usable.
|
|
1349
|
+
*
|
|
1350
|
+
* @param {Array[]} polys
|
|
1351
|
+
* @param {vtkPoints} points
|
|
1352
|
+
* @param {Vector3} normal
|
|
1353
|
+
* @param {Array} polyGroup
|
|
1354
|
+
* @param {Number} outerPolyId
|
|
1355
|
+
* @param {Number} innerPolyId
|
|
1356
|
+
* @param {Number} outerIdx
|
|
1357
|
+
* @param {Number} innerIdx
|
|
1358
|
+
*/
|
|
1359
|
+
|
|
1360
|
+
function vtkCCSCheckCut(polys, points, normal, polyGroup, outerPolyId, innerPolyId, outerIdx, innerIdx) {
|
|
1361
|
+
var ptId1 = polys[outerPolyId][outerIdx];
|
|
1362
|
+
var ptId2 = polys[innerPolyId][innerIdx];
|
|
1363
|
+
var tol = CCS_POLYGON_TOLERANCE;
|
|
1364
|
+
var p1 = [];
|
|
1365
|
+
var p2 = [];
|
|
1366
|
+
points.getPoint(ptId1, p1);
|
|
1367
|
+
points.getPoint(ptId2, p2);
|
|
1368
|
+
var w = [];
|
|
1369
|
+
subtract(p2, p1, w);
|
|
1370
|
+
var l = normalize(w); // Cuts between coincident points are good
|
|
1371
|
+
|
|
1372
|
+
if (l === 0) {
|
|
1373
|
+
return true;
|
|
1374
|
+
} // Define a tolerance with units of distance squared
|
|
1375
|
+
|
|
1376
|
+
|
|
1377
|
+
var tol2 = l * l * tol * tol; // Check the sense of the cut: it must be pointing "in" for both polys.
|
|
1378
|
+
|
|
1379
|
+
var polyId = outerPolyId;
|
|
1380
|
+
var polyIdx = outerIdx;
|
|
1381
|
+
var r = p1;
|
|
1382
|
+
var r1 = [];
|
|
1383
|
+
var r2 = p2;
|
|
1384
|
+
var r3 = [];
|
|
1385
|
+
|
|
1386
|
+
for (var ii = 0; ii < 2; ii++) {
|
|
1387
|
+
var poly = polys[polyId];
|
|
1388
|
+
var n = poly.length;
|
|
1389
|
+
var prevIdx = n - polyIdx - 1;
|
|
1390
|
+
var nextIdx = polyIdx + 1;
|
|
1391
|
+
|
|
1392
|
+
if (prevIdx >= n) {
|
|
1393
|
+
prevIdx -= n;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (nextIdx >= n) {
|
|
1397
|
+
nextIdx -= n;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
points.getPoint(poly[prevIdx], r1);
|
|
1401
|
+
points.getPoint(poly[nextIdx], r3);
|
|
1402
|
+
|
|
1403
|
+
if (vtkCCSVectorProgression(r, r1, r2, r3, normal) > 0) {
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
polyId = innerPolyId;
|
|
1408
|
+
polyIdx = innerIdx;
|
|
1409
|
+
r = p2;
|
|
1410
|
+
r2 = p1;
|
|
1411
|
+
} // Check for intersections of the cut with polygon edges.
|
|
1412
|
+
// First, create a cut plane that divides space at the cut line.
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
var pc = [];
|
|
1416
|
+
cross(normal, w, pc);
|
|
1417
|
+
pc[3] = -dot(pc, p1);
|
|
1418
|
+
|
|
1419
|
+
for (var i = 0; i < polyGroup.length; i++) {
|
|
1420
|
+
var _poly2 = polys[polyGroup[i]];
|
|
1421
|
+
var _n = _poly2.length;
|
|
1422
|
+
var q1 = [];
|
|
1423
|
+
var q2 = [];
|
|
1424
|
+
var qtId1 = _poly2[_n - 1];
|
|
1425
|
+
points.getPoint(qtId1, q1);
|
|
1426
|
+
var v1 = pc[0] * q1[0] + pc[1] * q1[1] + pc[2] * q1[2] + pc[3];
|
|
1427
|
+
var c1 = v1 > 0;
|
|
1428
|
+
|
|
1429
|
+
for (var j = 0; j < _n; j++) {
|
|
1430
|
+
var qtId2 = _poly2[j];
|
|
1431
|
+
points.getPoint(qtId2, q2);
|
|
1432
|
+
var v2 = pc[0] * q2[0] + pc[1] * q2[1] + pc[2] * q2[2] + pc[3];
|
|
1433
|
+
var c2 = v2 > 0; // If lines share an endpoint, they can't intersect,
|
|
1434
|
+
// so don't bother with the check.
|
|
1435
|
+
|
|
1436
|
+
if (ptId1 !== qtId1 && ptId1 !== qtId2 && ptId2 !== qtId1 && ptId2 !== qtId2) {
|
|
1437
|
+
// Check for intersection
|
|
1438
|
+
if ((c1 ? !c2 : c2) || v1 * v1 < tol2 || v2 * v2 < tol2) {
|
|
1439
|
+
subtract(q2, q1, w);
|
|
1440
|
+
|
|
1441
|
+
if (dot(w, w) > 0) {
|
|
1442
|
+
var qc = [];
|
|
1443
|
+
cross(w, normal, qc);
|
|
1444
|
+
qc[3] = -dot(qc, q1);
|
|
1445
|
+
var u1 = qc[0] * p1[0] + qc[1] * p1[1] + qc[2] * p1[2] + qc[3];
|
|
1446
|
+
var u2 = qc[0] * p2[0] + qc[1] * p2[1] + qc[2] * p2[2] + qc[3];
|
|
1447
|
+
var d1 = u1 > 0;
|
|
1448
|
+
var d2 = u2 > 0;
|
|
1449
|
+
|
|
1450
|
+
if (d1 ? !d2 : d2) {
|
|
1451
|
+
// One final check to make sure endpoints aren't coincident
|
|
1452
|
+
var p = p1;
|
|
1453
|
+
var q = q1;
|
|
1454
|
+
|
|
1455
|
+
if (v2 * v2 < v1 * v1) {
|
|
1456
|
+
p = p2;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (u2 * u2 < u1 * u1) {
|
|
1460
|
+
q = q2;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (distance2BetweenPoints(p, q) > tol2) {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
qtId1 = qtId2;
|
|
1472
|
+
q1[0] = q2[0];
|
|
1473
|
+
q1[1] = q2[1];
|
|
1474
|
+
q1[2] = q2[2];
|
|
1475
|
+
v1 = v2;
|
|
1476
|
+
c1 = c2;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
return true;
|
|
1481
|
+
} // ---------------------------------------------------
|
|
1482
|
+
|
|
1483
|
+
/**
|
|
1484
|
+
* Check the quality of a cut between an outer and inner polygon.
|
|
1485
|
+
* An ideal cut is one that forms a 90 degree angle with each
|
|
1486
|
+
* line segment that it joins to. Smaller values indicate a
|
|
1487
|
+
* higher quality cut.
|
|
1488
|
+
*
|
|
1489
|
+
* @param {Array} outerPoly
|
|
1490
|
+
* @param {Array} innerPoly
|
|
1491
|
+
* @param {Number} i
|
|
1492
|
+
* @param {Number} j
|
|
1493
|
+
* @param {vtkPoints} points
|
|
1494
|
+
*/
|
|
1495
|
+
|
|
1496
|
+
function vtkCCSCutQuality(outerPoly, innerPoly, i, j, points) {
|
|
1497
|
+
var n = outerPoly.length;
|
|
1498
|
+
var m = innerPoly.length;
|
|
1499
|
+
var a = i > 0 ? i - 1 : n - 1;
|
|
1500
|
+
var b = i < n - 1 ? i + 1 : 0;
|
|
1501
|
+
var c = j > 0 ? j - 1 : m - 1;
|
|
1502
|
+
var d = j < m - 1 ? j + 1 : 0;
|
|
1503
|
+
var p0 = [];
|
|
1504
|
+
var p1 = [];
|
|
1505
|
+
var p2 = [];
|
|
1506
|
+
points.getPoint(outerPoly[i], p1);
|
|
1507
|
+
points.getPoint(innerPoly[j], p2);
|
|
1508
|
+
var v1 = [];
|
|
1509
|
+
var v2 = [];
|
|
1510
|
+
subtract(p2, p1, v1);
|
|
1511
|
+
var l1 = dot(v1, v1);
|
|
1512
|
+
var l2;
|
|
1513
|
+
var qmax = 0;
|
|
1514
|
+
var q;
|
|
1515
|
+
points.getPoint(outerPoly[a], p0);
|
|
1516
|
+
subtract(p0, p1, v2);
|
|
1517
|
+
l2 = dot(v2, v2);
|
|
1518
|
+
|
|
1519
|
+
if (l2 > 0) {
|
|
1520
|
+
q = dot(v1, v2);
|
|
1521
|
+
q *= q / l2;
|
|
1522
|
+
|
|
1523
|
+
if (q > qmax) {
|
|
1524
|
+
qmax = q;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
points.getPoint(outerPoly[b], p0);
|
|
1529
|
+
subtract(p0, p1, v2);
|
|
1530
|
+
l2 = dot(v2, v2);
|
|
1531
|
+
|
|
1532
|
+
if (l2 > 0) {
|
|
1533
|
+
q = dot(v1, v2);
|
|
1534
|
+
q *= q / l2;
|
|
1535
|
+
|
|
1536
|
+
if (q > qmax) {
|
|
1537
|
+
qmax = q;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
points.getPoint(innerPoly[c], p0);
|
|
1542
|
+
subtract(p2, p0, v2);
|
|
1543
|
+
l2 = dot(v2, v2);
|
|
1544
|
+
|
|
1545
|
+
if (l2 > 0) {
|
|
1546
|
+
q = dot(v1, v2);
|
|
1547
|
+
q *= q / l2;
|
|
1548
|
+
|
|
1549
|
+
if (q > qmax) {
|
|
1550
|
+
qmax = q;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
points.getPoint(innerPoly[d], p0);
|
|
1555
|
+
subtract(p2, p0, v2);
|
|
1556
|
+
l2 = dot(v2, v2);
|
|
1557
|
+
|
|
1558
|
+
if (l2 > 0) {
|
|
1559
|
+
q = dot(v1, v2);
|
|
1560
|
+
q *= q / l2;
|
|
1561
|
+
|
|
1562
|
+
if (q > qmax) {
|
|
1563
|
+
qmax = q;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (l1 > 0) {
|
|
1568
|
+
return qmax / l1; // also l1 + qmax, incorporates distance;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
return Number.MAX_VALUE;
|
|
1572
|
+
} // ---------------------------------------------------
|
|
1573
|
+
|
|
1574
|
+
/**
|
|
1575
|
+
* Find the two sharpest verts on an inner (i.e. inside-out) poly.
|
|
1576
|
+
*
|
|
1577
|
+
* @param {Array} poly
|
|
1578
|
+
* @param {vtkPoints} points
|
|
1579
|
+
* @param {Vector3} normal
|
|
1580
|
+
* @param {[Number, Number]} verts
|
|
1581
|
+
*/
|
|
1582
|
+
|
|
1583
|
+
function vtkCCSFindSharpestVerts(poly, points, normal, verts) {
|
|
1584
|
+
var p1 = [];
|
|
1585
|
+
var p2 = [];
|
|
1586
|
+
var v1 = [];
|
|
1587
|
+
var v2 = [];
|
|
1588
|
+
var v = [];
|
|
1589
|
+
var l1;
|
|
1590
|
+
var l2;
|
|
1591
|
+
var minVal = [0, 0];
|
|
1592
|
+
verts[0] = 0;
|
|
1593
|
+
verts[1] = 0;
|
|
1594
|
+
var n = poly.length;
|
|
1595
|
+
points.getPoint(poly[n - 1], p2);
|
|
1596
|
+
points.getPoint(poly[0], p1);
|
|
1597
|
+
subtract(p1, p2, v1);
|
|
1598
|
+
l1 = Math.sqrt(dot(v1, v1));
|
|
1599
|
+
|
|
1600
|
+
for (var j = 0; j < n; j++) {
|
|
1601
|
+
var k = j + 1;
|
|
1602
|
+
|
|
1603
|
+
if (k === n) {
|
|
1604
|
+
k = 0;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
points.getPoint(poly[k], p2);
|
|
1608
|
+
subtract(p2, p1, v2);
|
|
1609
|
+
l2 = Math.sqrt(dot(v2, v2));
|
|
1610
|
+
cross(v1, v2, v);
|
|
1611
|
+
var b = dot(v, normal);
|
|
1612
|
+
|
|
1613
|
+
if (b < 0 && l1 * l2 > 0) {
|
|
1614
|
+
// Dot product is |v1||v2|cos(theta), range [-1, +1]
|
|
1615
|
+
var val = dot(v1, v2) / (l1 * l2);
|
|
1616
|
+
|
|
1617
|
+
if (val < minVal[0]) {
|
|
1618
|
+
minVal[1] = minVal[0];
|
|
1619
|
+
minVal[0] = val;
|
|
1620
|
+
verts[1] = verts[0];
|
|
1621
|
+
verts[0] = j;
|
|
1622
|
+
}
|
|
1623
|
+
} // Rotate to the next point
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
p1[0] = p2[0];
|
|
1627
|
+
p1[1] = p2[1];
|
|
1628
|
+
p1[2] = p2[2];
|
|
1629
|
+
v1[0] = v2[0];
|
|
1630
|
+
v1[1] = v2[1];
|
|
1631
|
+
v1[2] = v2[2];
|
|
1632
|
+
l1 = l2;
|
|
1633
|
+
}
|
|
1634
|
+
} // ---------------------------------------------------
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* Find two valid cuts between outerPoly and innerPoly.
|
|
1638
|
+
* Used by vtkCCSCutHoleyPolys.
|
|
1639
|
+
*
|
|
1640
|
+
* @param {Array} polys
|
|
1641
|
+
* @param {Array} polyGroup
|
|
1642
|
+
* @param {Number} outerPolyId
|
|
1643
|
+
* @param {Number} innerPolyId
|
|
1644
|
+
* @param {vtkPoints} points
|
|
1645
|
+
* @param {Vector3} normal
|
|
1646
|
+
* @param {Array[]} cuts
|
|
1647
|
+
* @param {Boolean} exhaustive
|
|
1648
|
+
*/
|
|
1649
|
+
|
|
1650
|
+
function vtkCCSFindCuts(polys, polyGroup, outerPolyId, innerPolyId, points, normal, cuts, exhaustive) {
|
|
1651
|
+
var outerPoly = polys[outerPolyId];
|
|
1652
|
+
var innerPoly = polys[innerPolyId];
|
|
1653
|
+
var innerSize = innerPoly.length; // Find the two sharpest points on the inner poly
|
|
1654
|
+
|
|
1655
|
+
var verts = [];
|
|
1656
|
+
vtkCCSFindSharpestVerts(innerPoly, points, normal, verts); // A list of cut locations according to quality
|
|
1657
|
+
|
|
1658
|
+
var cutlist = [];
|
|
1659
|
+
cutlist.length = outerPoly.length; // Search for potential cuts (need to find two cuts)
|
|
1660
|
+
|
|
1661
|
+
var cutId = 0;
|
|
1662
|
+
cuts[0][0] = 0;
|
|
1663
|
+
cuts[0][1] = 0;
|
|
1664
|
+
cuts[1][0] = 0;
|
|
1665
|
+
cuts[1][1] = 0;
|
|
1666
|
+
var foundCut = false;
|
|
1667
|
+
|
|
1668
|
+
for (cutId = 0; cutId < 2; cutId++) {
|
|
1669
|
+
var count = exhaustive ? innerSize : 3;
|
|
1670
|
+
|
|
1671
|
+
for (var i = 0; i < count && !foundCut; i++) {
|
|
1672
|
+
// Semi-randomize the search order
|
|
1673
|
+
// TODO: Does this do the same as in C++?
|
|
1674
|
+
// eslint-disable-next-line no-bitwise
|
|
1675
|
+
var j = (i >> 1) + (i & 1) * (innerSize + 1 >> 1); // Start at the best first point
|
|
1676
|
+
|
|
1677
|
+
j = (j + verts[cutId]) % innerSize;
|
|
1678
|
+
|
|
1679
|
+
for (var kk = 0; kk < outerPoly.length; kk++) {
|
|
1680
|
+
var q = vtkCCSCutQuality(outerPoly, innerPoly, kk, j, points);
|
|
1681
|
+
cutlist[kk] = [q, kk];
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
cutlist.sort(function (a, b) {
|
|
1685
|
+
return a[0] - b[0];
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
for (var lid = 0; lid < cutlist.length; lid++) {
|
|
1689
|
+
var k = cutlist[lid][1]; // If this is the second cut, do extra checks
|
|
1690
|
+
|
|
1691
|
+
if (cutId > 0) {
|
|
1692
|
+
// Make sure cuts don't share an endpoint
|
|
1693
|
+
if (j === cuts[0][1] || k === cuts[0][0]) {
|
|
1694
|
+
// eslint-disable-next-line no-continue
|
|
1695
|
+
continue;
|
|
1696
|
+
} // Make sure cuts don't intersect
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
var p1 = [];
|
|
1700
|
+
var p2 = [];
|
|
1701
|
+
points.getPoint(outerPoly[cuts[0][0]], p1);
|
|
1702
|
+
points.getPoint(innerPoly[cuts[0][1]], p2);
|
|
1703
|
+
var q1 = [];
|
|
1704
|
+
var q2 = [];
|
|
1705
|
+
points.getPoint(outerPoly[k], q1);
|
|
1706
|
+
points.getPoint(innerPoly[j], q2);
|
|
1707
|
+
var u = void 0;
|
|
1708
|
+
var v = void 0;
|
|
1709
|
+
|
|
1710
|
+
if (vtkLine.intersection(p1, p2, q1, q2, u, v) === vtkLine.IntersectionState.YES_INTERSECTION) {
|
|
1711
|
+
// eslint-disable-next-line no-continue
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
} // This check is done for both cuts
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
if (vtkCCSCheckCut(polys, points, normal, polyGroup, outerPolyId, innerPolyId, k, j)) {
|
|
1718
|
+
cuts[cutId][0] = k;
|
|
1719
|
+
cuts[cutId][1] = j;
|
|
1720
|
+
foundCut = true;
|
|
1721
|
+
break;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
if (!foundCut) {
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
return true;
|
|
1732
|
+
} // ---------------------------------------------------
|
|
1733
|
+
|
|
1734
|
+
/**
|
|
1735
|
+
* Helper for vtkCCSCutHoleyPolys. Change a polygon and a hole
|
|
1736
|
+
* into two separate polygons by making two cuts between them.
|
|
1737
|
+
*
|
|
1738
|
+
* @param {Array[]} polys
|
|
1739
|
+
* @param {Array} polyEdges
|
|
1740
|
+
* @param {Number} outerPolyId
|
|
1741
|
+
* @param {Number} innerPolyId
|
|
1742
|
+
* @param {vtkPoints} points
|
|
1743
|
+
* @param {Array[]} cuts
|
|
1744
|
+
*/
|
|
1745
|
+
|
|
1746
|
+
function vtkCCSMakeCuts(polys, polyEdges, outerPolyId, innerPolyId, points, cuts) {
|
|
1747
|
+
var q = [];
|
|
1748
|
+
var r = [];
|
|
1749
|
+
|
|
1750
|
+
for (var bb = 0; bb < 2; bb++) {
|
|
1751
|
+
var ptId1 = polys[outerPolyId][cuts[bb][0]];
|
|
1752
|
+
var ptId2 = polys[innerPolyId][cuts[bb][1]];
|
|
1753
|
+
points.getPoint(ptId1, q);
|
|
1754
|
+
points.getPoint(ptId2, r);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
var outerPoly = polys[outerPolyId];
|
|
1758
|
+
var innerPoly = polys[innerPolyId];
|
|
1759
|
+
var outerEdges = polyEdges[outerPolyId];
|
|
1760
|
+
var innerEdges = polyEdges[innerPolyId]; // Generate new polys from the cuts
|
|
1761
|
+
|
|
1762
|
+
var n = outerPoly.length;
|
|
1763
|
+
var m = innerPoly.length;
|
|
1764
|
+
var idx; // Generate poly1
|
|
1765
|
+
|
|
1766
|
+
var n1 = n * (cuts[1][0] < cuts[0][0]) + cuts[1][0] - cuts[0][0] + 1;
|
|
1767
|
+
var n2 = n1 + m * (cuts[0][1] < cuts[1][1]) + cuts[0][1] - cuts[1][1] + 1;
|
|
1768
|
+
var poly1 = [];
|
|
1769
|
+
poly1.length = n2;
|
|
1770
|
+
var edges1 = new Array(n2);
|
|
1771
|
+
idx = cuts[0][0];
|
|
1772
|
+
|
|
1773
|
+
for (var i1 = 0; i1 < n1; i1++) {
|
|
1774
|
+
var k = idx++;
|
|
1775
|
+
poly1[i1] = outerPoly[k];
|
|
1776
|
+
edges1[i1] = outerEdges[k];
|
|
1777
|
+
idx *= idx !== n;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
edges1[n1 - 1] = -1;
|
|
1781
|
+
idx = cuts[1][1];
|
|
1782
|
+
|
|
1783
|
+
for (var i2 = n1; i2 < n2; i2++) {
|
|
1784
|
+
var _k2 = idx++;
|
|
1785
|
+
|
|
1786
|
+
poly1[i2] = innerPoly[_k2];
|
|
1787
|
+
edges1[i2] = innerEdges[_k2];
|
|
1788
|
+
idx *= idx !== m;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
edges1[n2 - 1] = -1; // Generate poly2
|
|
1792
|
+
|
|
1793
|
+
var m1 = n * (cuts[0][0] < cuts[1][0]) + cuts[0][0] - cuts[1][0] + 1;
|
|
1794
|
+
var m2 = m1 + m * (cuts[1][1] < cuts[0][1]) + cuts[1][1] - cuts[0][1] + 1;
|
|
1795
|
+
var poly2 = [];
|
|
1796
|
+
poly2.length = m2;
|
|
1797
|
+
var edges2 = new Array(m2);
|
|
1798
|
+
idx = cuts[1][0];
|
|
1799
|
+
|
|
1800
|
+
for (var j1 = 0; j1 < m1; j1++) {
|
|
1801
|
+
var _k3 = idx++;
|
|
1802
|
+
|
|
1803
|
+
poly2[j1] = outerPoly[_k3];
|
|
1804
|
+
edges2[j1] = outerEdges[_k3];
|
|
1805
|
+
idx *= idx !== n;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
edges2[m1 - 1] = -1;
|
|
1809
|
+
idx = cuts[0][1];
|
|
1810
|
+
|
|
1811
|
+
for (var j2 = m1; j2 < m2; j2++) {
|
|
1812
|
+
var _k4 = idx++;
|
|
1813
|
+
|
|
1814
|
+
poly2[j2] = innerPoly[_k4];
|
|
1815
|
+
edges2[j2] = innerEdges[_k4];
|
|
1816
|
+
idx *= idx !== m;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
edges2[m2 - 1] = -1; // Replace outerPoly and innerPoly with these new polys
|
|
1820
|
+
|
|
1821
|
+
polys[outerPolyId] = poly1;
|
|
1822
|
+
polys[innerPolyId] = poly2;
|
|
1823
|
+
polyEdges[outerPolyId] = edges1;
|
|
1824
|
+
polyEdges[innerPolyId] = edges2;
|
|
1825
|
+
} // ---------------------------------------------------
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* After the holes have been identified, make cuts between the
|
|
1829
|
+
* outer poly and each hole. Make two cuts per hole. The only
|
|
1830
|
+
* strict requirement is that the cut must not intersect any
|
|
1831
|
+
* edges, but it's best to make sure that no really sharp angles
|
|
1832
|
+
* are created.
|
|
1833
|
+
*
|
|
1834
|
+
* @param {Array[]} polys
|
|
1835
|
+
* @param {vtkPoints} points
|
|
1836
|
+
* @param {Array[]} polyGroups
|
|
1837
|
+
* @param {Array} polyEdges
|
|
1838
|
+
* @param {Vector3} normal
|
|
1839
|
+
* @returns {boolean}
|
|
1840
|
+
*/
|
|
1841
|
+
|
|
1842
|
+
function vtkCCSCutHoleyPolys(polys, points, polyGroups, polyEdges, normal) {
|
|
1843
|
+
var cutFailure = 0; // Go through all groups and cut out the first inner poly that is
|
|
1844
|
+
// found. Every time an inner poly is cut out, the groupId counter
|
|
1845
|
+
// is reset because cutting a poly creates a new group.
|
|
1846
|
+
|
|
1847
|
+
var groupId = 0;
|
|
1848
|
+
|
|
1849
|
+
while (groupId < polyGroups.length) {
|
|
1850
|
+
var polyGroup = polyGroups[groupId]; // Only need to make a cut if the group size is greater than 1
|
|
1851
|
+
|
|
1852
|
+
if (polyGroup.length > 1) {
|
|
1853
|
+
// The first member of the group is the outer poly
|
|
1854
|
+
var outerPolyId = polyGroup[0]; // The second member of the group is the first inner poly
|
|
1855
|
+
|
|
1856
|
+
var innerPolyId = polyGroup[1]; // Sort the group by size, do largest holes first
|
|
1857
|
+
|
|
1858
|
+
var innerBySize = new Array(polyGroup.length);
|
|
1859
|
+
|
|
1860
|
+
for (var i = 1; i < polyGroup.length; i++) {
|
|
1861
|
+
innerBySize[i] = [polys[polyGroup[i]].length, i];
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
innerBySize = [innerBySize[0]].concat(_toConsumableArray(innerBySize.splice(1).sort(function (a, b) {
|
|
1865
|
+
return a[0] - b[0];
|
|
1866
|
+
})));
|
|
1867
|
+
reverseElements(innerBySize, 1, innerBySize.length - 1); // Need to check all inner polys in sequence, until one succeeds.
|
|
1868
|
+
// Do a quick search first, then do an exhaustive search.
|
|
1869
|
+
|
|
1870
|
+
var madeCut = 0;
|
|
1871
|
+
var inner = 0;
|
|
1872
|
+
|
|
1873
|
+
for (var exhaustive = 0; exhaustive < 2 && !madeCut; exhaustive++) {
|
|
1874
|
+
for (var j = 1; j < polyGroup.length; j++) {
|
|
1875
|
+
inner = innerBySize[j][1];
|
|
1876
|
+
innerPolyId = polyGroup[inner];
|
|
1877
|
+
var cuts = [];
|
|
1878
|
+
|
|
1879
|
+
if (vtkCCSFindCuts(polys, polyGroup, outerPolyId, innerPolyId, points, normal, cuts, exhaustive)) {
|
|
1880
|
+
vtkCCSMakeCuts(polys, polyEdges, outerPolyId, innerPolyId, points, cuts);
|
|
1881
|
+
madeCut = 1;
|
|
1882
|
+
break;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
if (madeCut) {
|
|
1888
|
+
// Move successfully cut innerPolyId into its own group
|
|
1889
|
+
polyGroup.splice(inner, 1); // Only add if innerPolyId hasn't been set already.
|
|
1890
|
+
// Having the same poly occur as both polyGroup and
|
|
1891
|
+
// innerPoly would cause an infinite loop.
|
|
1892
|
+
|
|
1893
|
+
if (polyGroups[innerPolyId].length === 0) {
|
|
1894
|
+
polyGroups[innerPolyId].push(innerPolyId);
|
|
1895
|
+
}
|
|
1896
|
+
} else {
|
|
1897
|
+
// Remove all failed inner polys from the group
|
|
1898
|
+
for (var k = 1; k < polyGroup.length; k++) {
|
|
1899
|
+
innerPolyId = polyGroup[k]; // Only add if innerPolyId hasn't been set already.
|
|
1900
|
+
// Having the same poly occur as both polyGroup and
|
|
1901
|
+
// innerPoly would cause an infinite loop.
|
|
1902
|
+
|
|
1903
|
+
if (polyGroups[innerPolyId].length === 0) {
|
|
1904
|
+
polyGroups[innerPolyId].push(innerPolyId);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
polyGroup.length = 1;
|
|
1909
|
+
cutFailure = 1;
|
|
1910
|
+
} // If there are other interior polys in the group, find out whether
|
|
1911
|
+
// they are in poly1 or poly2
|
|
1912
|
+
|
|
1913
|
+
|
|
1914
|
+
if (polyGroup.length > 1) {
|
|
1915
|
+
var poly1 = polys[outerPolyId];
|
|
1916
|
+
var pp = new Float64Array(3 * poly1.length);
|
|
1917
|
+
var bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
|
1918
|
+
var tol2 = vtkCCSPrepareForPolyInPoly(poly1, points, pp, bounds);
|
|
1919
|
+
var nextGroupId = groupId;
|
|
1920
|
+
var ii = 1;
|
|
1921
|
+
|
|
1922
|
+
while (ii < polyGroup.length) {
|
|
1923
|
+
if (vtkCCSPolyInPoly(poly1, polys[polyGroup[ii]], points, normal, pp, bounds, tol2)) {
|
|
1924
|
+
// Keep this poly in polyGroup
|
|
1925
|
+
ii++;
|
|
1926
|
+
} else {
|
|
1927
|
+
// Move this poly to poly2 group
|
|
1928
|
+
polyGroups[innerPolyId].push(polyGroup[ii]);
|
|
1929
|
+
polyGroup.splice(ii, 1); // Reduce the groupId to ensure that this new group will get cut
|
|
1930
|
+
|
|
1931
|
+
if (innerPolyId < nextGroupId) {
|
|
1932
|
+
nextGroupId = innerPolyId;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
} // Set the groupId for the next iteration
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
groupId = nextGroupId; // eslint-disable-next-line no-continue
|
|
1939
|
+
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
} // Increment to the next group
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
groupId++;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
return !cutFailure;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
export { reverseElements, vtkCCSCheckCut, vtkCCSCheckPolygonSense, vtkCCSCutHoleyPolys, vtkCCSCutQuality, vtkCCSFindCuts, vtkCCSFindSharpestVerts, vtkCCSFindTrueEdges, vtkCCSInsertTriangle, vtkCCSJoinLooseEnds, vtkCCSMakeCuts, vtkCCSMakeHoleyPolys, vtkCCSMakePolysFromLines, vtkCCSPolyInPoly, vtkCCSPrepareForPolyInPoly, vtkCCSReversePoly, vtkCCSSplitAtPinchPoints, vtkCCSTriangleQuality, vtkCCSTriangulate, vtkCCSVectorProgression };
|