// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkSelectPolyData.h" #include "vtkCellData.h" #include "vtkDijkstraGraphGeodesicPath.h" #include "vtkExecutive.h" #include "vtkFloatArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkLine.h" #include "vtkPointData.h" #include "vtkPointLocator.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkTriangleFilter.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkSelectPolyData); vtkCxxSetObjectMacro(vtkSelectPolyData, Loop, vtkPoints); // Description: // Instantiate object with InsideOut turned off. vtkSelectPolyData::vtkSelectPolyData() : SelectionScalarsArrayName(nullptr) { this->GenerateSelectionScalars = 0; this->SetSelectionScalarsArrayName("Selection"); this->InsideOut = 0; this->EdgeSearchMode = VTK_GREEDY_EDGE_SEARCH; this->Loop = nullptr; this->SelectionMode = VTK_INSIDE_SMALLEST_REGION; this->ClosestPoint[0] = this->ClosestPoint[1] = this->ClosestPoint[2] = 0.0; this->GenerateUnselectedOutput = 0; this->SetNumberOfOutputPorts(3); vtkNew output2; this->GetExecutive()->SetOutputData(1, output2); vtkNew output3; this->GetExecutive()->SetOutputData(2, output3); } //------------------------------------------------------------------------------ vtkSelectPolyData::~vtkSelectPolyData() { this->SetSelectionScalarsArrayName(nullptr); if (this->Loop) { this->Loop->Delete(); } } //------------------------------------------------------------------------------ vtkPolyData* vtkSelectPolyData::GetUnselectedOutput() { if (this->GetNumberOfOutputPorts() < 2) { return nullptr; } return vtkPolyData::SafeDownCast(this->GetExecutive()->GetOutputData(1)); } //------------------------------------------------------------------------------ vtkAlgorithmOutput* vtkSelectPolyData::GetUnselectedOutputPort() { return this->GetOutputPort(1); } //------------------------------------------------------------------------------ vtkPolyData* vtkSelectPolyData::GetSelectionEdges() { if (this->GetNumberOfOutputPorts() < 3) { return nullptr; } return vtkPolyData::SafeDownCast(this->GetExecutive()->GetOutputData(2)); } //------------------------------------------------------------------------------ void vtkSelectPolyData::GreedyEdgeSearch(vtkPolyData* mesh, vtkIdList* edgePointIds) { vtkIdType numLoopPts = this->Loop->GetNumberOfPoints(); // First thing to do is find the closest mesh points to the loop // points. This creates a list of mesh point ids corresponding to the loop point positions. vtkNew loopIds; loopIds->SetNumberOfIds(numLoopPts); vtkPoints* inPts = mesh->GetPoints(); vtkIdType numPts = mesh->GetNumberOfPoints(); for (vtkIdType loopPointId = 0; loopPointId < numLoopPts; loopPointId++) { if (this->CheckAbort()) { break; } double xLoop[3]; this->Loop->GetPoint(loopPointId, xLoop); vtkIdType closestMeshPointId = 0; double closestDist2 = VTK_DOUBLE_MAX; for (vtkIdType meshPointId = 0; meshPointId < numPts; meshPointId++) { double x[3]; inPts->GetPoint(meshPointId, x); double dist2 = vtkMath::Distance2BetweenPoints(x, xLoop); if (dist2 < closestDist2) { closestMeshPointId = meshPointId; closestDist2 = dist2; } } // for all input points loopIds->SetId(loopPointId, closestMeshPointId); } // for all loop points edgePointIds->InsertNextId(loopIds->GetId(0)); // Now that we've got point ids, we build the loop. Start with the // first two points in the loop (which define a line), and find the // mesh edge that is directed along the line, and whose // end point is closest to the line. Continue until loop closes in on // itself. vtkNew neighbors; neighbors->Allocate(10000); for (vtkIdType loopPointIndex = 0; loopPointIndex < numLoopPts; loopPointIndex++) { if (this->CheckAbort()) { break; } vtkIdType currentId = loopIds->GetId(loopPointIndex); vtkIdType nextId = loopIds->GetId((loopPointIndex + 1) % numLoopPts); vtkIdType prevId = (-1); double x[3]; double x0[3]; double x1[3]; double vec[3]; inPts->GetPoint(currentId, x); inPts->GetPoint(currentId, x0); inPts->GetPoint(nextId, x1); for (int j = 0; j < 3; j++) { vec[j] = x1[j] - x0[j]; } // track edge for (vtkIdType id = currentId; id != nextId;) { vtkSelectPolyData::GetPointNeighbors(mesh, id, neighbors); // points connected by edge vtkIdType numNei = neighbors->GetNumberOfIds(); vtkIdType closest = -1; double closestDist2 = VTK_DOUBLE_MAX; for (vtkIdType j = 0; j < numNei; j++) { vtkIdType neiId = neighbors->GetId(j); if (neiId == nextId) { closest = neiId; break; } else { double neiX[3]; inPts->GetPoint(neiId, neiX); double dir[3]; for (vtkIdType k = 0; k < 3; k++) { dir[k] = neiX[k] - x[k]; } if (neiId != prevId && vtkMath::Dot(dir, vec) > 0.0) // candidate { double dist2 = vtkLine::DistanceToLine(neiX, x0, x1); if (dist2 < closestDist2) { closest = neiId; closestDist2 = dist2; } } // in direction of line } } // for all neighbors if (closest < 0) { vtkErrorMacro(<< "Can't follow edge. Set EdgeSearchMode to Dijkstra to avoid this error."); edgePointIds->Initialize(); // clear edge list to indicate error return; } else { edgePointIds->InsertNextId(closest); prevId = id; id = closest; inPts->GetPoint(id, x); } } // for tracking edge } // for all edges of loop } //------------------------------------------------------------------------------ void vtkSelectPolyData::DijkstraEdgeSearch(vtkPolyData* mesh, vtkIdList* edgePointIds) { vtkNew edgeSearchFilter; edgeSearchFilter->StopWhenEndReachedOn(); edgeSearchFilter->SetInputData(mesh); vtkNew pointLocator; pointLocator->SetDataSet(mesh); vtkPoints* inPts = mesh->GetPoints(); vtkIdType numLoopPts = this->Loop->GetNumberOfPoints(); vtkIdType currentId = 0; double xLoop[3]; this->Loop->GetPoint(0, xLoop); vtkIdType nextId = pointLocator->FindClosestPoint(xLoop); for (vtkIdType i = 0; i < numLoopPts; i++) { if (this->CheckAbort()) { break; } currentId = nextId; this->Loop->GetPoint((i + 1) % numLoopPts, xLoop); nextId = pointLocator->FindClosestPoint(xLoop); edgeSearchFilter->SetStartVertex(currentId); edgeSearchFilter->SetEndVertex(nextId); edgeSearchFilter->Update(); vtkPolyData* outputPath = edgeSearchFilter->GetOutput(); double x0[3]; inPts->GetPoint(currentId, x0); for (int j = outputPath->GetNumberOfPoints() - 1; j >= 0; --j) { double x[3]; outputPath->GetPoint(j, x); double dist2 = vtkMath::Distance2BetweenPoints(x, x0); if (dist2 > 0.0) { // Find point ID to add in the input mesh to remember the next edge point edgePointIds->InsertNextId(pointLocator->FindClosestPoint(x)); for (int k = 0; k < 3; ++k) { // Remember last added point so that it does not get added twice x0[k] = x[k]; } } } } } //------------------------------------------------------------------------------ int vtkSelectPolyData::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the input and output objects vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); // Initialize and check data vtkDebugMacro(<< "Selecting data..."); this->GetUnselectedOutput()->Initialize(); this->GetSelectionEdges()->Initialize(); // CHeck if inputs are valid if (input->GetNumberOfPoints() < 1) { vtkErrorMacro("Input contains no points"); return 1; } vtkIdType numLoopPts; if (this->Loop == nullptr || (numLoopPts = this->Loop->GetNumberOfPoints()) < 3) { vtkErrorMacro("Please define a loop with at least three points"); return 1; } // Convert to triangle mesh. All further computations are done on the triangulated mesh. vtkSmartPointer triMesh; { vtkNew tf; tf->SetInputData(input); tf->PassLinesOff(); tf->PassVertsOff(); tf->SetContainerAlgorithm(this); tf->Update(); triMesh = tf->GetOutput(); } vtkCellArray* inPolys = triMesh->GetPolys(); if (inPolys->GetNumberOfCells() < 1) { vtkErrorMacro("This filter operates on surface primitives"); return 1; } // Create a mesh that only contains points and polys // (probably to avoid potential interference of other cell types) // and links are computed (so that neighbors can be retrieved). vtkNew mesh; vtkPoints* inPts = triMesh->GetPoints(); mesh->SetPoints(inPts); mesh->SetPolys(inPolys); mesh->BuildLinks(); // to do neighborhood searching vtkIdType numCells = mesh->GetNumberOfCells(); // Get a list of point IDs of the mesh that forms a continuous closed loop vtkNew edgePointIds; edgePointIds->Allocate(numLoopPts * 10, 1000); switch (this->EdgeSearchMode) { case VTK_GREEDY_EDGE_SEARCH: this->GreedyEdgeSearch(mesh, edgePointIds); break; case VTK_DIJKSTRA_EDGE_SEARCH: this->DijkstraEdgeSearch(triMesh, edgePointIds); break; default: vtkErrorMacro("Unknown edge search mode: " << this->EdgeSearchMode); } if (edgePointIds->GetNumberOfIds() == 0 || this->CheckAbort()) { return 1; } // Save the found edge list into SelectionEdges polydata vtkIdType numMeshLoopPts = edgePointIds->GetNumberOfIds(); vtkNew selectionEdges; selectionEdges->AllocateEstimate(1, numMeshLoopPts); selectionEdges->InsertNextCell(numMeshLoopPts); for (vtkIdType i = 0; i < numMeshLoopPts; i++) { selectionEdges->InsertCellPoint(edgePointIds->GetId(i)); } this->GetSelectionEdges()->SetPoints(inPts); this->GetSelectionEdges()->SetLines(selectionEdges); // Store distance from edge in point and cell marks and // get ID of the cell that is farthest from the loop. vtkNew pointMarks; vtkNew cellMarks; vtkIdType cellIdInSelectedRegion = this->ComputeTopologicalDistance(mesh, edgePointIds, pointMarks, cellMarks); // If the region that is closest to a specific point needs to be extracted then get // a cell that is closes to that position. if (this->SelectionMode == VTK_INSIDE_CLOSEST_POINT_REGION) { // find closest point and use as a seed cellIdInSelectedRegion = this->GetClosestCellId(mesh, pointMarks); } // Set point and cell mark values in the selected region to -1. // We'll end up having >0 values outside the selected region, -1 inside. this->FillMarksInRegion(mesh, edgePointIds, pointMarks, cellMarks, cellIdInSelectedRegion); // Invert mark value if we want to get the smallest region. if (this->SelectionMode == VTK_INSIDE_SMALLEST_REGION) { for (vtkIdType i = 0; i < numCells; i++) { int markValue = cellMarks->GetValue(i); cellMarks->SetValue(i, -markValue); } vtkIdType numPts = pointMarks->GetNumberOfValues(); for (vtkIdType i = 0; i < numPts; i++) { int markValue = pointMarks->GetValue(i); pointMarks->SetValue(i, -markValue); } } // Write filter output. vtkPointData* inPD = triMesh->GetPointData(); vtkCellData* inCD = triMesh->GetCellData(); if (this->GenerateSelectionScalars) { // Write distance from contour as scalars to the output mesh. // This can be used for example for later cliipping the mesh with vtkClipPolyData. this->SetSelectionScalarsToOutput(inPD, inCD, mesh, edgePointIds, pointMarks, output); } else { // crop the input mesh to the selected region this->SetClippedResultToOutput(inPD, inCD, mesh, cellMarks, output); } return 1; } //------------------------------------------------------------------------------ vtkIdType vtkSelectPolyData::ComputeTopologicalDistance( vtkPolyData* mesh, vtkIdList* edgePointIds, vtkIntArray* pointMarks, vtkIntArray* cellMarks) { vtkIdType numPts = mesh->GetNumberOfPoints(); vtkIdType numCells = mesh->GetNumberOfCells(); // Next, prepare to mark off inside/outside and on boundary of loop. // Mark the boundary of the loop using point marks. Also initialize // the advancing front (used to mark traversal/compute scalars). // Prepare to compute the advancing front // Mark all points and cells as unvisited cellMarks->SetNumberOfValues(numCells); const int unvisited = VTK_INT_MAX; for (vtkIdType i = 0; i < numCells; i++) { cellMarks->SetValue(i, unvisited); } pointMarks->SetNumberOfValues(numPts); for (vtkIdType i = 0; i < numPts; i++) { pointMarks->SetValue(i, unvisited); } // Current and next front contain point IDs vtkSmartPointer currentFront = vtkSmartPointer::New(); vtkSmartPointer nextFront = vtkSmartPointer::New(); vtkIdType numMeshLoopPts = edgePointIds->GetNumberOfIds(); for (vtkIdType i = 0; i < numMeshLoopPts; i++) { vtkIdType id = edgePointIds->GetId(i); pointMarks->SetValue(id, 0); // marks the start of the front currentFront->InsertNextId(id); } // Traverse the front as long as we can. We're basically computing a // topological distance. int maxFrontValue = 0; vtkIdType maxFrontCell = (-1); int currentFrontValue = 1; vtkIdType numPtsInFront = 0; while ((numPtsInFront = currentFront->GetNumberOfIds())) { // Process all triangles around the current front points for (vtkIdType i = 0; i < numPtsInFront; i++) { vtkIdType id = currentFront->GetId(i); vtkIdType* cells; vtkIdType ncells; mesh->GetPointCells(id, ncells, cells); for (vtkIdType j = 0; j < ncells; j++) { id = cells[j]; if (cellMarks->GetValue(id) != unvisited) { // the cell is already visited continue; } // This cell has not been visited yet if (currentFrontValue > maxFrontValue) { // update maximum distance maxFrontCell = id; } cellMarks->SetValue(id, currentFrontValue); // Add all unvisited points of this triangle to the front const vtkIdType* pts; vtkIdType npts; mesh->GetCellPoints(id, npts, pts); for (vtkIdType k = 0; k < npts; k++) { if (pointMarks->GetValue(pts[k]) == unvisited) { pointMarks->SetValue(pts[k], 1); nextFront->InsertNextId(pts[k]); } } } } // All points in the current front has been processed, start a new iteration currentFrontValue++; // Swap currentFront and nextFront vtkSmartPointer tmpFront = currentFront; currentFront = nextFront; nextFront = tmpFront; nextFront->Reset(); } return maxFrontCell; } //------------------------------------------------------------------------------ vtkIdType vtkSelectPolyData::GetClosestCellId(vtkPolyData* mesh, vtkIntArray* pointMarks) { vtkPoints* inPts = mesh->GetPoints(); vtkIdType numPts = inPts->GetNumberOfPoints(); vtkIdType closestCellId = -1; double closestDist2 = VTK_DOUBLE_MAX; vtkIdType closestPointId = -1; for (vtkIdType pointId = 0; pointId < numPts; pointId++) { double x[3]; inPts->GetPoint(pointId, x); double dist2 = vtkMath::Distance2BetweenPoints(x, this->ClosestPoint); // get closest point not on the boundary if (dist2 < closestDist2 && pointMarks->GetValue(pointId) != 0) { closestPointId = pointId; closestDist2 = dist2; } } if (closestPointId >= 0) { vtkIdType ncells; vtkIdType* cells; mesh->GetPointCells(closestPointId, ncells, cells); if (ncells > 0) { closestCellId = cells[0]; } } return closestCellId; } //------------------------------------------------------------------------------ void vtkSelectPolyData::FillMarksInRegion(vtkPolyData* mesh, vtkIdList* edgePointIds, vtkIntArray* pointMarks, vtkIntArray* cellMarks, vtkIdType cellIdInSelectedRegion) { // We do the fill as a moving front. This is an alternative to recursion. The // fill negates one region of the mesh on one side of the loop. // In contrast to ComputeTopologicalDistance, current and next front // in this method contain cell IDs. vtkSmartPointer currentFront = vtkSmartPointer::New(); vtkSmartPointer nextFront = vtkSmartPointer::New(); currentFront->InsertNextId(cellIdInSelectedRegion); // Initialize the front with the received cell ID constexpr int fillValue = -1; constexpr int boundaryValue = 0; cellMarks->SetValue(cellIdInSelectedRegion, fillValue); vtkNew neighbors; neighbors->Allocate(10000); vtkIdType numCellsInFront; while ((numCellsInFront = currentFront->GetNumberOfIds()) > 0) { // Iterate through all the triangles and visit all the neighbor triangles for (vtkIdType i = 0; i < numCellsInFront; i++) { vtkIdType id = currentFront->GetId(i); const vtkIdType* pts; vtkIdType npts; mesh->GetCellPoints(id, npts, pts); for (vtkIdType j = 0; j < 3; j++) { vtkIdType cellPointId1 = pts[j]; vtkIdType cellPointId2 = pts[(j + 1) % 3]; int cellPointValue1 = pointMarks->GetValue(cellPointId1); int cellPointValue2 = pointMarks->GetValue(cellPointId2); if (cellPointValue1 != boundaryValue) { pointMarks->SetValue(cellPointId1, fillValue); } if (cellPointValue1 == boundaryValue && cellPointValue2 == boundaryValue) { // This may be a boundary edge or just an edge that connects two boundary points. // Do a full search in the boundary edge list to find out. if (vtkSelectPolyData::IsBoundaryEdge(cellPointId1, cellPointId2, edgePointIds)) { // cannot cross boundary continue; } } // add neighbors of this edge to the advancing front mesh->GetCellEdgeNeighbors(id, cellPointId1, cellPointId2, neighbors); vtkIdType numNei = neighbors->GetNumberOfIds(); for (vtkIdType k = 0; k < numNei; k++) { vtkIdType neiId = neighbors->GetId(k); int val = cellMarks->GetValue(neiId); if (val == fillValue) { // already processed continue; } cellMarks->SetValue(neiId, fillValue); nextFront->InsertNextId(neiId); } } // for all edges of cell } // all cells in front // Swap currentFront and nextFront vtkSmartPointer tmpFront = currentFront; currentFront = nextFront; nextFront = tmpFront; nextFront->Reset(); } // while still advancing } //------------------------------------------------------------------------------ bool vtkSelectPolyData::IsBoundaryEdge( vtkIdType pointId1, vtkIdType pointId2, vtkIdList* edgePointIds) { vtkIdType numMeshLoopPts = edgePointIds->GetNumberOfIds(); for (vtkIdType edgePointIndex = 0; edgePointIndex < numMeshLoopPts; ++edgePointIndex) { vtkIdType edgePointId = edgePointIds->GetId(edgePointIndex); if (edgePointId == pointId1) { vtkIdType wrappedEdgePointIndex = (edgePointIndex + 1) % numMeshLoopPts; vtkIdType edgePointIdAfter = edgePointIds->GetId(wrappedEdgePointIndex); if (edgePointIdAfter == pointId2) { // boundary edge return true; } // Conceptually (edgePointIndex - 1) % numMeshLoopPts, but avoiding negatives. wrappedEdgePointIndex = (edgePointIndex > 0) ? (edgePointIndex - 1) : (numMeshLoopPts - 1); vtkIdType edgePointIdBefore = edgePointIds->GetId(wrappedEdgePointIndex); if (edgePointIdBefore == pointId2) { // boundary edge return true; } } if (edgePointId == pointId2) { vtkIdType wrappedEdgePointIndex = (edgePointIndex + 1) % numMeshLoopPts; vtkIdType edgePointIdAfter = edgePointIds->GetId(wrappedEdgePointIndex); if (edgePointIdAfter == pointId1) { // boundary edge return true; } // Conceptually (edgePointIndex - 1) % numMeshLoopPts, but avoiding negatives. wrappedEdgePointIndex = (edgePointIndex > 0) ? (edgePointIndex - 1) : (numMeshLoopPts - 1); vtkIdType edgePointIdBefore = edgePointIds->GetId(wrappedEdgePointIndex); if (edgePointIdBefore == pointId1) { // boundary edge return true; } } } // not a boundary edge return false; } //------------------------------------------------------------------------------ void vtkSelectPolyData::SetSelectionScalarsToOutput(vtkPointData* originalPointData, vtkCellData* originalCellData, vtkPolyData* mesh, vtkIdList* edgePointIds, vtkIntArray* pointMarks, vtkPolyData* output) { vtkPoints* inPts = mesh->GetPoints(); vtkIdType numPts = inPts->GetNumberOfPoints(); vtkNew selectionScalars; selectionScalars->SetName(this->SelectionScalarsArrayName); selectionScalars->SetNumberOfTuples(numPts); // "Boundary" here refers to a polyline that connects the loop point positions. // Compute signed distance to loop for non-boundary points. vtkIdType numLoopPts = this->Loop->GetNumberOfPoints(); for (vtkIdType pointId = 0; pointId < numPts; pointId++) { if (this->CheckAbort()) { break; } if (pointMarks->GetValue(pointId) == 0) { // boundary point, we'll deal with these later continue; } // Not an edge point. double x[3]; inPts->GetPoint(pointId, x); double closestDist2 = VTK_DOUBLE_MAX; for (vtkIdType i = 0; i < numLoopPts; i++) { double x0[3]; double x1[3]; this->Loop->GetPoint(i, x0); this->Loop->GetPoint((i + 1) % numLoopPts, x1); double t; double xLoop[3]; double dist2 = vtkLine::DistanceToLine(x, x0, x1, t, xLoop); closestDist2 = std::min(dist2, closestDist2); } // Set signed distance double closestDist = 0.0; if (pointMarks->GetValue(pointId) < 0) { closestDist = -sqrt(closestDist2); } else { closestDist = sqrt(closestDist2); } selectionScalars->SetComponent(pointId, 0, closestDist); } // Compute signed distance to loop for boundary points. vtkIdType numMeshLoopPts = edgePointIds->GetNumberOfIds(); vtkNew neighbors; neighbors->Allocate(10000); for (vtkIdType edgePointIndex = 0; edgePointIndex < numMeshLoopPts; edgePointIndex++) { if (this->CheckAbort()) { break; } vtkIdType edgePointId = edgePointIds->GetId(edgePointIndex); double x[3]; inPts->GetPoint(edgePointId, x); // Find the boundary line segment closest to this point double closestPointOnBoundaryPos[3] = { 0.0, 0.0, 0.0 }; // closest position of edgePoint on the boundary double closestDist2 = VTK_DOUBLE_MAX; // distance from closest boundary { for (vtkIdType loopPointId = 0; loopPointId < numLoopPts; loopPointId++) { double x0[3]; double x1[3]; this->Loop->GetPoint(loopPointId, x0); this->Loop->GetPoint((loopPointId + 1) % numLoopPts, x1); double t; double xLoop[3]; // closest position on the boundary double dist2 = vtkLine::DistanceToLine(x, x0, x1, t, xLoop); if (dist2 < closestDist2) { closestDist2 = dist2; closestPointOnBoundaryPos[0] = xLoop[0]; closestPointOnBoundaryPos[1] = xLoop[1]; closestPointOnBoundaryPos[2] = xLoop[2]; } } } // Find neighbor farthest from the boundary (inside/outside information // is the most reliable for this neighbor). vtkIdType farthestNeighborPointId = 0; { vtkSelectPolyData::GetPointNeighbors(mesh, edgePointId, neighbors); vtkIdType numNei = neighbors->GetNumberOfIds(); double maxDist = 0.0; for (vtkIdType i = 0; i < numNei; i++) { vtkIdType neiId = neighbors->GetId(i); if (pointMarks->GetValue(neiId) != 0) // find the furthest away { double dist = fabs(selectionScalars->GetComponent(neiId, 0)); if (dist > maxDist) { farthestNeighborPointId = neiId; maxDist = dist; } } } } // First compute distance assuming that x is on the same side of the boundary as the farthest // neighbor. double dist = sqrt(closestDist2); if (pointMarks->GetValue(farthestNeighborPointId) < 0) { dist = -dist; } // If x and the farthest neighbor are actually different sides of the boundary then invert the // signed distance value. double farthestNeighborPointPos[3]; inPts->GetPoint(farthestNeighborPointId, farthestNeighborPointPos); if (vtkMath::Distance2BetweenPoints(farthestNeighborPointPos, x) > vtkMath::Distance2BetweenPoints(farthestNeighborPointPos, closestPointOnBoundaryPos)) { // x is on the other side of the boundary dist = -dist; } selectionScalars->SetComponent(edgePointId, 0, dist); } // for all boundary points output->CopyStructure(mesh); // pass geometry/topology unchanged vtkPointData* outPD = output->GetPointData(); outPD->CopyAllOn(); outPD->PassData(originalPointData); int idx = outPD->AddArray(selectionScalars); outPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS); vtkCellData* outCD = output->GetCellData(); outCD->PassData(originalCellData); } //------------------------------------------------------------------------------ void vtkSelectPolyData::SetClippedResultToOutput(vtkPointData* originalPointData, vtkCellData* originalCellData, vtkPolyData* mesh, vtkIntArray* cellMarks, vtkPolyData* output) { vtkCellData* outCD = output->GetCellData(); outCD->CopyAllOn(vtkDataSetAttributes::COPYTUPLE); outCD->CopyAllocate(originalCellData); // spit out all the negative cells vtkNew newPolys; vtkIdType numCells = mesh->GetNumberOfCells(); newPolys->AllocateEstimate(numCells / 2, 3); vtkIdType newID = 0; for (vtkIdType i = 0; i < numCells; i++) { if (this->CheckAbort()) { break; } if ((cellMarks->GetValue(i) < 0) || (cellMarks->GetValue(i) > 0 && this->InsideOut)) { const vtkIdType* pts; vtkIdType npts; mesh->GetCellPoints(i, npts, pts); newID = newPolys->InsertNextCell(npts, pts); outCD->CopyData(originalCellData, i, newID); } } vtkPoints* inPts = mesh->GetPoints(); output->SetPoints(inPts); output->SetPolys(newPolys); vtkPointData* outPD = output->GetPointData(); outPD->PassData(originalPointData); if (this->GenerateUnselectedOutput) { vtkCellData* unCD = this->GetUnselectedOutput()->GetCellData(); unCD->CopyAllOn(vtkDataSetAttributes::COPYTUPLE); unCD->CopyAllocate(originalCellData); vtkNew unPolys; unPolys->AllocateEstimate(numCells / 2, 3); for (vtkIdType i = 0; i < numCells; i++) { if (this->CheckAbort()) { break; } if ((cellMarks->GetValue(i) >= 0) || this->InsideOut) { const vtkIdType* pts; vtkIdType npts; mesh->GetCellPoints(i, npts, pts); newID = unPolys->InsertNextCell(npts, pts); unCD->CopyData(originalCellData, i, newID); } } this->GetUnselectedOutput()->SetPoints(inPts); this->GetUnselectedOutput()->SetPolys(unPolys); this->GetUnselectedOutput()->GetPointData()->PassData(originalPointData); } } //------------------------------------------------------------------------------ void vtkSelectPolyData::GetPointNeighbors(vtkPolyData* mesh, vtkIdType ptId, vtkIdList* nei) { nei->Reset(); vtkIdType ncells; vtkIdType* cells; mesh->GetPointCells(ptId, ncells, cells); for (vtkIdType i = 0; i < ncells; i++) { const vtkIdType* pts; vtkIdType npts; mesh->GetCellPoints(cells[i], npts, pts); for (vtkIdType j = 0; j < 3; j++) { if (pts[j] != ptId) { nei->InsertUniqueId(pts[j]); } } } } //------------------------------------------------------------------------------ vtkMTimeType vtkSelectPolyData::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); vtkMTimeType time; if (this->Loop != nullptr) { time = this->Loop->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } //------------------------------------------------------------------------------ void vtkSelectPolyData::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Generate Unselected Output: " << (this->GenerateUnselectedOutput ? "On\n" : "Off\n"); os << indent << "Inside Mode: "; os << this->GetSelectionModeAsString() << "\n"; os << indent << "Closest Point: (" << this->ClosestPoint[0] << ", " << this->ClosestPoint[1] << ", " << this->ClosestPoint[2] << ")\n"; os << indent << "Generate Selection Scalars: " << (this->GenerateSelectionScalars ? "On\n" : "Off\n"); if (this->GenerateSelectionScalars) { os << indent << "Selection Scalars array name: " << this->SelectionScalarsArrayName << "\n"; } os << indent << "Inside Out: " << (this->InsideOut ? "On\n" : "Off\n"); os << indent << "Edge Search Mode: "; os << this->GetEdgeSearchModeAsString() << "\n"; if (this->Loop) { os << indent << "Loop of " << this->Loop->GetNumberOfPoints() << "points defined\n"; } else { os << indent << "Loop not defined\n"; } } VTK_ABI_NAMESPACE_END