import macro from 'vtk.js/Sources/macros'; import vtkPoints from 'vtk.js/Sources/Common/Core/Points'; import * as vtkMath from 'vtk.js/Sources/Common/Core/Math'; import vtkLine from 'vtk.js/Sources/Common/DataModel/Line'; import vtkPolygon from 'vtk.js/Sources/Common/DataModel/Polygon'; import vtkIncrementalOctreePointLocator from 'vtk.js/Sources/Common/DataModel/IncrementalOctreePointLocator'; import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants'; import { CCS_POLYGON_TOLERANCE } from './Constants'; import { PolygonWithPointIntersectionState } from '../../../Common/DataModel/Polygon/Constants';
const { vtkErrorMacro } = macro;
export function reverseElements( arr, firstIdx = undefined, lastIdx = undefined ) { const first = firstIdx ?? 0; const last = lastIdx ?? arr.length - 1; const mid = first + Math.floor((last - first) / 2); for (let i = first; i <= mid; ++i) { [arr[i], arr[last - (i - first)]] = [arr[last - (i - first)], arr[i]]; } }
export function vtkCCSTriangleQuality(p0, p1, p2, normal) { const u = []; const v = []; const w = [];
vtkMath.subtract(p1, p0, u); vtkMath.subtract(p2, p1, v); vtkMath.subtract(p0, p2, w);
const 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];
let 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]);
perim *= perim; perim = perim !== 0 ? perim : 1.0;
return (area2 / perim) * 10.392304845413264; }
export function vtkCCSInsertTriangle( polys, poly, trids, polyEdges, originalEdges ) { const nextVert = [1, 2, 0];
let edgeCount = 0; const edgeLocs = [-1, -1, -1];
for (let vert = 0; vert < 3; vert++) { const currId = trids[vert]; const edgeLoc = polyEdges[currId]; if (edgeLoc >= 0) { let nextId = currId + 1; if (nextId === poly.length) { nextId = 0; }
if (nextId === trids[nextVert[vert]]) { edgeLocs[vert] = edgeLoc; edgeCount++; } } }
if (edgeCount === 0) { polys.insertNextCell([poly[trids[0]], poly[trids[1]], poly[trids[2]]]); } else { const edgePts = [ [poly[trids[0]], poly[trids[1]]], [poly[trids[1]], poly[trids[2]]], [poly[trids[2]], poly[trids[0]]], ];
let maxPoints = 0; let currSide = 0; for (let i = 0; i < 3; i++) { if (edgeLocs[i] >= 0) { const edgeLoc = edgeLocs[i]; const npts = originalEdges[edgeLoc]; const pts = originalEdges.slice(edgeLoc + 1, edgeLoc + 1 + npts); if (!(edgePts[i][0] === pts[0] || edgePts[i][1] === pts[npts - 1])) { vtkErrorMacro('assertion error in vtkCCSInsertTriangle'); } if (npts > maxPoints) { maxPoints = npts; currSide = i; } edgePts[i] = pts; } }
const prevSide = (currSide + 2) % 3; const nextSide = (currSide + 1) % 3;
const prevNeeded = edgePts[prevSide].length > 2; const nextNeeded = edgePts[nextSide].length > 2;
const tailPtIds = []; tailPtIds[prevSide] = edgePts[currSide][1]; tailPtIds[currSide] = edgePts[prevSide][0]; tailPtIds[nextSide] = edgePts[currSide][edgePts[currSide].length - 2];
for (let side = 0; side < 3; side++) { if ( (side !== prevSide || prevNeeded) && (side !== nextSide || nextNeeded) ) { let m = 0; let n = edgePts[side].length - 1;
if (side === currSide) { m += prevNeeded; n -= nextNeeded; }
for (let k = m; k < n; k++) { polys.insertNextCell([ edgePts[side][k], edgePts[side][k + 1], tailPtIds[side], ]); } } } } }
export function vtkCCSTriangulate( poly, points, polyEdges, originalEdges, polys, normal ) { let n = poly.length;
if (n < 3) { return true; }
if (n === 3) { const trids = [0, 1, 2]; vtkCCSInsertTriangle(polys, poly, trids, polyEdges, originalEdges); return true; }
let triangulationFailure = false; let ppoint = []; let point = []; let npoint = []; let i = 0; let j = 0; let k = 0;
const verts = []; verts.length = n;
for (i = 0; i < n; i++) { verts[i] = [i, 0]; }
k = n - 2; points.getPoint(poly[verts[k][0]], point); i = n - 1; points.getPoint(poly[verts[i][0]], npoint);
let concave = 0; let maxq = 0; let maxi = 0; for (j = 0; j < n; j++) { [ppoint, point, npoint] = [point, npoint, ppoint]; points.getPoint(poly[verts[j][0]], npoint);
const q = vtkCCSTriangleQuality(ppoint, point, npoint, normal); if (q > maxq) { maxi = i; maxq = q; } concave += q < 0; verts[i][1] = q; i = j; }
let foundEar; for (;;) { if (maxq <= Number.MIN_VALUE) { triangulationFailure = true; break; }
i = maxi; j = i + 1 !== n ? i + 1 : 0; k = i !== 0 ? i - 1 : n - 1;
if (verts[i][1] > 0) { foundEar = true; points.getPoint(poly[verts[j][0]], npoint); points.getPoint(poly[verts[k][0]], ppoint);
if (concave) { const v = []; const u = [];
vtkMath.subtract(npoint, ppoint, v); vtkMath.cross(v, normal, u); const d = vtkMath.dot(ppoint, u);
let jj = j + 1 !== n ? j + 1 : 0; let x = []; points.getPoint(poly[verts[jj][0]], x); let side = vtkMath.dot(x, u) < d; let foundNegative = side;
jj = jj + 1 !== n ? jj + 1 : 0; let y = []; const s = []; const t = []; for (; foundEar && jj !== k; jj = jj + 1 !== n ? jj + 1 : 0) { [x, y] = [y, x]; points.getPoint(poly[verts[jj][0]], x); const sside = vtkMath.dot(x, u) < d; if (side ? !sside : sside) { side = !side; foundNegative = true; foundEar = vtkLine.intersection(ppoint, npoint, x, y, s, t) === vtkLine.IntersectionState.NO_INTERSECTION; } } foundEar &&= foundNegative; }
if (!foundEar) { verts[i][1] = Number.MIN_VALUE; } else { const trids = [verts[i][0], verts[j][0], verts[k][0]]; vtkCCSInsertTriangle(polys, poly, trids, polyEdges, originalEdges);
verts.splice(i, 1); k -= i === 0; j -= j !== 0;
if (--n < 3) { break; }
const kk = k !== 0 ? k - 1 : n - 1; points.getPoint(poly[verts[kk][0]], point); const kq = vtkCCSTriangleQuality(point, ppoint, npoint, normal); concave -= verts[k][1] < 0 && kq >= 0; verts[k][1] = kq;
const jj = j + 1 !== n ? j + 1 : 0; points.getPoint(poly[verts[jj][0]], point); const jq = vtkCCSTriangleQuality(ppoint, npoint, point, normal); concave -= verts[j][1] < 0 && jq >= 0; verts[j][1] = jq; } }
maxi = 0; maxq = verts[0][1]; for (i = 1; i < n; i++) { const q = verts[i][1]; if (q > maxq) { maxi = i; maxq = q; } } }
return !triangulationFailure; }
export function vtkCCSMakePolysFromLines( polyData, firstLine, endLine, oriented, newPolys, incompletePolys ) { let npts = 0; let pts = [];
const usedLines = new Uint8Array(endLine - firstLine);
polyData.buildLinks(polyData.getPoints().getNumberOfPoints());
let numNewPolys = 0; let remainingLines = endLine - firstLine;
while (remainingLines > 0) { const polyId = numNewPolys++; const poly = []; newPolys.push(poly);
let lineId = 0; let completePoly = false;
for (lineId = firstLine; lineId < endLine; lineId++) { if (!usedLines[lineId - firstLine]) { pts = polyData.getCellPoints(lineId).cellPointIds; npts = pts.length;
let n = npts; if (npts > 2 && pts[0] === pts[npts - 1]) { n = npts - 1; completePoly = true; } poly.length = n; for (let i = 0; i < n; i++) { poly[i] = pts[i]; } break; } }
usedLines[lineId - firstLine] = 1; remainingLines--;
let noLinesMatch = remainingLines === 0 && !completePoly;
while (!completePoly && !noLinesMatch && remainingLines > 0) { noLinesMatch = true;
const npoly = poly.length;
const lineEndPts = []; const endPts = [poly[npoly - 1], poly[0]];
for (let endIdx = 0; endIdx < 2; endIdx++) { const matches = []; const cells = polyData.getPointCells(endPts[endIdx]);
for (let icell = 0; icell < cells.length; icell++) { lineId = cells[icell]; if ( lineId >= firstLine && lineId < endLine && !usedLines[lineId - firstLine] ) { pts = polyData.getCellPoints(lineId).cellPointIds; npts = pts.length; lineEndPts[0] = pts[0]; lineEndPts[1] = pts[npts - 1];
if ( endPts[endIdx] === lineEndPts[endIdx] || (!oriented && endPts[endIdx] === lineEndPts[1 - endIdx]) ) { matches.push(lineId); } } }
if (matches.length > 0) { if (matches.length > 1) { let k = matches.length; do { lineId = matches[--k]; pts = polyData.getCellPoints(lineId).cellPointIds; npts = pts.length; lineEndPts[0] = pts[0]; lineEndPts[1] = pts[npts - 1]; const r = endPts[endIdx] !== lineEndPts[endIdx];
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]))) ) { matches.splice(k, 1); } } while (k > 0 && matches.length > 1);
}
lineId = matches[0]; pts = polyData.getCellPoints(lineId).cellPointIds; npts = pts.length; lineEndPts[0] = pts[0]; lineEndPts[1] = pts[npts - 1];
if (endPts[endIdx] === lineEndPts[endIdx]) { completePoly = endPts[1 - endIdx] === lineEndPts[1 - endIdx]; } else { completePoly = endPts[1 - endIdx] === lineEndPts[endIdx]; }
if (endIdx === 0) { for (let i = 1; i < npts - (completePoly ? 1 : 0); i++) { poly.push(pts[i]); } } else { for (let i = completePoly ? 1 : 0; i < npts - 1; i++) { poly.unshift(pts[i]); } }
if (endPts[endIdx] !== lineEndPts[endIdx]) { let pit = poly.length; let ptsIt = completePoly ? 1 : 0; let ptsEnd = npts - 1; if (endIdx === 1) { pit = npts - 1 - (completePoly ? 1 : 0); ptsIt = 1; ptsEnd = npts - (completePoly ? 1 : 0); } while (ptsIt !== ptsEnd) { poly[--pit] = poly[ptsIt++]; } }
usedLines[lineId - firstLine] = 1; remainingLines--; noLinesMatch = false; } } }
if (noLinesMatch) { incompletePolys.push(polyId); } } }
export function vtkCCSJoinLooseEnds(polys, incompletePolys, points, normal) { const tol = CCS_POLYGON_TOLERANCE;
const removePolys = [];
const p1 = []; const p2 = []; let poly1; let poly2; let pt1; let pt2; let dMin; let iMin; let v; let d;
let n = incompletePolys.length; while (n !== 0) { poly1 = polys[incompletePolys[n - 1]]; pt1 = poly1[poly1.length - 1]; points.getPoint(pt1, p1);
dMin = Number.MAX_VALUE; iMin = 0;
for (let i = 0; i < n; i++) { poly2 = polys[incompletePolys[i]]; pt2 = poly2[0]; points.getPoint(pt2, p2);
v = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]]; d = vtkMath.norm(v); if (d !== 0) { v[0] /= d; v[1] /= d; v[2] /= d; }
const pm = [ 0.5 * (p1[0] + p2[0]), 0.5 * (p1[1] + p2[1]), 0.5 * (p1[2] + p2[2]), ];
const pc = []; vtkMath.cross(normal, v, pc); pc[3] = -vtkMath.dot(pc, pm);
let badPoint = false; const m = polys.length; const p = []; for (let j = 0; j < m && !badPoint; j++) { const poly = polys[j]; const npts = poly.length; for (let k = 0; k < npts; k++) { const ptId = poly[k]; if (ptId !== pt1 && ptId !== pt2) { points.getPoint(ptId, p); const val = p[0] * pc[0] + p[1] * pc[1] + p[2] * pc[2] + pc[3]; const r2 = vtkMath.distance2BetweenPoints(p, pm);
if (val < 0 && val * val > tol * tol * r2) { badPoint = true; break; } } }
if (!badPoint && d < dMin) { dMin = d; iMin = i; } } }
if (dMin < Number.MAX_VALUE) { if (iMin === n - 1) { incompletePolys.pop(); } else { const id2 = incompletePolys[iMin];
poly1.push(...polys[id2]);
removePolys.push(id2); incompletePolys.splice(iMin, 1); } } else { removePolys.push(incompletePolys[n - 1]); incompletePolys.pop(); } n = incompletePolys.length; }
removePolys.sort((a, b) => a - b); let i = removePolys.length; while (i > 0) { polys.splice(removePolys[--i], 1); }
incompletePolys.length = 0; }
export function vtkCCSVectorProgression(p, p1, p2, p3, normal) { const v1 = [p1[0] - p[0], p1[1] - p[1], p1[2] - p[2]]; const v2 = [p2[0] - p[0], p2[1] - p[1], p2[2] - p[2]]; const v3 = [p3[0] - p[0], p3[1] - p[1], p3[2] - p[2]]; const w1 = []; const w2 = [];
vtkMath.cross(v2, v1, w1); vtkMath.cross(v2, v3, w2); const s1 = vtkMath.dot(w1, normal); const s2 = vtkMath.dot(w2, normal);
if (s1 !== 0 && s2 !== 0) { const sb1 = s1 < 0; const sb2 = s2 < 0;
if (sb1 ? !sb2 : sb2) { return 1 - 2 * sb2; }
const c1 = vtkMath.dot(v2, v1); const l1 = vtkMath.norm(v1); const c2 = vtkMath.dot(v2, v3); const l2 = vtkMath.norm(v3);
const ck = (c2 * l2 - c1 * l1) * (1 - sb1 * 2);
if (ck !== 0) { return 1 - 2 * (ck < 0); } }
return 0; }
export function vtkCCSSplitAtPinchPoints( polys, points, polyGroups, polyEdges, normal, oriented ) { const tryPoints = vtkPoints.newInstance({ dataType: VtkDataTypes.DOUBLE, empty: true, });
const locator = vtkIncrementalOctreePointLocator.newInstance();
let splitCount = 0; let poly; let n; let bounds; let tol;
for (let i = 0; i < polys.length; i++) { poly = polys[i]; n = poly.length;
bounds = []; tol = CCS_POLYGON_TOLERANCE * Math.sqrt(vtkPolygon.getBounds(poly, points, bounds));
if (tol === 0) { continue; }
tryPoints.initialize(); locator.setTolerance(tol); locator.initPointInsertion(tryPoints, bounds);
let foundMatch = false; let idx1 = 0; let idx2 = 0; let unique = 0; const point = []; const p1 = []; const p2 = []; const p3 = [];
for (idx2 = 0; idx2 < n; idx2++) { points.getPoint(poly[idx2], point);
const { success, pointIdx } = locator.insertUniquePoint(point, 0); if (!success) { locator.insertNextPoint(point);
idx1 = pointIdx; unique = poly[idx2] !== poly[idx1];
if (idx2 > idx1 + 2 - unique && n + idx1 > idx2 + 2 - unique) { if (oriented) { let prevIdx = n + idx1 - 1; let midIdx = idx1 + 1; let nextIdx = idx2 + 1; if (prevIdx >= n) { prevIdx -= n; } if (midIdx >= n) { midIdx -= n; } if (nextIdx >= n) { nextIdx -= n; }
points.getPoint(poly[prevIdx], p1); points.getPoint(poly[midIdx], p2); points.getPoint(poly[nextIdx], p3);
if (vtkCCSVectorProgression(point, p1, p2, p3, normal) > 0) { foundMatch = true; break; } } else { foundMatch = true; break; } } } }
if (foundMatch) { splitCount++;
const m = idx2 - idx1; const oldPoly = polys[i]; const oldEdges = polyEdges[i]; const newPoly1 = oldPoly.slice(idx1, idx1 + m + unique); const newEdges1 = oldEdges.slice(idx1, idx1 + m + unique); const newPoly2 = new Array(n - m + unique); const newEdges2 = new Array(n - m + unique);
if (unique) { newEdges1[m] = -1; }
for (let j = 0; j < idx1 + unique; j++) { newPoly2[j] = oldPoly[j]; newEdges2[j] = oldEdges[j]; } if (unique) { newEdges2[idx1] = -1; } for (let k = idx2; k < n; k++) { newPoly2[k - m + unique] = oldPoly[k]; newEdges2[k - m + unique] = oldEdges[k]; }
polys[i] = newPoly1; polyEdges[i] = newEdges1; polys.push(newPoly2); polyEdges.push(newEdges2);
polyGroups.length = polys.length; if (polyGroups[i].length > 0) { polyGroups[polys.length - 1].push(polys.length - 1); } } } return splitCount; }
export function vtkCCSFindTrueEdges(polys, points, polyEdges, originalEdges) { const atol2 = CCS_POLYGON_TOLERANCE * CCS_POLYGON_TOLERANCE;
const p0 = []; const p1 = []; const p2 = []; const v1 = []; const v2 = []; let l1; let l2;
for (let polyId = 0; polyId < polys.length; polyId++) { const oldPoly = polys[polyId]; const n = oldPoly.length; const newEdges = []; polyEdges.push(newEdges);
if (n < 4) { newEdges[0] = -1; newEdges[1] = -1; newEdges[2] = -1; continue; }
let m = n;
const bounds = []; const tol2 = vtkPolygon.getBounds(oldPoly, points, bounds) * atol2;
const newPoly = []; let cornerPointId = 0; let oldOriginalId = -1;
const partialEdge = []; let cellCount = 0;
points.getPoint(oldPoly[n - 1], p0); points.getPoint(oldPoly[0], p1); vtkMath.subtract(p1, p0, v1); l1 = vtkMath.dot(v1, v1);
for (let j = 0; j < n; j++) { let k = j + 1; if (k >= n) { k -= n; }
points.getPoint(oldPoly[k], p2); vtkMath.subtract(p2, p1, v2); l2 = vtkMath.dot(v2, v2);
const c = vtkMath.dot(v1, v2); const s2 = l1 * l2 - c * c;
const pointId = oldPoly[j];
if ( m <= 3 || (l1 > tol2 && (c < 0 || l1 < tol2 || l2 < tol2 || s2 > l1 * l2 * atol2)) ) { if (cellCount > 1) { if (pointId !== oldOriginalId) { originalEdges.push(pointId); cellCount++; } const countLocation = originalEdges.length - cellCount - 1; originalEdges[countLocation] = cellCount; newEdges.push(countLocation); } else if (cellCount === 0) { partialEdge.push(pointId); } else { newEdges.push(-1); }
newPoly.push(pointId);
cornerPointId = pointId; oldOriginalId = pointId; cellCount = 1;
p0[0] = p2[0]; p0[1] = p2[1]; p0[2] = p2[2]; p1[0] = p2[0]; p1[1] = p2[1]; p1[2] = p2[2]; v1[0] = v2[0]; v1[1] = v2[1]; v1[2] = v2[2]; l1 = l2; } else { if (cellCount > 0 && pointId !== oldOriginalId) { if (cellCount === 1) { originalEdges.push(1); originalEdges.push(cornerPointId); } originalEdges.push(pointId); oldOriginalId = pointId; cellCount++; } else { partialEdge.push(pointId); }
m--;
p1[0] = p2[0]; p1[1] = p2[1]; p1[2] = p2[2]; vtkMath.subtract(p2, p0, v1); l1 = vtkMath.dot(v1, v1); } }
for (let ii = 0; ii < partialEdge.length; ii++) { const pointId = partialEdge[ii]; if (pointId !== oldOriginalId) { if (cellCount === 1) { originalEdges.push(1); originalEdges.push(cornerPointId); } originalEdges.push(pointId); oldOriginalId = pointId; cellCount++; } }
if (cellCount > 1) { const countLocation = originalEdges.length - cellCount - 1; originalEdges[countLocation] = cellCount; newEdges.push(countLocation); } polys[polyId] = newPoly; } }
export function vtkCCSReversePoly(poly, edges, originalEdges) { reverseElements(poly, 1, poly.length - 1); edges.reverse(); for (let i = 0; i < edges.length; i++) { if (edges[i] >= 0) { const firstPtsIdx = edges[i] + 1; const npts = originalEdges[edges[i]]; reverseElements(originalEdges, firstPtsIdx, firstPtsIdx + npts - 1); } } }
export function vtkCCSCheckPolygonSense(poly, points, normal) { const pnormal = [0.0, 0.0, 0.0]; const p0 = []; const p1 = []; const p2 = []; const v1 = []; const v2 = []; const v = [];
points.getPoint(poly[0], p0); points.getPoint(poly[1], p1); vtkMath.subtract(p1, p0, v1);
for (let jj = 2; jj < poly.length; jj++) { points.getPoint(poly[jj], p2); vtkMath.subtract(p2, p0, v2); vtkMath.cross(v1, v2, v); vtkMath.add(pnormal, v, pnormal); p1[0] = p2[0]; p1[1] = p2[1]; p1[2] = p2[2]; v1[0] = v2[0]; v1[1] = v2[1]; v1[2] = v2[2]; }
const d = vtkMath.dot(pnormal, normal);
return { isNormalNotZero: d !== 0, sense: d > 0 }; }
export function vtkCCSPolyInPoly( outerPoly, innerPoly, points, normal, pp, bounds, tol2 ) { const n = outerPoly.length; const m = innerPoly.length; const p = []; const q1 = []; const q2 = [];
for (let jj = 0; jj < m; jj++) { const kk = (jj >> 1) + (jj & 1) * ((m + 1) >> 1); points.getPoint(innerPoly[kk], p); const intersectionState = vtkPolygon.pointInPolygon(p, pp, bounds, normal); if (intersectionState === PolygonWithPointIntersectionState.FAILURE) { vtkErrorMacro('Error finding point in polygon in vtkCCSPolyInPoly'); } if (intersectionState !== PolygonWithPointIntersectionState.OUTSIDE) { let pointOnEdge = 0; points.getPoint(outerPoly[n - 1], q1);
for (let ii = 0; ii < n; ii++) { points.getPoint(outerPoly[ii], q2); const { distance } = vtkLine.distanceToLine(p, q1, q2); if (distance < tol2) { pointOnEdge = 1; break; } q1[0] = q2[0]; q1[1] = q2[1]; q1[2] = q2[2]; }
if (!pointOnEdge) { return true; } } }
return false; }
export function vtkCCSPrepareForPolyInPoly(outerPoly, points, pp, bounds) { const n = outerPoly.length;
if (n === 0) { return 0.0; }
const point = []; let j = 0; for (let i = 0; i < n; i++) { points.getPoint(outerPoly[i], point); pp[j++] = point[0]; pp[j++] = point[1]; pp[j++] = point[2]; }
return ( vtkPolygon.getBounds(outerPoly, points, bounds) * (CCS_POLYGON_TOLERANCE * CCS_POLYGON_TOLERANCE) ); }
export function vtkCCSMakeHoleyPolys( newPolys, points, polyGroups, polyEdges, originalEdges, normal, oriented ) { const numNewPolys = newPolys.length; if (numNewPolys <= 1) { return; }
const polyReversed = []; const innerPolys = [];
let groupCount; if (!oriented) { groupCount = new Int32Array(numNewPolys); }
let nmax = 1; for (let kk = 0; kk < numNewPolys; kk++) { nmax = Math.max(nmax, newPolys[kk].length); }
const pp = new Float64Array(3 * nmax); const bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; let tol2;
for (let i = 0; i < numNewPolys; i++) { const n = newPolys[i].length;
if (n < 3) { continue; }
const { isNormalNotZero, sense } = vtkCCSCheckPolygonSense( newPolys[i], points, normal ); if (isNormalNotZero) { polyReversed[i] = !sense; }
tol2 = vtkCCSPrepareForPolyInPoly(newPolys[i], points, pp, bounds);
for (let j = 0; j < numNewPolys; j++) { if (j !== i && newPolys[j].length >= 3) { const pg = polyGroups[j]; if (!pg.includes(i)) { if ( vtkCCSPolyInPoly( newPolys[i], newPolys[j], points, normal, pp.subarray(3 * n), bounds, tol2 ) ) { polyGroups[i].push(j); if (groupCount) { groupCount[j] += 1; } } } } } }
if (!oriented) { const outerPolyStack = []; for (let ll = 0; ll < numNewPolys; ll++) { if (groupCount[ll] === 0) { outerPolyStack.push(ll); } }
let j; while (outerPolyStack.length > 0) { j = outerPolyStack.length - 1; outerPolyStack.pop();
if (polyReversed[j]) { vtkCCSReversePoly(newPolys[j], polyEdges[j], originalEdges); polyReversed[j] = false; }
if (polyGroups[j].length > 1) { innerPolys.length = 0; for (let k = 1; k < polyGroups[j].length; k++) { const jj = polyGroups[j][k]; if (groupCount[jj] > 1) { groupCount[jj] -= 2; if (groupCount[jj] === 0) { outerPolyStack.push(jj); } } else { innerPolys[jj] = 1; polyGroups[jj].length = 0; if (!polyReversed[jj]) { vtkCCSReversePoly(newPolys[jj], polyEdges[jj], originalEdges); polyReversed[jj] = false; } } }
polyGroups[j].length = 0; polyGroups[j].push(j); for (let jj = 0; jj < numNewPolys; jj++) { if (innerPolys[jj]) { polyGroups[j].push(jj); } } } } } else { for (let j = 0; j < numNewPolys; j++) { if (polyReversed[j]) { polyGroups[j].length = 0; } else if (polyGroups[j].length > 1) { innerPolys.length = 0; for (let k = 1; k < polyGroups[j].length; k++) { innerPolys[polyGroups[j][k]] = true; }
for (let kk = 1; kk < polyGroups[j].length; kk++) { const jj = polyGroups[j][kk]; if (!polyReversed[jj]) { for (let ii = 0; ii < polyGroups[jj].length; ii++) { innerPolys[polyGroups[jj][ii]] = false; } } }
polyGroups[j].length = 0; polyGroups[j].push(j); for (let jj = 0; jj < numNewPolys; jj++) { if (innerPolys[jj]) { polyGroups[j].push(jj); } } } } }
}
export function vtkCCSCheckCut( polys, points, normal, polyGroup, outerPolyId, innerPolyId, outerIdx, innerIdx ) { const ptId1 = polys[outerPolyId][outerIdx]; const ptId2 = polys[innerPolyId][innerIdx];
const tol = CCS_POLYGON_TOLERANCE;
const p1 = []; const p2 = []; points.getPoint(ptId1, p1); points.getPoint(ptId2, p2);
const w = []; vtkMath.subtract(p2, p1, w); const l = vtkMath.normalize(w);
if (l === 0) { return true; }
const tol2 = l * l * tol * tol;
let polyId = outerPolyId; let polyIdx = outerIdx;
let r = p1; const r1 = []; let r2 = p2; const r3 = [];
for (let ii = 0; ii < 2; ii++) { const poly = polys[polyId]; const n = poly.length; let prevIdx = n - polyIdx - 1; let nextIdx = polyIdx + 1; if (prevIdx >= n) { prevIdx -= n; } if (nextIdx >= n) { nextIdx -= n; }
points.getPoint(poly[prevIdx], r1); points.getPoint(poly[nextIdx], r3);
if (vtkCCSVectorProgression(r, r1, r2, r3, normal) > 0) { return false; }
polyId = innerPolyId; polyIdx = innerIdx; r = p2; r2 = p1; }
const pc = []; vtkMath.cross(normal, w, pc); pc[3] = -vtkMath.dot(pc, p1);
for (let i = 0; i < polyGroup.length; i++) { const poly = polys[polyGroup[i]]; const n = poly.length;
const q1 = []; const q2 = []; let qtId1 = poly[n - 1]; points.getPoint(qtId1, q1); let v1 = pc[0] * q1[0] + pc[1] * q1[1] + pc[2] * q1[2] + pc[3]; let c1 = v1 > 0;
for (let j = 0; j < n; j++) { const qtId2 = poly[j]; points.getPoint(qtId2, q2); const v2 = pc[0] * q2[0] + pc[1] * q2[1] + pc[2] * q2[2] + pc[3]; const c2 = v2 > 0;
if ( ptId1 !== qtId1 && ptId1 !== qtId2 && ptId2 !== qtId1 && ptId2 !== qtId2 ) { if ((c1 ? !c2 : c2) || v1 * v1 < tol2 || v2 * v2 < tol2) { vtkMath.subtract(q2, q1, w); if (vtkMath.dot(w, w) > 0) { const qc = []; vtkMath.cross(w, normal, qc); qc[3] = -vtkMath.dot(qc, q1);
const u1 = qc[0] * p1[0] + qc[1] * p1[1] + qc[2] * p1[2] + qc[3]; const u2 = qc[0] * p2[0] + qc[1] * p2[1] + qc[2] * p2[2] + qc[3]; const d1 = u1 > 0; const d2 = u2 > 0;
if (d1 ? !d2 : d2) { let p = p1; let q = q1; if (v2 * v2 < v1 * v1) { p = p2; } if (u2 * u2 < u1 * u1) { q = q2; } if (vtkMath.distance2BetweenPoints(p, q) > tol2) { return false; } } } } }
qtId1 = qtId2; q1[0] = q2[0]; q1[1] = q2[1]; q1[2] = q2[2]; v1 = v2; c1 = c2; } }
return true; }
export function vtkCCSCutQuality(outerPoly, innerPoly, i, j, points) { const n = outerPoly.length; const m = innerPoly.length;
const a = i > 0 ? i - 1 : n - 1; const b = i < n - 1 ? i + 1 : 0;
const c = j > 0 ? j - 1 : m - 1; const d = j < m - 1 ? j + 1 : 0;
const p0 = []; const p1 = []; const p2 = []; points.getPoint(outerPoly[i], p1); points.getPoint(innerPoly[j], p2);
const v1 = []; const v2 = []; vtkMath.subtract(p2, p1, v1);
const l1 = vtkMath.dot(v1, v1); let l2; let qmax = 0; let q;
points.getPoint(outerPoly[a], p0); vtkMath.subtract(p0, p1, v2); l2 = vtkMath.dot(v2, v2); if (l2 > 0) { q = vtkMath.dot(v1, v2); q *= q / l2; if (q > qmax) { qmax = q; } }
points.getPoint(outerPoly[b], p0); vtkMath.subtract(p0, p1, v2); l2 = vtkMath.dot(v2, v2); if (l2 > 0) { q = vtkMath.dot(v1, v2); q *= q / l2; if (q > qmax) { qmax = q; } }
points.getPoint(innerPoly[c], p0); vtkMath.subtract(p2, p0, v2); l2 = vtkMath.dot(v2, v2); if (l2 > 0) { q = vtkMath.dot(v1, v2); q *= q / l2; if (q > qmax) { qmax = q; } }
points.getPoint(innerPoly[d], p0); vtkMath.subtract(p2, p0, v2); l2 = vtkMath.dot(v2, v2); if (l2 > 0) { q = vtkMath.dot(v1, v2); q *= q / l2; if (q > qmax) { qmax = q; } }
if (l1 > 0) { return qmax / l1; }
return Number.MAX_VALUE; }
export function vtkCCSFindSharpestVerts(poly, points, normal, verts) { const p1 = []; const p2 = []; const v1 = []; const v2 = []; const v = []; let l1; let l2;
const minVal = [0, 0];
verts[0] = 0; verts[1] = 0;
const n = poly.length; points.getPoint(poly[n - 1], p2); points.getPoint(poly[0], p1);
vtkMath.subtract(p1, p2, v1); l1 = Math.sqrt(vtkMath.dot(v1, v1));
for (let j = 0; j < n; j++) { let k = j + 1; if (k === n) { k = 0; }
points.getPoint(poly[k], p2); vtkMath.subtract(p2, p1, v2); l2 = Math.sqrt(vtkMath.dot(v2, v2));
vtkMath.cross(v1, v2, v); const b = vtkMath.dot(v, normal);
if (b < 0 && l1 * l2 > 0) { const val = vtkMath.dot(v1, v2) / (l1 * l2); if (val < minVal[0]) { minVal[1] = minVal[0]; minVal[0] = val; verts[1] = verts[0]; verts[0] = j; } }
p1[0] = p2[0]; p1[1] = p2[1]; p1[2] = p2[2]; v1[0] = v2[0]; v1[1] = v2[1]; v1[2] = v2[2]; l1 = l2; } }
export function vtkCCSFindCuts( polys, polyGroup, outerPolyId, innerPolyId, points, normal, cuts, exhaustive ) { const outerPoly = polys[outerPolyId]; const innerPoly = polys[innerPolyId]; const innerSize = innerPoly.length; const verts = []; vtkCCSFindSharpestVerts(innerPoly, points, normal, verts);
const cutlist = []; cutlist.length = outerPoly.length;
let cutId = 0; cuts[0][0] = 0; cuts[0][1] = 0; cuts[1][0] = 0; cuts[1][1] = 0;
let foundCut = false; for (cutId = 0; cutId < 2; cutId++) { const count = exhaustive ? innerSize : 3;
for (let i = 0; i < count && !foundCut; i++) { let j = (i >> 1) + (i & 1) * ((innerSize + 1) >> 1); j = (j + verts[cutId]) % innerSize;
for (let kk = 0; kk < outerPoly.length; kk++) { const q = vtkCCSCutQuality(outerPoly, innerPoly, kk, j, points); cutlist[kk] = [q, kk]; }
cutlist.sort((a, b) => a[0] - b[0]);
for (let lid = 0; lid < cutlist.length; lid++) { const k = cutlist[lid][1];
if (cutId > 0) { if (j === cuts[0][1] || k === cuts[0][0]) { continue; }
const p1 = []; const p2 = []; points.getPoint(outerPoly[cuts[0][0]], p1); points.getPoint(innerPoly[cuts[0][1]], p2);
const q1 = []; const q2 = []; points.getPoint(outerPoly[k], q1); points.getPoint(innerPoly[j], q2);
let u; let v; if ( vtkLine.intersection(p1, p2, q1, q2, u, v) === vtkLine.IntersectionState.YES_INTERSECTION ) { continue; } }
if ( vtkCCSCheckCut( polys, points, normal, polyGroup, outerPolyId, innerPolyId, k, j ) ) { cuts[cutId][0] = k; cuts[cutId][1] = j; foundCut = true; break; } } }
if (!foundCut) { return false; } }
return true; }
export function vtkCCSMakeCuts( polys, polyEdges, outerPolyId, innerPolyId, points, cuts ) { const q = []; const r = []; for (let bb = 0; bb < 2; bb++) { const ptId1 = polys[outerPolyId][cuts[bb][0]]; const ptId2 = polys[innerPolyId][cuts[bb][1]]; points.getPoint(ptId1, q); points.getPoint(ptId2, r); }
const outerPoly = polys[outerPolyId]; const innerPoly = polys[innerPolyId];
const outerEdges = polyEdges[outerPolyId]; const innerEdges = polyEdges[innerPolyId];
const n = outerPoly.length; const m = innerPoly.length; let idx;
const n1 = n * (cuts[1][0] < cuts[0][0]) + cuts[1][0] - cuts[0][0] + 1; const n2 = n1 + m * (cuts[0][1] < cuts[1][1]) + cuts[0][1] - cuts[1][1] + 1;
const poly1 = []; poly1.length = n2; const edges1 = new Array(n2);
idx = cuts[0][0]; for (let i1 = 0; i1 < n1; i1++) { const k = idx++; poly1[i1] = outerPoly[k]; edges1[i1] = outerEdges[k]; idx *= idx !== n; } edges1[n1 - 1] = -1;
idx = cuts[1][1]; for (let i2 = n1; i2 < n2; i2++) { const k = idx++; poly1[i2] = innerPoly[k]; edges1[i2] = innerEdges[k]; idx *= idx !== m; } edges1[n2 - 1] = -1;
const m1 = n * (cuts[0][0] < cuts[1][0]) + cuts[0][0] - cuts[1][0] + 1; const m2 = m1 + m * (cuts[1][1] < cuts[0][1]) + cuts[1][1] - cuts[0][1] + 1;
const poly2 = []; poly2.length = m2; const edges2 = new Array(m2);
idx = cuts[1][0]; for (let j1 = 0; j1 < m1; j1++) { const k = idx++; poly2[j1] = outerPoly[k]; edges2[j1] = outerEdges[k]; idx *= idx !== n; } edges2[m1 - 1] = -1;
idx = cuts[0][1]; for (let j2 = m1; j2 < m2; j2++) { const k = idx++; poly2[j2] = innerPoly[k]; edges2[j2] = innerEdges[k]; idx *= idx !== m; } edges2[m2 - 1] = -1;
polys[outerPolyId] = poly1; polys[innerPolyId] = poly2; polyEdges[outerPolyId] = edges1; polyEdges[innerPolyId] = edges2; }
export function vtkCCSCutHoleyPolys( polys, points, polyGroups, polyEdges, normal ) { let cutFailure = 0;
let groupId = 0; while (groupId < polyGroups.length) { const polyGroup = polyGroups[groupId];
if (polyGroup.length > 1) { const outerPolyId = polyGroup[0];
let innerPolyId = polyGroup[1];
let innerBySize = new Array(polyGroup.length);
for (let i = 1; i < polyGroup.length; i++) { innerBySize[i] = [polys[polyGroup[i]].length, i]; }
innerBySize = [ innerBySize[0], ...innerBySize.splice(1).sort((a, b) => a[0] - b[0]), ]; reverseElements(innerBySize, 1, innerBySize.length - 1);
let madeCut = 0; let inner = 0; for (let exhaustive = 0; exhaustive < 2 && !madeCut; exhaustive++) { for (let j = 1; j < polyGroup.length; j++) { inner = innerBySize[j][1]; innerPolyId = polyGroup[inner];
const cuts = []; if ( vtkCCSFindCuts( polys, polyGroup, outerPolyId, innerPolyId, points, normal, cuts, exhaustive ) ) { vtkCCSMakeCuts( polys, polyEdges, outerPolyId, innerPolyId, points, cuts ); madeCut = 1; break; } } }
if (madeCut) { polyGroup.splice(inner, 1); if (polyGroups[innerPolyId].length === 0) { polyGroups[innerPolyId].push(innerPolyId); } } else { for (let k = 1; k < polyGroup.length; k++) { innerPolyId = polyGroup[k]; if (polyGroups[innerPolyId].length === 0) { polyGroups[innerPolyId].push(innerPolyId); } } polyGroup.length = 1; cutFailure = 1; }
if (polyGroup.length > 1) { const poly1 = polys[outerPolyId]; const pp = new Float64Array(3 * poly1.length); const bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; const tol2 = vtkCCSPrepareForPolyInPoly(poly1, points, pp, bounds);
let nextGroupId = groupId; let ii = 1; while (ii < polyGroup.length) { if ( vtkCCSPolyInPoly( poly1, polys[polyGroup[ii]], points, normal, pp, bounds, tol2 ) ) { ii++; } else { polyGroups[innerPolyId].push(polyGroup[ii]); polyGroup.splice(ii, 1);
if (innerPolyId < nextGroupId) { nextGroupId = innerPolyId; } } }
groupId = nextGroupId; continue; } } groupId++; } return !cutFailure; }
|