// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkIntersectionPolyDataFilter.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCleanPolyData.h" #include "vtkDelaunay2D.h" #include "vtkDoubleArray.h" #include "vtkIdList.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkLine.h" #include "vtkLongArray.h" #include "vtkOBBTree.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPointData.h" #include "vtkPointLocator.h" #include "vtkPoints.h" #include "vtkPolyDataNormals.h" #include "vtkPolygon.h" #include "vtkSmartPointer.h" #include "vtkSortDataArray.h" #include "vtkTransform.h" #include "vtkTransformPolyDataFilter.h" #include "vtkTriangle.h" #include "vtkTriangleFilter.h" #include "vtkUnstructuredGrid.h" #include #include //------------------------------------------------------------------------------ // Helper typedefs and data structures. VTK_ABI_NAMESPACE_BEGIN namespace { struct simPoint { vtkIdType id; double pt[3]; }; struct simPolygon { std::list points; int orientation; }; } typedef std::multimap IntersectionMapType; typedef IntersectionMapType::iterator IntersectionMapIteratorType; typedef struct { vtkIdType CellId; vtkIdType EdgeId; vtkIdType LineId; } CellEdgeLineType; typedef std::multimap PointEdgeMapType; typedef PointEdgeMapType::iterator PointEdgeMapIteratorType; //------------------------------------------------------------------------------ // Private implementation to hide STL. //------------------------------------------------------------------------------ class vtkIntersectionPolyDataFilter::Impl { public: Impl(); virtual ~Impl(); // Finds all triangle triangle intersections between two input OOBTrees static int FindTriangleIntersections( vtkOBBNode* node0, vtkOBBNode* node1, vtkMatrix4x4* transform, void* arg); // Runs the split mesh for the designated input surface int SplitMesh(int inputIndex, vtkPolyData* output, vtkPolyData* intersectionLines); protected: // Split cells into polygons created by intersection lines vtkCellArray* SplitCell(vtkPolyData* input, vtkIdType cellId, const vtkIdType* cellPts, IntersectionMapType* map, vtkPolyData* interLines, int inputIndex, int numCurrCells); // Function to add point to check edge list for remeshing step int AddToPointEdgeMap(int index, vtkIdType ptId, double x[3], vtkPolyData* mesh, vtkIdType cellId, vtkIdType edgeId, vtkIdType lineId, const vtkIdType triPtIds[3]); // Function to add information about the new cell data void AddToNewCellMap( int inputIndex, int interPtCount, int interPts[3], vtkPolyData* interLines, int numCurrCells); // Function inside SplitCell to get the smaller triangle loops int GetLoops(vtkPolyData* pd, std::vector* loops); // Get individual polygon loop of splitting cell int GetSingleLoop(vtkPolyData* pd, simPolygon* loop, vtkIdType nextCell, std::vector& interPtBool, std::vector& lineBool); // Follow a loop orientation to iterate around a split polygon int FollowLoopOrientation(vtkPolyData* pd, simPolygon* loop, vtkIdType* nextCell, vtkIdType nextPt, vtkIdType prevPt, vtkIdList* pointCells); // Set the loop orientation based on CW CCW geometric test void SetLoopOrientation(vtkPolyData* pd, simPolygon* loop, vtkIdType* nextCell, vtkIdType nextPt, vtkIdType prevPt, vtkIdList* pointCells); // Get the loop orientation is already given int GetLoopOrientation(vtkPolyData* pd, vtkIdType cell, vtkIdType ptId1, vtkIdType ptId2); // Orient the triangle based on the transform for remeshing void Orient( vtkPolyData* pd, vtkTransform* transform, vtkPolyData* boundary, vtkPolygon* boundarypoly); // Checks to make sure multiple lines are not added to the same triangle // that needs to re-triangulated int CheckLine(vtkPolyData* pd, vtkIdType ptId1, vtkIdType ptId2); // Gets a transform to the XY plane for three points comprising a triangle int GetTransform(vtkTransform* transform, vtkPoints* points); public: vtkPolyData* Mesh[2]; vtkOBBTree* OBBTree1; // Stores the intersection lines. vtkCellArray* IntersectionLines; vtkIdTypeArray* SurfaceId; vtkIdTypeArray* NewCellIds[2]; // Cell data that indicates in which cell each intersection // lies. One array for each output surface. vtkIdTypeArray* CellIds[2]; // Cell data that indicates on which surface the intersection point lies. // Map from points to the cells that contain them. Used for point // data interpolation. For points on the edge between two cells, it // does not matter which cell is recorded because the interpolation // will be the same. One array for each output surface. vtkIdTypeArray* PointCellIds[2]; vtkIntArray* BoundaryPoints[2]; // Merging filter used to convert intersection lines from "line // soup" to connected polylines. vtkPointLocator* PointMerger; // Map from cell ID to intersection line. IntersectionMapType* IntersectionMap[2]; IntersectionMapType* IntersectionPtsMap[2]; IntersectionMapType* PointMapper; // Map from point to an edge on which it resides, the ID of the // cell, and the ID of the line. PointEdgeMapType* PointEdgeMap[2]; // vtkPolyData to hold current splitting cell. Used to double check area // of small area cells vtkPolyData* SplittingPD; int TransformSign; double Tolerance; double RelativeSubtriangleArea; // Pointer to overarching filter vtkIntersectionPolyDataFilter* ParentFilter; protected: Impl(const Impl&) = delete; void operator=(const Impl&) = delete; }; //------------------------------------------------------------------------------ vtkIntersectionPolyDataFilter::Impl::Impl() : OBBTree1(nullptr) , IntersectionLines(nullptr) , SurfaceId(nullptr) , PointMerger(nullptr) { for (int i = 0; i < 2; i++) { this->Mesh[i] = nullptr; this->CellIds[i] = nullptr; this->IntersectionMap[i] = new IntersectionMapType(); this->IntersectionPtsMap[i] = new IntersectionMapType(); this->PointEdgeMap[i] = new PointEdgeMapType(); } this->PointMapper = new IntersectionMapType(); this->SplittingPD = vtkPolyData::New(); this->TransformSign = 0; this->Tolerance = 1e-6; this->RelativeSubtriangleArea = 1e-4; } //------------------------------------------------------------------------------ vtkIntersectionPolyDataFilter::Impl::~Impl() { for (int i = 0; i < 2; i++) { delete this->IntersectionMap[i]; delete this->IntersectionPtsMap[i]; delete this->PointEdgeMap[i]; } delete this->PointMapper; this->SplittingPD->Delete(); } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl ::FindTriangleIntersections( vtkOBBNode* node0, vtkOBBNode* node1, vtkMatrix4x4* transform, void* arg) { vtkIntersectionPolyDataFilter::Impl* info = reinterpret_cast(arg); // Set up local structures to hold Impl array information vtkPolyData* mesh0 = info->Mesh[0]; vtkPolyData* mesh1 = info->Mesh[1]; vtkOBBTree* obbTree1 = info->OBBTree1; vtkCellArray* intersectionLines = info->IntersectionLines; vtkIdTypeArray* intersectionSurfaceId = info->SurfaceId; vtkIdTypeArray* intersectionCellIds0 = info->CellIds[0]; vtkIdTypeArray* intersectionCellIds1 = info->CellIds[1]; vtkPointLocator* pointMerger = info->PointMerger; double tolerance = info->Tolerance; // The number of cells in OBBTree int numCells0 = node0->Cells->GetNumberOfIds(); for (vtkIdType id0 = 0; id0 < numCells0; id0++) { vtkIdType cellId0 = node0->Cells->GetId(id0); int type0 = mesh0->GetCellType(cellId0); // Make sure the cell is a triangle if (type0 == VTK_TRIANGLE) { vtkIdType npts0; const vtkIdType* triPtIds0; mesh0->GetCellPoints(cellId0, npts0, triPtIds0); double triPts0[3][3]; for (vtkIdType id = 0; id < npts0; id++) { mesh0->GetPoint(triPtIds0[id], triPts0[id]); } if (obbTree1->TriangleIntersectsNode(node1, triPts0[0], triPts0[1], triPts0[2], transform)) { int numCells1 = node1->Cells->GetNumberOfIds(); for (vtkIdType id1 = 0; id1 < numCells1; id1++) { vtkIdType cellId1 = node1->Cells->GetId(id1); int type1 = mesh1->GetCellType(cellId1); if (type1 == VTK_TRIANGLE) { // See if the two cells actually intersect. If they do, // add an entry into the intersection maps and add an // intersection line. vtkIdType npts1; const vtkIdType* triPtIds1; mesh1->GetCellPoints(cellId1, npts1, triPtIds1); double triPts1[3][3]; for (vtkIdType id = 0; id < npts1; id++) { mesh1->GetPoint(triPtIds1[id], triPts1[id]); } int coplanar = 0; double outpt0[3], outpt1[3]; double surfaceid[2]; int intersects = vtkIntersectionPolyDataFilter::TriangleTriangleIntersection(triPts0[0], triPts0[1], triPts0[2], triPts1[0], triPts1[1], triPts1[2], coplanar, outpt0, outpt1, surfaceid, tolerance); if (coplanar) { // Coplanar triangle intersection is not handled. // This intersection will not be included in the output. TODO // vtkDebugMacro(<<"Coplanar"); intersects = 0; continue; } // If actual intersection, add point and cell to edge, line, // and surface maps! if (intersects) { vtkIdType lineId = intersectionLines->GetNumberOfCells(); vtkIdType ptId0, ptId1; int unique[2]; unique[0] = pointMerger->InsertUniquePoint(outpt0, ptId0); unique[1] = pointMerger->InsertUniquePoint(outpt1, ptId1); int addline = 1; if (ptId0 == ptId1) { addline = 0; } if (ptId0 == ptId1 && surfaceid[0] != surfaceid[1]) { intersectionSurfaceId->InsertValue(ptId0, 3); } else { if (unique[0]) { intersectionSurfaceId->InsertValue(ptId0, surfaceid[0]); } else { if (intersectionSurfaceId->GetValue(ptId0) != 3) { intersectionSurfaceId->InsertValue(ptId0, surfaceid[0]); } } if (unique[1]) { intersectionSurfaceId->InsertValue(ptId1, surfaceid[1]); } else { if (intersectionSurfaceId->GetValue(ptId1) != 3) { intersectionSurfaceId->InsertValue(ptId1, surfaceid[1]); } } } info->IntersectionPtsMap[0]->insert(std::make_pair(ptId0, cellId0)); info->IntersectionPtsMap[1]->insert(std::make_pair(ptId0, cellId1)); info->IntersectionPtsMap[0]->insert(std::make_pair(ptId1, cellId0)); info->IntersectionPtsMap[1]->insert(std::make_pair(ptId1, cellId1)); // Check to see if duplicate line. Line can only be a duplicate // line if both points are not unique and they don't // equal each other if (!unique[0] && !unique[1] && ptId0 != ptId1) { vtkSmartPointer lineTest = vtkSmartPointer::New(); lineTest->SetPoints(pointMerger->GetPoints()); lineTest->SetLines(intersectionLines); lineTest->BuildLinks(); int newLine = info->CheckLine(lineTest, ptId0, ptId1); if (newLine == 0) { addline = 0; } } if (addline) { // If the line is new and does not consist of two identical // points, add the line to the intersection and update // mapping information intersectionLines->InsertNextCell(2); intersectionLines->InsertCellPoint(ptId0); intersectionLines->InsertCellPoint(ptId1); intersectionCellIds0->InsertNextValue(cellId0); intersectionCellIds1->InsertNextValue(cellId1); info->PointCellIds[0]->InsertValue(ptId0, cellId0); info->PointCellIds[0]->InsertValue(ptId1, cellId0); info->PointCellIds[1]->InsertValue(ptId0, cellId1); info->PointCellIds[1]->InsertValue(ptId1, cellId1); info->IntersectionMap[0]->insert(std::make_pair(cellId0, lineId)); info->IntersectionMap[1]->insert(std::make_pair(cellId1, lineId)); // Check which edges of cellId0 and cellId1 outpt0 and // outpt1 are on, if any. int isOnEdge = 0; int m0p0 = 0, m0p1 = 0, m1p0 = 0, m1p1 = 0; for (vtkIdType edgeId = 0; edgeId < 3; edgeId++) { isOnEdge = info->AddToPointEdgeMap( 0, ptId0, outpt0, mesh0, cellId0, edgeId, lineId, triPtIds0); if (isOnEdge != -1) { m0p0++; } isOnEdge = info->AddToPointEdgeMap( 0, ptId1, outpt1, mesh0, cellId0, edgeId, lineId, triPtIds0); if (isOnEdge != -1) { m0p1++; } isOnEdge = info->AddToPointEdgeMap( 1, ptId0, outpt0, mesh1, cellId1, edgeId, lineId, triPtIds1); if (isOnEdge != -1) { m1p0++; } isOnEdge = info->AddToPointEdgeMap( 1, ptId1, outpt1, mesh1, cellId1, edgeId, lineId, triPtIds1); if (isOnEdge != -1) { m1p1++; } } // Special cases caught by tolerance and not from the Point // Merger if (m0p0 > 0 && m1p0 > 0) { intersectionSurfaceId->InsertValue(ptId0, 3); } if (m0p1 > 0 && m1p1 > 0) { intersectionSurfaceId->InsertValue(ptId1, 3); } } // Add information about origin surface to std::maps for // checks later if (intersectionSurfaceId->GetValue(ptId0) == 1) { info->IntersectionPtsMap[0]->insert(std::make_pair(ptId0, cellId0)); } else if (intersectionSurfaceId->GetValue(ptId0) == 2) { info->IntersectionPtsMap[1]->insert(std::make_pair(ptId0, cellId1)); } else { info->IntersectionPtsMap[0]->insert(std::make_pair(ptId0, cellId0)); info->IntersectionPtsMap[1]->insert(std::make_pair(ptId0, cellId1)); } if (intersectionSurfaceId->GetValue(ptId1) == 1) { info->IntersectionPtsMap[0]->insert(std::make_pair(ptId1, cellId0)); } else if (intersectionSurfaceId->GetValue(ptId1) == 2) { info->IntersectionPtsMap[1]->insert(std::make_pair(ptId1, cellId1)); } else { info->IntersectionPtsMap[0]->insert(std::make_pair(ptId1, cellId0)); info->IntersectionPtsMap[1]->insert(std::make_pair(ptId1, cellId1)); } } } } } } } return 1; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl ::SplitMesh( int inputIndex, vtkPolyData* output, vtkPolyData* intersectionLines) { vtkPolyData* input = this->Mesh[inputIndex]; IntersectionMapType* intersectionMap = this->IntersectionMap[inputIndex]; vtkCellData* inCD = input->GetCellData(); vtkCellData* outCD = output->GetCellData(); vtkIdType numCells = input->GetNumberOfCells(); vtkIdType cellIdX = 0; // // Process points // vtkIdType inputNumPoints = input->GetPoints()->GetNumberOfPoints(); vtkSmartPointer points = vtkSmartPointer::New(); points->Allocate(100); output->SetPoints(points); // // Split intersection lines. The lines structure is constructed // using a vtkPointLocator. However, some lines may have an endpoint // on a cell edge that has no neighbor. We need to duplicate a line // point in such a case and update the point ID in the line cell. // vtkSmartPointer splitLines = vtkSmartPointer::New(); splitLines->DeepCopy(intersectionLines); vtkPointData* inPD = input->GetPointData(); vtkPointData* outPD = output->GetPointData(); outPD->CopyAllocate(inPD, input->GetNumberOfPoints()); // Copy over the point data from the input for (vtkIdType ptId = 0; ptId < inputNumPoints; ptId++) { double pt[3]; input->GetPoints()->GetPoint(ptId, pt); output->GetPoints()->InsertNextPoint(pt); outPD->CopyData(inPD, ptId, ptId); this->BoundaryPoints[inputIndex]->InsertValue(ptId, 0); } // Copy the points from splitLines to the output, interpolating the // data as we go. for (vtkIdType id = 0; id < splitLines->GetNumberOfPoints(); id++) { double pt[3]; splitLines->GetPoint(id, pt); vtkIdType newPtId = output->GetPoints()->InsertNextPoint(pt); // Retrieve the cell ID from splitLines vtkIdType cellId = this->PointCellIds[inputIndex]->GetValue(id); double closestPt[3], pcoords[3], dist2, weights[3]; int subId; vtkCell* cell = input->GetCell(cellId); cell->EvaluatePosition(pt, closestPt, subId, pcoords, dist2, weights); outPD->InterpolatePoint(input->GetPointData(), newPtId, cell->PointIds, weights); this->BoundaryPoints[inputIndex]->InsertValue(newPtId, 0); } // // Process cells // outCD->CopyAllocate(inCD, numCells); if (input->GetPolys()->GetNumberOfCells() > 0) { vtkCellArray* cells = input->GetPolys(); vtkIdType newId = output->GetNumberOfCells(); vtkSmartPointer newPolys = vtkSmartPointer::New(); newPolys->AllocateEstimate(cells->GetNumberOfCells(), 3); output->SetPolys(newPolys); vtkSmartPointer edgeNeighbors = vtkSmartPointer::New(); vtkIdType nptsX = 0; const vtkIdType* pts = nullptr; vtkSmartPointer cellsToCheck = vtkSmartPointer::New(); for (cells->InitTraversal(); cells->GetNextCell(nptsX, pts); cellIdX++) { if (nptsX != 3) { vtkGenericWarningMacro(<< "vtkIntersectionPolyDataFilter only works" << " with triangle meshes."); continue; } cellsToCheck->Reset(); cellsToCheck->Allocate(nptsX + 1); cellsToCheck->InsertNextId(cellIdX); // Collect the cells relevant for splitting this cell. If the // cell is in the intersection map, split. If not, one of its // edges may be split by an intersection line that splits a // neighbor cell. Mark the cell as needing a split if this is // the case. bool needsSplit = intersectionMap->find(cellIdX) != intersectionMap->end(); for (vtkIdType ptId = 0; ptId < nptsX; ptId++) { vtkIdType pt0Id = pts[ptId]; vtkIdType pt1Id = pts[(ptId + 1) % nptsX]; edgeNeighbors->Reset(); input->GetCellEdgeNeighbors(cellIdX, pt0Id, pt1Id, edgeNeighbors); for (vtkIdType nbr = 0; nbr < edgeNeighbors->GetNumberOfIds(); nbr++) { vtkIdType nbrId = edgeNeighbors->GetId(nbr); cellsToCheck->InsertNextId(nbrId); if (intersectionMap->find(nbrId) != intersectionMap->end()) { needsSplit = true; } } // for (vtkIdType nbr = 0; ... } // for (vtkIdType pt = 0; ... // Splitting occurs here if (!needsSplit) { // Just insert the cell and copy the cell data newId = newPolys->InsertNextCell(3, pts); outCD->CopyData(inCD, cellIdX, newId); } else { // Total number of cells so that we know the id numbers of the new // cells added and we can add it to the new cell id mapping int numCurrCells = newPolys->GetNumberOfCells(); vtkCellArray* splitCells = this->SplitCell( input, cellIdX, pts, intersectionMap, splitLines, inputIndex, numCurrCells); if (splitCells == nullptr) { vtkDebugWithObjectMacro(this->ParentFilter, << "Error in splitting cell!"); return 0; } double pt0[3], pt1[3], pt2[3], normal[3]; points->GetPoint(pts[0], pt0); points->GetPoint(pts[1], pt1); points->GetPoint(pts[2], pt2); vtkTriangle::ComputeNormal(pt0, pt1, pt2, normal); vtkMath::Normalize(normal); vtkIdType npts; const vtkIdType* ptIds; splitCells->InitTraversal(); while (splitCells->GetNextCell(npts, ptIds)) { // Check for reversed cells. I'm not sure why, but in some // cases, cells are reversed. double subCellNormal[3]; points->GetPoint(ptIds[0], pt0); points->GetPoint(ptIds[1], pt1); points->GetPoint(ptIds[2], pt2); vtkTriangle::ComputeNormal(pt0, pt1, pt2, subCellNormal); vtkMath::Normalize(subCellNormal); if (vtkMath::Dot(normal, subCellNormal) > 0) { newId = newPolys->InsertNextCell(npts, ptIds); } else { newId = newPolys->InsertNextCell(npts); for (int i = 0; i < npts; i++) { newPolys->InsertCellPoint(ptIds[npts - i - 1]); } } outCD->CopyData(inCD, cellIdX, newId); // Duplicate cell data } splitCells->Delete(); } } // for (cells->InitTraversal(); ... } // if inputGetPolys()->GetNumberOfCells() > 1 ... return 1; } vtkCellArray* vtkIntersectionPolyDataFilter::Impl ::SplitCell(vtkPolyData* input, vtkIdType cellId, const vtkIdType* cellPts, IntersectionMapType* map, vtkPolyData* interLines, int inputIndex, int numCurrCells) { // Copy down the SurfaceID array that tells which surface the point belongs // to vtkIdTypeArray* surfaceMapper; surfaceMapper = vtkIdTypeArray::SafeDownCast(interLines->GetPointData()->GetArray("SurfaceID")); // Array to keep track of which points are on the boundary of the cell vtkSmartPointer cellBoundaryPt = vtkSmartPointer::New(); // Array to tell whether the original cell points lie on the intersecting // line int CellPointOnInterLine[3] = { 0, 0, 0 }; // Gather points from the cell vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer merger = vtkSmartPointer::New(); merger->SetTolerance(this->Tolerance); merger->InitPointInsertion(points, input->GetBounds()); double xyz[3]; for (int i = 0; i < 3; i++) { if (cellPts[i] >= input->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "invalid point read 1"); } input->GetPoint(cellPts[i], xyz); merger->InsertNextPoint(xyz); cellBoundaryPt->InsertNextValue(1); } // Set up line cells and array to track the just the intersecting lines // on the cell. vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer interceptlines = vtkSmartPointer::New(); double p0[3], p1[3], p2[3]; input->GetPoint(cellPts[0], p0); input->GetPoint(cellPts[1], p1); input->GetPoint(cellPts[2], p2); // This maps the point IDs for the vtkPolyData passed to // vtkDelaunay2D back to the original IDs in interLines. NOTE: The // point IDs from the cell are not stored here. std::map ptIdMap; IntersectionMapIteratorType iterLower = map->lower_bound(cellId); IntersectionMapIteratorType iterUpper = map->upper_bound(cellId); // Get all the lines associated with the original cell while (iterLower != iterUpper) { vtkIdType lineId = iterLower->second; vtkIdType nLinePts; const vtkIdType* linePtIds; interLines->GetLines()->GetCellAtId(lineId, nLinePts, linePtIds); interceptlines->InsertNextCell(2); lines->InsertNextCell(2); // Loop through the points of each line for (vtkIdType i = 0; i < nLinePts; i++) { std::map::iterator location = ptIdMap.find(linePtIds[i]); // If point already isn't in list if (location == ptIdMap.end()) { interLines->GetPoint(linePtIds[i], xyz); if (linePtIds[i] >= interLines->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "invalid point read 2"); } // Check to see if point is unique int unique = merger->InsertUniquePoint(xyz, ptIdMap[linePtIds[i]]); if (unique) { // If point is unique, check to see if it is actually a // point originating from this input surface or on both surfaces // Don't mark as boundary point if it originates from other surface if (surfaceMapper->GetValue(linePtIds[i]) == inputIndex + 1 || surfaceMapper->GetValue(linePtIds[i]) == 3) { cellBoundaryPt->InsertValue(ptIdMap[linePtIds[i]], 1); } else { cellBoundaryPt->InsertValue(ptIdMap[linePtIds[i]], 0); } } else { // Obviously if the pointid is less than three, it is one of the // original cell points and can be added to the inter cell point arr if (ptIdMap[linePtIds[i]] < 3) { CellPointOnInterLine[ptIdMap[linePtIds[i]]] = 1; } } interceptlines->InsertCellPoint(ptIdMap[linePtIds[i]]); lines->InsertCellPoint(ptIdMap[linePtIds[i]]); } // Point is already in list, so run through checks with its value else { interceptlines->InsertCellPoint(location->second); lines->InsertCellPoint(location->second); if (location->second < 3) { CellPointOnInterLine[location->second] = 1; } } } ++iterLower; } // Now check the neighbors of the cell IntersectionMapIteratorType ptIterLower; IntersectionMapIteratorType ptIterUpper; IntersectionMapIteratorType cellIterLower; IntersectionMapIteratorType cellIterUpper; vtkSmartPointer nbrCellIds = vtkSmartPointer::New(); for (vtkIdType i = 0; i < 3; i++) { // Get Points belonging to each edge of this cell vtkIdType edgePtId0 = cellPts[i]; vtkIdType edgePtId1 = cellPts[(i + 1) % 3]; double edgePt0[3], edgePt1[3]; if (edgePtId0 >= input->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "invalid point read 3"); } if (edgePtId1 >= input->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "invalid point read 4"); } input->GetPoint(edgePtId0, edgePt0); input->GetPoint(edgePtId1, edgePt1); nbrCellIds->Reset(); input->GetCellEdgeNeighbors(cellId, edgePtId0, edgePtId1, nbrCellIds); // Loop through attached neighbor cells and check for split edges for (vtkIdType j = 0; j < nbrCellIds->GetNumberOfIds(); j++) { vtkIdType nbrCellId = nbrCellIds->GetId(j); iterLower = map->lower_bound(nbrCellId); iterUpper = map->upper_bound(nbrCellId); while (iterLower != iterUpper) { vtkIdType lineId = iterLower->second; vtkIdType nLinePts; const vtkIdType* linePtIds; interLines->GetLines()->GetCellAtId(lineId, nLinePts, linePtIds); for (vtkIdType k = 0; k < nLinePts; k++) { if (linePtIds[k] >= interLines->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "invalid point read 5"); } interLines->GetPoint(linePtIds[k], xyz); ptIterLower = this->PointMapper->lower_bound(linePtIds[k]); ptIterUpper = this->PointMapper->upper_bound(linePtIds[k]); // Find all points within this neighbor cell while (ptIterLower != ptIterUpper) { vtkIdType mappedPtId = ptIterLower->second; cellIterLower = this->IntersectionPtsMap[inputIndex]->lower_bound(mappedPtId); cellIterUpper = this->IntersectionPtsMap[inputIndex]->upper_bound(mappedPtId); // Check all cell values associated with this point while (cellIterLower != cellIterUpper) { vtkIdType checkCellId = cellIterLower->second; // If this cell id is the same as the current cell id, this // means the point is a split edge, need to add to list!! if (checkCellId == cellId) { int unique = 0; if (ptIdMap.find(linePtIds[k]) == ptIdMap.end()) { unique = merger->InsertUniquePoint(xyz, ptIdMap[linePtIds[k]]); } else { // Point is less than 3, original cell point if (ptIdMap[linePtIds[k]] < 3) { CellPointOnInterLine[ptIdMap[linePtIds[k]]] = 1; } } if (unique) { // Check to see what surface point originates from. Don't // mark if point is from other surface if (surfaceMapper->GetValue(linePtIds[k]) == inputIndex + 1 || surfaceMapper->GetValue(linePtIds[k]) == 3) { cellBoundaryPt->InsertValue(ptIdMap[linePtIds[k]], 1); } else { cellBoundaryPt->InsertValue(ptIdMap[linePtIds[k]], 0); } } else { if (ptIdMap[linePtIds[k]] < 3) { CellPointOnInterLine[ptIdMap[linePtIds[k]]] = 1; } } } ++cellIterLower; } ++ptIterLower; } } ++iterLower; } } } // Set up reverse ID map std::map reverseIdMap; std::map reverseLineIdMap; std::map::iterator iter = ptIdMap.begin(); while (iter != ptIdMap.end()) { // If we have more than one point mapping back to the same point // in the input mesh, just use the first one. This will give a // preference for using cell points when an intersection line shares // a point with a cell and prevent introducing accidental holes // in the mesh. if (reverseIdMap.find(iter->second) == reverseIdMap.end()) { reverseIdMap[iter->second] = iter->first + input->GetNumberOfPoints(); } if (reverseLineIdMap.find(iter->second) == reverseLineIdMap.end()) { reverseLineIdMap[iter->second] = iter->first; } ++iter; } double v0[3], v1[3], n[3], c[3]; vtkTriangle::TriangleCenter(p0, p1, p2, c); vtkTriangle::ComputeNormal(p0, p1, p2, n); vtkMath::Perpendiculars(n, v0, v1, 0.0); // For each point on an edge, compute it's relative angle about n. vtkSmartPointer edgePtIdList = vtkSmartPointer::New(); vtkSmartPointer interPtIdList = vtkSmartPointer::New(); edgePtIdList->Allocate(points->GetNumberOfPoints()); vtkSmartPointer angleList = vtkSmartPointer::New(); angleList->Allocate(points->GetNumberOfPoints()); bool* interPtBool = new bool[points->GetNumberOfPoints()]; for (vtkIdType ptId = 0; ptId < points->GetNumberOfPoints(); ptId++) { double x[3]; points->GetPoint(ptId, x); interPtBool[ptId] = false; if (cellBoundaryPt->GetValue(ptId)) { // Point is on line. Add its id to id list and add its angle to // angle list. edgePtIdList->InsertNextValue(ptId); double d[3]; vtkMath::Subtract(x, c, d); angleList->InsertNextValue(atan2(vtkMath::Dot(d, v0), vtkMath::Dot(d, v1))); if (ptId > 2) { // Intersection Point! interPtIdList->InsertNextValue(ptId); interPtBool[ptId] = true; } } // Setting the boundary points if (ptId > 2) { this->BoundaryPoints[inputIndex]->InsertValue(reverseIdMap[ptId], 1); } else if (CellPointOnInterLine[ptId]) { this->BoundaryPoints[inputIndex]->InsertValue(cellPts[ptId], 1); } else { this->BoundaryPoints[inputIndex]->InsertValue(cellPts[ptId], 0); } } // Sort the edgePtIdList according to the angle list. The starting // point doesn't matter. We just need to generate boundary lines in // a consistent order. vtkSortDataArray::Sort(angleList, edgePtIdList); vtkSmartPointer checkPD = vtkSmartPointer::New(); checkPD->SetPoints(points); checkPD->SetLines(lines); checkPD->BuildLinks(); vtkIdType id; // Check to see if the lines are unique for (id = 0; id < edgePtIdList->GetNumberOfTuples() - 1; id++) { int unique = this->CheckLine(checkPD, edgePtIdList->GetValue(id), edgePtIdList->GetValue(id + 1)); if (unique) { lines->InsertNextCell(2); lines->InsertCellPoint(edgePtIdList->GetValue(id)); lines->InsertCellPoint(edgePtIdList->GetValue(id + 1)); } } int unique = this->CheckLine(checkPD, edgePtIdList->GetValue(edgePtIdList->GetNumberOfTuples() - 1), edgePtIdList->GetValue(0)); if (unique) { lines->InsertNextCell(2); lines->InsertCellPoint(edgePtIdList->GetValue(edgePtIdList->GetNumberOfTuples() - 1)); lines->InsertCellPoint(edgePtIdList->GetValue(0)); } // Set up a transform that will rotate the points to the // XY-plane (normal aligned with z-axis). vtkSmartPointer transform = vtkSmartPointer::New(); this->TransformSign = this->GetTransform(transform, points); vtkCellArray* splitCells = vtkCellArray::New(); vtkSmartPointer interpd = vtkSmartPointer::New(); interpd->SetPoints(points); interpd->SetLines(interceptlines); interpd->BuildLinks(); vtkSmartPointer fullpd = vtkSmartPointer::New(); fullpd->SetPoints(points); fullpd->SetLines(lines); SplittingPD->DeepCopy(fullpd); vtkSmartPointer transformer = vtkSmartPointer::New(); vtkSmartPointer transformedpd = vtkSmartPointer::New(); transformer->SetInputData(fullpd); transformer->SetTransform(transform); transformer->Update(); transformedpd = transformer->GetOutput(); transformedpd->BuildLinks(); // If the triangle has intersecting lines and new points if (interPtIdList->GetNumberOfTuples() > 0 && interceptlines->GetNumberOfCells() > 0) { // Get polygon loops of intersected triangle std::vector loops; if (this->GetLoops(transformedpd, &loops) != 1) { splitCells->Delete(); splitCells = nullptr; delete[] interPtBool; return splitCells; } // For each loop, orient and triangulate for (int k = 0; k < (int)loops.size(); k++) { vtkCellArray* polys; vtkSmartPointer newpd = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer newLines = vtkSmartPointer::New(); std::list::iterator it; int ptiter = 0; int* pointMapper = new int[loops[k].points.size()]; for (it = loops[k].points.begin(); it != loops[k].points.end(); ++it) { if (ptiter < (int)loops[k].points.size() - 1) { newPoints->InsertNextPoint(points->GetPoint((it)->id)); pointMapper[ptiter] = (it)->id; } if (ptiter < (int)loops[k].points.size() - 2) { newLines->InsertNextCell(2); newLines->InsertCellPoint(ptiter); newLines->InsertCellPoint(ptiter + 1); } ptiter++; } newLines->InsertNextCell(2); newLines->InsertCellPoint(ptiter - 2); newLines->InsertCellPoint(0); // Orient polygon newpd->SetPoints(newPoints); newpd->SetLines(newLines); vtkSmartPointer boundary = vtkSmartPointer::New(); vtkSmartPointer boundaryPoly = vtkSmartPointer::New(); this->Orient(newpd, transform, boundary, boundaryPoly); boundaryPoly->GetPoints()->DeepCopy(boundary->GetPoints()); vtkSmartPointer idList = vtkSmartPointer::New(); int success = boundaryPoly->BoundedTriangulate(idList, this->RelativeSubtriangleArea); vtkSmartPointer del2D = vtkSmartPointer::New(); del2D->SetContainerAlgorithm(this->ParentFilter); vtkSmartPointer triangulator = vtkSmartPointer::New(); triangulator->SetContainerAlgorithm(this->ParentFilter); vtkSmartPointer triangulatedPolyCells = vtkSmartPointer::New(); if (success) { for (vtkIdType i = 0; i < idList->GetNumberOfIds(); i += 3) { vtkIdType t[3] = { idList->GetId(i), idList->GetId(i + 1), idList->GetId(i + 2) }; triangulatedPolyCells->InsertNextCell(3, t); } polys = triangulatedPolyCells.Get(); } else { // Triangulate with delaunay2D del2D->SetInputData(newpd); del2D->SetSourceData(boundary); del2D->SetTolerance(0.0); del2D->SetAlpha(0.0); del2D->SetOffset(0); del2D->SetProjectionPlaneMode(VTK_SET_TRANSFORM_PLANE); del2D->SetTransform(transform); del2D->BoundingTriangulationOff(); del2D->Update(); polys = del2D->GetOutput()->GetPolys(); // If the number of cells output is not two minus the number of // points, the triangulation failed with 0 offset! Try again with // a higher offset. This typically resolves triangulation issues if (polys->GetNumberOfCells() != newpd->GetNumberOfPoints() - 2) { int numoffsets = 1; while ((polys->GetNumberOfCells() != newpd->GetNumberOfPoints() - 2) && numoffsets < 20) { vtkSmartPointer del2Doffset = vtkSmartPointer::New(); del2Doffset->SetInputData(newpd); del2Doffset->SetSourceData(boundary); del2Doffset->SetTolerance(0.0); del2Doffset->SetAlpha(0.0); del2Doffset->SetOffset(numoffsets); del2Doffset->SetProjectionPlaneMode(VTK_SET_TRANSFORM_PLANE); del2Doffset->SetTransform(transform); del2Doffset->BoundingTriangulationOff(); del2Doffset->Update(); polys->DeepCopy(del2Doffset->GetOutput()->GetPolys()); numoffsets++; } if (polys->GetNumberOfCells() != newpd->GetNumberOfPoints() - 2) { // If the offsets all failed, try last attempt with ear splitting triangulator->SetInputData(boundary); triangulator->Update(); polys = triangulator->GetOutput()->GetPolys(); splitCells->Delete(); splitCells = nullptr; delete[] pointMapper; delete[] interPtBool; return splitCells; } } else { polys = del2D->GetOutput()->GetPolys(); } } // Renumber the point IDs. vtkIdType npts; const vtkIdType* ptIds; interLines->BuildLinks(); for (polys->InitTraversal(); polys->GetNextCell(npts, ptIds);) { if (pointMapper[ptIds[0]] >= points->GetNumberOfPoints() || pointMapper[ptIds[1]] >= points->GetNumberOfPoints() || pointMapper[ptIds[2]] >= points->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "Invalid point ID!!!"); } splitCells->InsertNextCell(npts); int interPtCount = 0; int interPts[3]; for (int i = 0; i < npts; i++) { vtkIdType remappedPtId; if (pointMapper[ptIds[i]] < 3) // Point from the cell { remappedPtId = cellPts[pointMapper[ptIds[i]]]; // If original cell point is also on intersecting lines if (CellPointOnInterLine[pointMapper[ptIds[i]]]) { interPts[interPtCount++] = reverseLineIdMap[pointMapper[ptIds[i]]]; } } else // If point is from intersection lines { remappedPtId = reverseIdMap[pointMapper[ptIds[i]]]; interPts[interPtCount++] = reverseLineIdMap[pointMapper[ptIds[i]]]; } splitCells->InsertCellPoint(remappedPtId); } if (interPtCount >= 2) // If there are more than two, inter line { // Add the information to new cell mapping on intersection lines this->AddToNewCellMap(inputIndex, interPtCount, interPts, interLines, numCurrCells); } numCurrCells++; } delete[] pointMapper; } } else // Not (intersection lines and new points) { // Possible to have only additional point and not lines // Triangulate with delaunay2D vtkSmartPointer del2D = vtkSmartPointer::New(); del2D->SetInputData(fullpd); del2D->SetSourceData(fullpd); del2D->SetTolerance(0.0); del2D->SetAlpha(0.0); del2D->SetOffset(0); del2D->SetProjectionPlaneMode(VTK_SET_TRANSFORM_PLANE); del2D->SetTransform(transform); del2D->BoundingTriangulationOff(); del2D->Update(); vtkCellArray* polys = del2D->GetOutput()->GetPolys(); // Renumber the point IDs. vtkIdType npts; const vtkIdType* ptIds; for (polys->InitTraversal(); polys->GetNextCell(npts, ptIds);) { if (ptIds[0] >= points->GetNumberOfPoints() || ptIds[1] >= points->GetNumberOfPoints() || ptIds[2] >= points->GetNumberOfPoints()) { vtkGenericWarningMacro(<< "Invalid point ID!!!"); } splitCells->InsertNextCell(npts); int interPtCount = 0; int interPts[3]; for (int i = 0; i < npts; i++) { vtkIdType remappedPtId; if (ptIds[i] < 3) // Point from the cell { remappedPtId = cellPts[ptIds[i]]; if (CellPointOnInterLine[ptIds[i]]) { interPts[interPtCount++] = reverseLineIdMap[ptIds[i]]; } } else { remappedPtId = reverseIdMap[ptIds[i]]; interPts[interPtCount++] = reverseLineIdMap[ptIds[i]]; } splitCells->InsertCellPoint(remappedPtId); } if (interPtCount >= 2) { this->AddToNewCellMap(inputIndex, interPtCount, interPts, interLines, numCurrCells); } numCurrCells++; } } delete[] interPtBool; return splitCells; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl ::AddToPointEdgeMap(int index, vtkIdType ptId, double x[3], vtkPolyData* mesh, vtkIdType cellId, vtkIdType edgeId, vtkIdType lineId, const vtkIdType triPtIds[3]) { int value = -1; vtkIdType edgePtId0 = triPtIds[edgeId]; vtkIdType edgePtId1 = triPtIds[(edgeId + 1) % 3]; double pt0[3], pt1[3]; mesh->GetPoint(edgePtId0, pt0); mesh->GetPoint(edgePtId1, pt1); // Check to see if this point-cell combo is already in the list PointEdgeMapIteratorType iterLower = this->PointEdgeMap[index]->lower_bound(ptId); PointEdgeMapIteratorType iterUpper = this->PointEdgeMap[index]->upper_bound(ptId); while (iterLower != iterUpper) { if (iterLower->second.CellId == cellId) { return iterLower->second.EdgeId; } ++iterLower; } double t, dist, closestPt[3]; dist = vtkLine::DistanceToLine(x, pt0, pt1, t, closestPt); if (fabs(dist) < pow(this->Tolerance, 3) && t >= 0.0 && t <= 1.0) { CellEdgeLineType cellEdgeLine; cellEdgeLine.CellId = cellId; cellEdgeLine.EdgeId = edgeId; cellEdgeLine.LineId = lineId; this->PointEdgeMap[index]->insert(std::make_pair(ptId, cellEdgeLine)); value = edgeId; } return value; } //------------------------------------------------------------------------------ // Add new cells to the mapping data array attached to the intersection lines void vtkIntersectionPolyDataFilter::Impl::AddToNewCellMap( int inputIndex, int interPtCount, int interPts[3], vtkPolyData* interLines, int numCurrCells) { vtkIdList** cellIds; cellIds = new vtkIdList*[interPtCount]; for (int i = 0; i < interPtCount; i++) { cellIds[i] = vtkIdList::New(); vtkSmartPointer temp = vtkSmartPointer::New(); interLines->GetPointCells(interPts[i], cellIds[i]); if (i > 0) { temp->DeepCopy(cellIds[i - 1]); temp->IntersectWith(cellIds[i]); } if (temp->GetNumberOfIds() > 0) { // For each id for (int j = 0; j < temp->GetNumberOfIds(); j++) { // If it hasn't already been set if (this->NewCellIds[inputIndex]->GetComponent(temp->GetId(j), 0) == -1) { // Add to new cell mapping data array on intersection lines this->NewCellIds[inputIndex]->InsertComponent(temp->GetId(j), 0, numCurrCells); } else { // Add to new cell mapping data array on intersection lines this->NewCellIds[inputIndex]->InsertComponent(temp->GetId(j), 1, numCurrCells); } } } } // If number of intersection points is more than two, intersection line if (interPtCount > 2) { cellIds[0]->IntersectWith(cellIds[interPtCount - 1]); if (cellIds[0]->GetNumberOfIds() > 0) { for (int j = 0; j < cellIds[0]->GetNumberOfIds(); j++) { if (this->NewCellIds[inputIndex]->GetComponent(cellIds[0]->GetId(j), 0) == -1) { // Add to new cell mapping data array on intersection lines this->NewCellIds[inputIndex]->InsertComponent(cellIds[0]->GetId(j), 0, numCurrCells); } else { // Add to new cell mapping data array on intersection lines this->NewCellIds[inputIndex]->InsertComponent(cellIds[0]->GetId(j), 1, numCurrCells); } } } } for (int i = 0; i < interPtCount; i++) { cellIds[i]->Delete(); } delete[] cellIds; } int vtkIntersectionPolyDataFilter::Impl ::GetLoops(vtkPolyData* pd, std::vector* loops) { vtkSmartPointer pointCells = vtkSmartPointer::New(); vtkSmartPointer cellPoints = vtkSmartPointer::New(); simPoint nextPt; vtkIdType nextCell; int numPoints = pd->GetNumberOfPoints(); int numCells = pd->GetNumberOfCells(); std::vector ptBool(numPoints, false); // Add one for the cell that could be added in GetSingleLoop std::vector lineBool(numCells + 1, false); // For each point in triangle and additional lines for (vtkIdType ptId = 0; ptId < numPoints; ptId++) { // if the point hasn't already been touch and put in a loop if (!ptBool[ptId]) { nextPt.id = ptId; pd->GetPoint(nextPt.id, nextPt.pt); simPolygon interloop; interloop.points.push_back(nextPt); ptBool[nextPt.id] = true; pd->GetPointCells(nextPt.id, pointCells); nextCell = pointCells->GetId(0); lineBool[nextCell] = true; // Get one loop for untouched point if (this->GetSingleLoop(pd, &interloop, nextCell, ptBool, lineBool) != 1) { return 0; } // Add new loop loops->push_back(interloop); } } // Check now for untouched lines, possible to still have for (vtkIdType lineId = 0; lineId < pd->GetNumberOfCells(); lineId++) { if (!lineBool[lineId]) { vtkDebugWithObjectMacro(this->ParentFilter, << "LINE FALSE: Find extra loop/s"); pd->GetCellPoints(lineId, cellPoints); nextPt.id = cellPoints->GetId(0); pd->GetPoint(nextPt.id, nextPt.pt); simPolygon interloop; interloop.points.push_back(nextPt); lineBool[lineId] = true; nextCell = lineId; // Get single loop if the line is still untouched if (this->GetSingleLoop(pd, &interloop, nextCell, ptBool, lineBool) != 1) { return 0; } // Add new loop to loops loops->push_back(interloop); } } return 1; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl ::GetSingleLoop(vtkPolyData* pd, simPolygon* loop, vtkIdType nextCell, std::vector& interPtBool, std::vector& lineBool) { int intertype = 0; vtkSmartPointer pointCells = vtkSmartPointer::New(); vtkSmartPointer cellPoints = vtkSmartPointer::New(); // Set up next and next cell values vtkIdType nextPt = loop->points.front().id; vtkIdType startPt = nextPt; interPtBool[nextPt] = true; pd->GetCellPoints(nextCell, cellPoints); simPoint newpoint; vtkIdType prevPt = nextPt; // Find next point by following line and choosing point that is not already // being used if (cellPoints->GetId(0) == nextPt) { newpoint.id = cellPoints->GetId(1); nextPt = cellPoints->GetId(1); } else { newpoint.id = cellPoints->GetId(0); nextPt = cellPoints->GetId(0); } pd->GetPoint(newpoint.id, newpoint.pt); loop->points.push_back(newpoint); interPtBool[nextPt] = true; // Loop until we get back to the point we started at, completing the loop! while (nextPt != startPt) { pd->GetPointCells(nextPt, pointCells); // There are multiple lines attached to this point; must figure out // the correct way to go if (pointCells->GetNumberOfIds() > 2) { // This is the first intersection. Find line of minimum angle and // set the orientation of the loop (i.e. CW or CCW) if (intertype == 0) { this->SetLoopOrientation(pd, loop, &nextCell, nextPt, prevPt, pointCells); intertype = 1; } // This is not the first intersection. Follow line that continues along // the set loop orientation else { if (this->FollowLoopOrientation(pd, loop, &nextCell, nextPt, prevPt, pointCells) != 1) { return 0; } } } // There is one line attached to point. This means the intersection has // an open intersection loop (i.e. the surfaces are open and one does not // completeley intersect the other. // Make an artificial triangle loop in this case else if (pointCells->GetNumberOfIds() < 2) { vtkSmartPointer currentpd = vtkSmartPointer::New(); vtkSmartPointer currentcells = vtkSmartPointer::New(); currentcells = pd->GetLines(); currentcells->InsertNextCell(2); currentcells->InsertCellPoint(nextPt); currentcells->InsertCellPoint(startPt); nextCell = currentcells->GetNumberOfCells() - 1; currentpd->SetLines(currentcells); currentpd->SetPoints(pd->GetPoints()); pd->DeepCopy(currentpd); pd->BuildLinks(); } // Normal number of lines, simply follow around triangle loop else { if (pointCells->GetId(0) == nextCell) { nextCell = pointCells->GetId(1); } else { nextCell = pointCells->GetId(0); } } lineBool[nextCell] = true; prevPt = nextPt; pd->GetCellPoints(nextCell, cellPoints); simPoint internewpoint; if (cellPoints->GetId(0) == nextPt) { internewpoint.id = cellPoints->GetId(1); nextPt = cellPoints->GetId(1); } else { internewpoint.id = cellPoints->GetId(0); nextPt = cellPoints->GetId(0); } pd->GetPoint(internewpoint.id, internewpoint.pt); loop->points.push_back(internewpoint); interPtBool[nextPt] = true; } // Cell is boring; i.e. it only has boundary points. set the orientation if (intertype == 0) { nextPt = 0; pd->GetPointCells(nextPt, pointCells); nextCell = pointCells->GetId(0); pd->GetCellPoints(pointCells->GetId(1), cellPoints); if (cellPoints->GetId(0) == nextPt) { prevPt = cellPoints->GetId(1); } else { prevPt = cellPoints->GetId(0); } loop->orientation = this->GetLoopOrientation(pd, nextCell, prevPt, nextPt); } return 1; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl ::FollowLoopOrientation(vtkPolyData* pd, simPolygon* loop, vtkIdType* nextCell, vtkIdType nextPt, vtkIdType prevPt, vtkIdList* pointCells) { // Follow the orientation of this loop int foundcell = 0; double newcell = 0; double minangle = VTK_DOUBLE_MAX; for (vtkIdType i = 0; i < pointCells->GetNumberOfIds(); i++) { vtkIdType cellId = pointCells->GetId(i); if (*nextCell != cellId) { // Get orientation for newly selected line int neworient = this->GetLoopOrientation(pd, cellId, prevPt, nextPt); // If the orientation of the newly selected line is correct, check // the angle of this it will make with the previous line if (neworient == loop->orientation) { foundcell = 1; double l0pt0[3], l0pt1[3], l1pt0[3], l1pt1[3]; pd->GetPoint(prevPt, l0pt0); pd->GetPoint(nextPt, l0pt1); vtkSmartPointer specialCellPoints = vtkSmartPointer::New(); pd->GetCellPoints(cellId, specialCellPoints); if (specialCellPoints->GetId(0) == nextPt) { pd->GetPoint(specialCellPoints->GetId(1), l1pt0); pd->GetPoint(specialCellPoints->GetId(0), l1pt1); } else { pd->GetPoint(specialCellPoints->GetId(0), l1pt0); pd->GetPoint(specialCellPoints->GetId(1), l1pt1); } double edge1[3], edge2[3]; for (int j = 0; j < 2; j++) { edge1[j] = l0pt1[j] - l0pt0[j]; edge2[j] = l1pt1[j] - l1pt0[j]; } edge1[2] = 0.; edge2[2] = 0.; vtkMath::Normalize(edge1); vtkMath::Normalize(edge2); double dot = vtkMath::Dot(edge1, edge2); if (dot > 1.0) { dot = 1.0; } else if (dot < -1.0) { dot = -1.0; } double angle = vtkMath::DegreesFromRadians(acos(dot)); if (angle < minangle) { minangle = angle; newcell = cellId; } } } } if (foundcell == 0) { vtkWarningWithObjectMacro(this->ParentFilter, << "No cell with correct orientation found"); return 0; } // Set the next line to follow equal to the line that follows the // orientation of the loop and has the minimum angle. Angle check is // necessary because it is possible to have more than one line that follow // the loop orientation *nextCell = newcell; return 1; } //------------------------------------------------------------------------------ void vtkIntersectionPolyDataFilter::Impl ::SetLoopOrientation(vtkPolyData* pd, simPolygon* loop, vtkIdType* nextCell, vtkIdType nextPt, vtkIdType prevPt, vtkIdList* pointCells) { // Set the orientation of this loop! double mincell = 0; double minangle = VTK_DOUBLE_MAX; for (vtkIdType i = 0; i < pointCells->GetNumberOfIds(); i++) { vtkIdType cellId = pointCells->GetId(i); // If the next line is not equal to the current line, check the angle // it makes with the previous line if (*nextCell != cellId) { double l0pt0[3], l0pt1[3], l1pt0[3], l1pt1[3]; pd->GetPoint(prevPt, l0pt0); pd->GetPoint(nextPt, l0pt1); vtkSmartPointer specialCellPoints = vtkSmartPointer::New(); pd->GetCellPoints(cellId, specialCellPoints); if (specialCellPoints->GetId(0) == nextPt) { pd->GetPoint(specialCellPoints->GetId(1), l1pt0); pd->GetPoint(specialCellPoints->GetId(0), l1pt1); } else { pd->GetPoint(specialCellPoints->GetId(0), l1pt0); pd->GetPoint(specialCellPoints->GetId(1), l1pt1); } double edge1[3], edge2[3]; for (int j = 0; j < 2; j++) { edge1[j] = l0pt1[j] - l0pt0[j]; edge2[j] = l1pt1[j] - l1pt0[j]; } edge1[2] = 0.; edge2[2] = 0.; vtkMath::Normalize(edge1); vtkMath::Normalize(edge2); double dot = vtkMath::Dot(edge1, edge2); if (dot > 1.0) { dot = 1.0; } else if (dot < -1.0) { dot = -1.0; } double angle = vtkMath::DegreesFromRadians(acos(dot)); if (angle < minangle) { minangle = angle; mincell = cellId; } } } // Set the next line as the line that makes the minimum angle with the // previous cell and set the orientation of the loop *nextCell = mincell; loop->orientation = this->GetLoopOrientation(pd, *nextCell, prevPt, nextPt); } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::Impl::GetLoopOrientation( vtkPolyData* pd, vtkIdType cell, vtkIdType ptId1, vtkIdType ptId2) { // Calculate the actual orientation of this loop, by calculating the signed // area of the triangle made by the three points vtkSmartPointer cellPoints = vtkSmartPointer::New(); pd->GetCellPoints(cell, cellPoints); vtkIdType ptId3; if (cellPoints->GetId(0) == ptId2) { ptId3 = cellPoints->GetId(1); } else { ptId3 = cellPoints->GetId(0); } double pt1[3], pt2[3], pt3[3]; pd->GetPoint(ptId1, pt1); pd->GetPoint(ptId2, pt2); pd->GetPoint(ptId3, pt3); double area = 0; area = area + (pt1[0] * pt2[1]) - (pt2[0] * pt1[1]); area = area + (pt2[0] * pt3[1]) - (pt3[0] * pt2[1]); area = area + (pt3[0] * pt1[1]) - (pt1[0] * pt3[1]); int orientation = 1; if (fabs(area) < 1e-10) { // The area is very small for these three based upon the transformed pd // from the cells original three points. Get a new transform from these // interior three points to make sure the area is correct vtkDebugWithObjectMacro(this->ParentFilter, << "Very Small Area Triangle"); vtkDebugWithObjectMacro( this->ParentFilter, << "Double check area with more accurate transform"); vtkSmartPointer testPoints = vtkSmartPointer::New(); vtkSmartPointer testPD = vtkSmartPointer::New(); vtkSmartPointer testCells = vtkSmartPointer::New(); testPoints->InsertNextPoint(this->SplittingPD->GetPoint(ptId1)); testPoints->InsertNextPoint(this->SplittingPD->GetPoint(ptId2)); testPoints->InsertNextPoint(this->SplittingPD->GetPoint(ptId3)); for (int i = 0; i < 3; i++) { testCells->InsertNextCell(2); testCells->InsertCellPoint(i); testCells->InsertCellPoint((i + 1) % 3); } testPD->SetPoints(testPoints); testPD->SetLines(testCells); testPD->BuildLinks(); vtkSmartPointer newTransform = vtkSmartPointer::New(); int sign = this->GetTransform(newTransform, testPoints); if (sign != this->TransformSign) { testPoints->SetPoint(0, this->SplittingPD->GetPoint(ptId2)); testPoints->SetPoint(1, this->SplittingPD->GetPoint(ptId1)); this->GetTransform(newTransform, testPoints); testPoints->SetPoint(0, this->SplittingPD->GetPoint(ptId1)); testPoints->SetPoint(1, this->SplittingPD->GetPoint(ptId2)); } vtkSmartPointer newTransformer = vtkSmartPointer::New(); newTransformer->SetInputData(testPD); newTransformer->SetTransform(newTransform); newTransformer->Update(); newTransformer->GetOutput()->GetPoint(0, pt1); newTransformer->GetOutput()->GetPoint(1, pt2); newTransformer->GetOutput()->GetPoint(2, pt3); vtkDebugWithObjectMacro(this->ParentFilter, << "Area was: " << area); area = 0; area = area + (pt1[0] * pt2[1]) - (pt2[0] * pt1[1]); area = area + (pt2[0] * pt3[1]) - (pt3[0] * pt2[1]); area = area + (pt3[0] * pt1[1]) - (pt1[0] * pt3[1]); vtkDebugWithObjectMacro(this->ParentFilter, << "Corrected area is: " << area); } if (area < 0) { orientation = -1; } return orientation; } //------------------------------------------------------------------------------ void vtkIntersectionPolyDataFilter::Impl ::Orient( vtkPolyData* pd, vtkTransform* transform, vtkPolyData* boundary, vtkPolygon* boundarypoly) { // Orient this loop in a counter clockwise direction in preparation for // cell splitting. For delaunay2d, the polygon should be in CCW order, but // also for ear clipping method, it is nice to have also in CCW order. vtkSmartPointer transformer = vtkSmartPointer::New(); vtkSmartPointer transformedpd = vtkSmartPointer::New(); transformer->SetInputData(pd); transformer->SetTransform(transform); transformer->Update(); transformedpd = transformer->GetOutput(); double area = 0; double tedgept1[3]; double tedgept2[3]; vtkIdType nextPt; for (nextPt = 0; nextPt < pd->GetNumberOfPoints() - 1; nextPt++) { transformedpd->GetPoint(nextPt, tedgept1); transformedpd->GetPoint(nextPt + 1, tedgept2); area = area + (tedgept1[0] * tedgept2[1]) - (tedgept2[0] * tedgept1[1]); } transformedpd->GetPoint(nextPt, tedgept1); transformedpd->GetPoint(0, tedgept2); area = area + (tedgept1[0] * tedgept2[1]) - (tedgept2[0] * tedgept1[1]); if (area < 0) { for (nextPt = pd->GetNumberOfPoints() - 1; nextPt > -1; nextPt--) { boundarypoly->GetPointIds()->InsertNextId(nextPt); } } else { for (nextPt = 0; nextPt < pd->GetNumberOfPoints(); nextPt++) { boundarypoly->GetPointIds()->InsertNextId(nextPt); } } vtkSmartPointer cellarray = vtkSmartPointer::New(); cellarray->InsertNextCell(boundarypoly); boundary->SetPoints(pd->GetPoints()); boundary->SetPolys(cellarray); } //------------------------------------------------------------------------------ // Check to make sure the line is unique int vtkIntersectionPolyDataFilter::Impl::CheckLine( vtkPolyData* pd, vtkIdType ptId1, vtkIdType ptId2) { vtkSmartPointer pointCells1 = vtkSmartPointer::New(); vtkSmartPointer pointCells2 = vtkSmartPointer::New(); pd->GetPointCells(ptId1, pointCells1); pd->GetPointCells(ptId2, pointCells2); pointCells1->IntersectWith(pointCells2); int unique = 1; if (pointCells1->GetNumberOfIds() > 0) { unique = 0; } return unique; } int vtkIntersectionPolyDataFilter::Impl::GetTransform(vtkTransform* transform, vtkPoints* points) { double zaxis[3] = { 0, 0, 1 }; double rotationAxis[3], normal[3], center[3], rotationAngle; double pt0[3], pt1[3], pt2[3]; points->GetPoint(0, pt0); points->GetPoint(1, pt1); points->GetPoint(2, pt2); vtkTriangle::ComputeNormal(pt0, pt1, pt2, normal); double dotZAxis = vtkMath::Dot(normal, zaxis); if (fabs(1.0 - dotZAxis) < 1e-6) { // Aligned with z-axis rotationAxis[0] = 1.0; rotationAxis[1] = 0.0; rotationAxis[2] = 0.0; rotationAngle = 0.0; } else if (fabs(1.0 + dotZAxis) < 1e-6) { // Co-linear with z-axis, but reversed sense. // Aligned with z-axis rotationAxis[0] = 1.0; rotationAxis[1] = 0.0; rotationAxis[2] = 0.0; rotationAngle = 180.0; } else { // The general case vtkMath::Cross(normal, zaxis, rotationAxis); vtkMath::Normalize(rotationAxis); double dot = vtkMath::Dot(zaxis, normal); if (dot > 1.0) { dot = 1.0; } else if (dot < -1.0) { dot = -1.0; } rotationAngle = vtkMath::DegreesFromRadians(acos(dot)); } transform->PreMultiply(); transform->Identity(); transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]); vtkTriangle::TriangleCenter(pt0, pt1, pt2, center); transform->Translate(-center[0], -center[1], -center[2]); int zaxisdotsign = 1; if (dotZAxis < 0) { zaxisdotsign = -1; } return zaxisdotsign; } //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkIntersectionPolyDataFilter); //------------------------------------------------------------------------------ vtkIntersectionPolyDataFilter::vtkIntersectionPolyDataFilter() : SplitFirstOutput(1) , SplitSecondOutput(1) { this->SetNumberOfInputPorts(2); this->SetNumberOfOutputPorts(3); this->NumberOfIntersectionPoints = 0; this->NumberOfIntersectionLines = 0; this->CheckMesh = 1; this->CheckInput = 0; this->Status = 1; this->ComputeIntersectionPointArray = 0; this->Tolerance = 1e-6; this->RelativeSubtriangleArea = 1e-4; } //------------------------------------------------------------------------------ vtkIntersectionPolyDataFilter::~vtkIntersectionPolyDataFilter() = default; //------------------------------------------------------------------------------ void vtkIntersectionPolyDataFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "NumberOfIntersectionPoints: " << this->NumberOfIntersectionPoints << "\n"; os << indent << "NumberOfIntersectionLines: " << this->NumberOfIntersectionLines << "\n"; os << indent << "SplitFirstOutput: " << this->SplitFirstOutput << "\n"; os << indent << "SplitSecondOutput: " << this->SplitSecondOutput << "\n"; os << indent << "CheckMesh: " << this->CheckMesh << "\n"; os << indent << "Status: " << this->CheckMesh << "\n"; os << indent << "ComputeIntersectionPointArray: " << this->ComputeIntersectionPointArray << "\n"; os << indent << "Tolerance: " << this->Tolerance << "\n"; os << indent << "RelativeSubtriangleArea: " << this->RelativeSubtriangleArea << "\n"; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::TriangleTriangleIntersection(double p1[3], double q1[3], double r1[3], double p2[3], double q2[3], double r2[3], int& coplanar, double pt1[3], double pt2[3], double surfaceid[2], double tolerance) { double n1[3], n2[3]; // Compute supporting plane normals. vtkTriangle::ComputeNormal(p1, q1, r1, n1); vtkTriangle::ComputeNormal(p2, q2, r2, n2); double s1 = -vtkMath::Dot(n1, p1); double s2 = -vtkMath::Dot(n2, p2); // Compute signed distances of points p1, q1, r1 from supporting // plane of second triangle. double dist1[3]; dist1[0] = vtkMath::Dot(n2, p1) + s2; dist1[1] = vtkMath::Dot(n2, q1) + s2; dist1[2] = vtkMath::Dot(n2, r1) + s2; // If signs of all points are the same, all the points lie on the // same side of the supporting plane, and we can exit early. if ((dist1[0] * dist1[1] > tolerance) && (dist1[0] * dist1[2] > tolerance)) { // vtkDebugMacro(<<"Same side supporting plane 1!"); return 0; } // Do the same for p2, q2, r2 and supporting plane of first // triangle. double dist2[3]; dist2[0] = vtkMath::Dot(n1, p2) + s1; dist2[1] = vtkMath::Dot(n1, q2) + s1; dist2[2] = vtkMath::Dot(n1, r2) + s1; // If signs of all points are the same, all the points lie on the // same side of the supporting plane, and we can exit early. if ((dist2[0] * dist2[1] > tolerance) && (dist2[0] * dist2[2] > tolerance)) { // vtkDebugMacro(<<"Same side supporting plane 2!"); return 0; } // Check for coplanarity of the supporting planes. if (fabs(n1[0] - n2[0]) < 1e-9 && fabs(n1[1] - n2[1]) < 1e-9 && fabs(n1[2] - n2[2]) < 1e-9 && fabs(s1 - s2) < 1e-9) { coplanar = 1; // vtkDebugMacro(<<"Coplanar!"); return 0; } coplanar = 0; // There are more efficient ways to find the intersection line (if // it exists), but this is clear enough. double *pts1[3] = { p1, q1, r1 }, *pts2[3] = { p2, q2, r2 }; // Find line of intersection (L = p + t*v) between two planes. double n1n2 = vtkMath::Dot(n1, n2); double a = (s1 - s2 * n1n2) / (n1n2 * n1n2 - 1.0); double b = (s2 - s1 * n1n2) / (n1n2 * n1n2 - 1.0); double p[3], v[3]; p[0] = a * n1[0] + b * n2[0]; p[1] = a * n1[1] + b * n2[1]; p[2] = a * n1[2] + b * n2[2]; vtkMath::Cross(n1, n2, v); vtkMath::Normalize(v); int index1 = 0, index2 = 0; double t1[3], t2[3]; int ts1 = 50, ts2 = 50; for (int i = 0; i < 3; i++) { double t, x[3]; int id1 = i, id2 = (i + 1) % 3; // Find t coordinate on line of intersection between two planes. double val1 = vtkPlane::IntersectWithLine(pts1[id1], pts1[id2], n2, p2, t, x); if (val1 == 1 || (t > (0 - tolerance) && t < (1 + tolerance))) { if (t < 1 + tolerance && t > 1 - tolerance) { ts1 = index1; } t1[index1++] = vtkMath::Dot(x, v) - vtkMath::Dot(p, v); } double val2 = vtkPlane::IntersectWithLine(pts2[id1], pts2[id2], n1, p1, t, x); if (val2 == 1 || (t > (0 - tolerance) && t < (1 + tolerance))) { if (t < 1 + tolerance && t > 1 - tolerance) { ts2 = index2; } t2[index2++] = vtkMath::Dot(x, v) - vtkMath::Dot(p, v); } } // If the value of the index is greater than 2, the intersecting point // actually is intersected by all three edges. In this case, set the two // edges to the two edges where the intersecting point is not the end point if (index1 > 2) { index1--; std::swap(t1[ts1], t1[2]); } if (index2 > 2) { index2--; std::swap(t2[ts2], t2[2]); } // Check if only one edge or all edges intersect the supporting // planes intersection. if (index1 != 2 || index2 != 2) { // vtkDebugMacro(<<"Only one edge intersecting!"); return 0; } // Check for NaNs if (vtkMath::IsNan(t1[0]) || vtkMath::IsNan(t1[1]) || vtkMath::IsNan(t2[0]) || vtkMath::IsNan(t2[1])) { // vtkWarningMacro(<<"NaNs!"); return 0; } if (t1[0] > t1[1]) { std::swap(t1[0], t1[1]); } if (t2[0] > t2[1]) { std::swap(t2[0], t2[1]); } // Handle the different interval configuration cases. double tt1, tt2; if (t1[1] < t2[0] || t2[1] < t1[0]) { // vtkDebugMacro(<<"No Overlap!"); return 0; // No overlap } else if (t1[0] < t2[0]) { if (t1[1] < t2[1]) { // First point on surface 2, second point on surface 1 surfaceid[0] = 2; surfaceid[1] = 1; tt1 = t2[0]; tt2 = t1[1]; } else { // Both points belong to lines on surface 2 surfaceid[0] = 2; surfaceid[1] = 2; tt1 = t2[0]; tt2 = t2[1]; } } else // t1[0] >= t2[0] { if (t1[1] < t2[1]) { // Both points belong to lines on surface 1 surfaceid[0] = 1; surfaceid[1] = 1; tt1 = t1[0]; tt2 = t1[1]; } else { // First point on surface 1, second point on surface 2 surfaceid[0] = 1; surfaceid[1] = 2; tt1 = t1[0]; tt2 = t2[1]; } } // Create actual intersection points. pt1[0] = p[0] + tt1 * v[0]; pt1[1] = p[1] + tt1 * v[1]; pt1[2] = p[2] + tt1 * v[2]; pt2[0] = p[0] + tt2 * v[0]; pt2[1] = p[1] + tt2 * v[1]; pt2[2] = p[2] + tt2 * v[2]; return 1; } void vtkIntersectionPolyDataFilter::CleanAndCheckSurface( vtkPolyData* pd, double stats[2], double tolerance) { int badEdges = 0; int freeEdges = 0; vtkSmartPointer cleaner = vtkSmartPointer::New(); vtkSmartPointer bad = vtkSmartPointer::New(); vtkSmartPointer freeedge = vtkSmartPointer::New(); vtkSmartPointer edgeneighbors = vtkSmartPointer::New(); // Clean the input surface cleaner->SetInputData(pd); cleaner->ToleranceIsAbsoluteOn(); cleaner->SetAbsoluteTolerance(tolerance); cleaner->Update(); pd->DeepCopy(cleaner->GetOutput()); pd->BuildLinks(); // Loop through the surface and find edges with cells that have either more // than one neighbor or no neighbors. No neighbors can be okay,as this can // indicate a free edge. However, for a polydata surface, multiple neighbors // indicates a bad cell with possible intersecting facets! for (int i = 0; i < pd->GetNumberOfCells(); i++) { const vtkIdType* pts = nullptr; vtkIdType npts = 0; pd->GetCellPoints(i, npts, pts); int badcell = 0; int freeedgecell = 0; for (int j = 0; j < npts; j++) { vtkIdType p0 = pts[j]; vtkIdType p1 = pts[(j + 1) % npts]; pd->GetCellEdgeNeighbors(i, p0, p1, edgeneighbors); if (edgeneighbors->GetNumberOfIds() > 1) { badEdges++; badcell++; } else if (edgeneighbors->GetNumberOfIds() < 1) { freeEdges++; freeedgecell++; } } bad->InsertValue(i, badcell); freeedge->InsertValue(i, freeedgecell); } bad->SetName("BadTriangle"); pd->GetCellData()->AddArray(bad); pd->GetCellData()->SetActiveScalars("BadTriangle"); freeedge->SetName("FreeEdge"); pd->GetCellData()->AddArray(freeedge); pd->GetCellData()->SetActiveScalars("FreeEdge"); stats[0] = freeEdges; stats[1] = badEdges; } void vtkIntersectionPolyDataFilter::CleanAndCheckInput(vtkPolyData* pd, double tolerance) { vtkSmartPointer cleaner = vtkSmartPointer::New(); vtkSmartPointer triangulator = vtkSmartPointer::New(); vtkSmartPointer normaler = vtkSmartPointer::New(); // vtkDebugMacro(<<"Cleaning"); cleaner->SetInputData(pd); cleaner->ToleranceIsAbsoluteOn(); cleaner->SetAbsoluteTolerance(tolerance); cleaner->Update(); // vtkDebugMacro(<<"Triangulating"); triangulator->SetInputData(cleaner->GetOutput()); triangulator->Update(); // vtkDebugMacro(<<"Getting Normals"); normaler->SetInputData(triangulator->GetOutput()); normaler->AutoOrientNormalsOn(); normaler->SplittingOff(); normaler->ComputeCellNormalsOn(); normaler->Update(); const vtkIdType* cellPts; vtkIdType npts; double pt0[3], pt1[3], pt2[3]; normaler->GetOutput()->GetPolys()->GetCellAtId(0, npts, cellPts); normaler->GetOutput()->GetPoints()->GetPoint(cellPts[0], pt0); normaler->GetOutput()->GetPoints()->GetPoint(cellPts[1], pt1); normaler->GetOutput()->GetPoints()->GetPoint(cellPts[2], pt2); double v1[3], v2[3], cellNorm[3]; for (int i = 0; i < 3; i++) { v1[i] = pt1[i] - pt0[i]; v2[i] = pt2[i] - pt1[i]; } vtkMath::Cross(v1, v2, cellNorm); double arrayNormal[3]; // vtkDebugMacro(<<"Getting Normal Array"); normaler->GetOutput()->GetCellData()->GetNormals("Normals")->GetTuple(0, arrayNormal); } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo0 = inputVector[0]->GetInformationObject(0); vtkInformation* inInfo1 = inputVector[1]->GetInformationObject(0); vtkInformation* outIntersectionInfo = outputVector->GetInformationObject(0); vtkInformation* outPolyDataInfo0 = outputVector->GetInformationObject(1); vtkInformation* outPolyDataInfo1 = outputVector->GetInformationObject(2); vtkPolyData* input0 = vtkPolyData::SafeDownCast(inInfo0->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* input1 = vtkPolyData::SafeDownCast(inInfo1->Get(vtkDataObject::DATA_OBJECT())); if (input0 == nullptr || input1 == nullptr || input0->GetNumberOfPoints() == 0 || input1->GetNumberOfPoints() == 0 || input0->GetNumberOfCells() == 0 || input1->GetNumberOfCells() == 0) { vtkDebugMacro(<< "Empty input data"); return 1; } if (this->CheckInput) { vtkDebugMacro(<< "Checking Input 0"); this->CleanAndCheckInput(input0, this->Tolerance); vtkDebugMacro(<< "Checking Input 1"); this->CleanAndCheckInput(input1, this->Tolerance); } vtkPolyData* outputIntersection = vtkPolyData::SafeDownCast(outIntersectionInfo->Get(vtkDataObject::DATA_OBJECT())); vtkSmartPointer outputIntersectionPoints = vtkSmartPointer::New(); outputIntersection->SetPoints(outputIntersectionPoints); vtkPolyData* outputPolyData0 = vtkPolyData::SafeDownCast(outPolyDataInfo0->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* outputPolyData1 = vtkPolyData::SafeDownCast(outPolyDataInfo1->Get(vtkDataObject::DATA_OBJECT())); // Set up new poly data for the inputs to build cells and links. vtkSmartPointer mesh0 = vtkSmartPointer::New(); mesh0->DeepCopy(input0); vtkSmartPointer mesh1 = vtkSmartPointer::New(); mesh1->DeepCopy(input1); // Find the triangle-triangle intersections between mesh0 and mesh1 vtkSmartPointer obbTree0 = vtkSmartPointer::New(); obbTree0->SetDataSet(mesh0); obbTree0->SetNumberOfCellsPerNode(10); obbTree0->SetMaxLevel(1000000); obbTree0->SetTolerance(this->Tolerance); obbTree0->AutomaticOn(); obbTree0->BuildLocator(); if (this->CheckAbort()) { return 1; } vtkSmartPointer obbTree1 = vtkSmartPointer::New(); obbTree1->SetDataSet(mesh1); obbTree1->SetNumberOfCellsPerNode(10); obbTree1->SetMaxLevel(1000000); obbTree1->SetTolerance(this->Tolerance); obbTree1->AutomaticOn(); obbTree1->BuildLocator(); if (this->CheckAbort()) { return 1; } // Set up the structure for determining exact triangle-triangle // intersections. vtkIntersectionPolyDataFilter::Impl* impl = new vtkIntersectionPolyDataFilter::Impl(); impl->ParentFilter = this; impl->Mesh[0] = mesh0; impl->Mesh[1] = mesh1; impl->OBBTree1 = obbTree1; impl->Tolerance = this->Tolerance; impl->RelativeSubtriangleArea = this->RelativeSubtriangleArea; vtkSmartPointer lines = vtkSmartPointer::New(); outputIntersection->SetLines(lines); impl->IntersectionLines = lines; // Add cell data arrays that map the intersection line to the cells // it splits. impl->CellIds[0] = vtkIdTypeArray::New(); impl->CellIds[0]->SetName("Input0CellID"); outputIntersection->GetCellData()->AddArray(impl->CellIds[0]); impl->CellIds[0]->Delete(); impl->CellIds[1] = vtkIdTypeArray::New(); impl->CellIds[1]->SetName("Input1CellID"); outputIntersection->GetCellData()->AddArray(impl->CellIds[1]); impl->CellIds[1]->Delete(); impl->PointCellIds[0] = vtkIdTypeArray::New(); impl->PointCellIds[0]->SetName("PointCellsIDs"); impl->PointCellIds[1] = vtkIdTypeArray::New(); impl->PointCellIds[1]->SetName("PointCellsIDs"); impl->SurfaceId = vtkIdTypeArray::New(); impl->SurfaceId->SetName("SurfaceID"); outputIntersection->GetPointData()->AddArray(impl->SurfaceId); impl->NewCellIds[0] = vtkIdTypeArray::New(); impl->NewCellIds[0]->SetNumberOfComponents(2); impl->NewCellIds[1] = vtkIdTypeArray::New(); impl->NewCellIds[1]->SetNumberOfComponents(2); double bounds0[6], bounds1[6]; mesh0->GetBounds(bounds0); mesh1->GetBounds(bounds1); for (int i = 0; i < 3; i++) { int minIdx = 2 * i; int maxIdx = 2 * i + 1; bounds0[minIdx] = std::min(bounds1[minIdx], bounds0[minIdx]); bounds0[maxIdx] = std::max(bounds1[maxIdx], bounds0[maxIdx]); } // Set up the point merger for insertion of points into the intersection // lines. Tolerance is set to 1e-6 vtkSmartPointer pointMerger = vtkSmartPointer::New(); pointMerger->SetTolerance(sqrt(2.0) * this->Tolerance); pointMerger->InitPointInsertion(outputIntersection->GetPoints(), bounds0); impl->PointMerger = pointMerger; if (this->CheckAbort()) { delete impl; return 1; } // This performs the triangle intersection search obbTree0->IntersectWithOBBTree( obbTree1, nullptr, vtkIntersectionPolyDataFilter::Impl::FindTriangleIntersections, impl); int rawLines = outputIntersection->GetNumberOfLines(); for (int i = 0; i < 2; i++) { for (vtkIdType interCellId = 0; interCellId < rawLines; interCellId++) { impl->NewCellIds[i]->InsertTuple2(interCellId, -1, -1); } } vtkDebugMacro(<< "LINEPTSBEFORE " << outputIntersection->GetNumberOfPoints()); // The point merger doesn't doesn't detect 100 percent of the points already // inserted into the points object. This sometimes causes multiple lines // or points. To account for this, this simple clean retains what we need. vtkSmartPointer tmpLines = vtkSmartPointer::New(); tmpLines->DeepCopy(outputIntersection); tmpLines->BuildLinks(); vtkSmartPointer lineCleaner = vtkSmartPointer::New(); lineCleaner->SetInputData(outputIntersection); lineCleaner->ToleranceIsAbsoluteOn(); lineCleaner->SetAbsoluteTolerance(this->Tolerance); lineCleaner->SetContainerAlgorithm(this); lineCleaner->Update(); outputIntersection->DeepCopy(lineCleaner->GetOutput()); vtkSmartPointer linePtMapper = vtkSmartPointer::New(); linePtMapper->SetDataSet(outputIntersection); linePtMapper->BuildLocator(); double newpt[3]; vtkIdType mapPtId = 0; for (vtkIdType ptId = 0; ptId < tmpLines->GetNumberOfPoints(); ptId++) { tmpLines->GetPoint(ptId, newpt); mapPtId = linePtMapper->FindClosestPoint(newpt); impl->PointMapper->insert(std::make_pair(mapPtId, ptId)); } vtkDebugMacro(<< "LINEPTSAFTER " << outputIntersection->GetNumberOfPoints()); this->NumberOfIntersectionPoints = outputIntersection->GetNumberOfPoints(); this->NumberOfIntersectionLines = outputIntersection->GetNumberOfLines(); if (this->CheckAbort()) { delete impl; return 1; } if (this->NumberOfIntersectionPoints == 0 || this->NumberOfIntersectionLines == 0) { vtkGenericWarningMacro(<< "No Intersection between objects "); impl->NewCellIds[0]->Delete(); impl->NewCellIds[1]->Delete(); impl->PointCellIds[0]->Delete(); impl->PointCellIds[1]->Delete(); impl->SurfaceId->Delete(); delete impl; return 1; } if (this->CheckAbort()) { delete impl; return 1; } impl->BoundaryPoints[0] = vtkIntArray::New(); impl->BoundaryPoints[1] = vtkIntArray::New(); // Split the first output if so desired, needed if performing boolean op if (this->SplitFirstOutput) { mesh0->BuildLinks(); if (impl->SplitMesh(0, outputPolyData0, outputIntersection) != 1) { this->Status = 0; this->NumberOfIntersectionPoints = 0; this->NumberOfIntersectionLines = 0; impl->NewCellIds[0]->Delete(); impl->NewCellIds[1]->Delete(); impl->BoundaryPoints[0]->Delete(); impl->BoundaryPoints[1]->Delete(); impl->PointCellIds[0]->Delete(); impl->PointCellIds[1]->Delete(); impl->SurfaceId->Delete(); delete impl; return 0; } if (this->ComputeIntersectionPointArray) { impl->BoundaryPoints[0]->SetName("BoundaryPoints"); outputPolyData0->GetPointData()->AddArray(impl->BoundaryPoints[0]); outputPolyData0->GetPointData()->SetActiveScalars("BoundaryPoints"); } if (this->CheckMesh) { double dummy[2]; CleanAndCheckSurface(outputPolyData0, dummy, this->Tolerance); } outputPolyData0->BuildLinks(); } else { outputPolyData0->ShallowCopy(mesh0); } if (this->CheckAbort()) { delete impl; return 1; } // Split the second output if desired if (this->SplitSecondOutput) { mesh1->BuildLinks(); if (impl->SplitMesh(1, outputPolyData1, outputIntersection) != 1) { this->Status = 0; this->NumberOfIntersectionPoints = 0; this->NumberOfIntersectionLines = 0; impl->NewCellIds[0]->Delete(); impl->NewCellIds[1]->Delete(); impl->BoundaryPoints[0]->Delete(); impl->BoundaryPoints[1]->Delete(); impl->PointCellIds[0]->Delete(); impl->PointCellIds[1]->Delete(); impl->SurfaceId->Delete(); delete impl; return 0; } if (this->ComputeIntersectionPointArray) { impl->BoundaryPoints[1]->SetName("BoundaryPoints"); outputPolyData1->GetPointData()->AddArray(impl->BoundaryPoints[1]); outputPolyData1->GetPointData()->SetActiveScalars("BoundaryPoints"); } if (this->CheckMesh) { double dummy[2]; CleanAndCheckSurface(outputPolyData1, dummy, this->Tolerance); } outputPolyData1->BuildLinks(); } else { outputPolyData1->ShallowCopy(mesh1); } impl->NewCellIds[0]->SetName("NewCell0ID"); outputIntersection->GetCellData()->AddArray(impl->NewCellIds[0]); impl->NewCellIds[0]->Delete(); impl->NewCellIds[1]->SetName("NewCell1ID"); outputIntersection->GetCellData()->AddArray(impl->NewCellIds[1]); impl->NewCellIds[1]->Delete(); impl->BoundaryPoints[0]->Delete(); impl->BoundaryPoints[1]->Delete(); impl->PointCellIds[0]->Delete(); impl->PointCellIds[1]->Delete(); impl->SurfaceId->Delete(); delete impl; return 1; } //------------------------------------------------------------------------------ int vtkIntersectionPolyDataFilter::FillInputPortInformation(int port, vtkInformation* info) { if (!this->Superclass::FillInputPortInformation(port, info)) { return 0; } if (port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); } else if (port == 1) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 0); } return 1; } //------------------------------------------------------------------------------ VTK_ABI_NAMESPACE_END