// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkBooleanOperationPolyDataFilter.h" #include "vtkCellData.h" #include "vtkDistancePolyDataFilter.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkGenericCell.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkIntersectionPolyDataFilter.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkSmartPointer.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkBooleanOperationPolyDataFilter); //------------------------------------------------------------------------------ vtkBooleanOperationPolyDataFilter::vtkBooleanOperationPolyDataFilter() { this->Tolerance = 1e-6; this->Operation = VTK_UNION; this->ReorientDifferenceCells = 1; this->SetNumberOfInputPorts(2); this->SetNumberOfOutputPorts(2); } //------------------------------------------------------------------------------ vtkBooleanOperationPolyDataFilter::~vtkBooleanOperationPolyDataFilter() = default; //------------------------------------------------------------------------------ void vtkBooleanOperationPolyDataFilter::SortPolyData( vtkPolyData* input, vtkIdList* intersectionList, vtkIdList* unionList) { int numCells = input->GetNumberOfCells(); vtkDoubleArray* distArray = vtkArrayDownCast(input->GetCellData()->GetArray("Distance")); for (int cid = 0; cid < numCells; cid++) { if (distArray->GetValue(cid) > this->Tolerance) { unionList->InsertNextId(cid); } else { intersectionList->InsertNextId(cid); } } } //------------------------------------------------------------------------------ int vtkBooleanOperationPolyDataFilter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo0 = inputVector[0]->GetInformationObject(0); vtkInformation* inInfo1 = inputVector[1]->GetInformationObject(0); vtkInformation* outInfo0 = outputVector->GetInformationObject(0); vtkInformation* outInfo1 = outputVector->GetInformationObject(1); if (!inInfo0 || !inInfo1 || !outInfo0 || !outInfo1) { return 0; } vtkPolyData* input0 = vtkPolyData::SafeDownCast(inInfo0->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* input1 = vtkPolyData::SafeDownCast(inInfo1->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* outputSurface = vtkPolyData::SafeDownCast(outInfo0->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* outputIntersection = vtkPolyData::SafeDownCast(outInfo1->Get(vtkDataObject::DATA_OBJECT())); if (!input0 || !input1 || !outputSurface || !outputIntersection) { return 0; } // Get intersected versions vtkSmartPointer PolyDataIntersection = vtkSmartPointer::New(); PolyDataIntersection->SetInputConnection(0, this->GetInputConnection(0, 0)); PolyDataIntersection->SetInputConnection(1, this->GetInputConnection(1, 0)); PolyDataIntersection->SplitFirstOutputOn(); PolyDataIntersection->SplitSecondOutputOn(); PolyDataIntersection->SetContainerAlgorithm(this); PolyDataIntersection->Update(); if (PolyDataIntersection->GetStatus() != 1) { return 0; } outputIntersection->CopyStructure(PolyDataIntersection->GetOutput()); outputIntersection->GetPointData()->PassData(PolyDataIntersection->GetOutput()->GetPointData()); outputIntersection->GetCellData()->PassData(PolyDataIntersection->GetOutput()->GetCellData()); // Compute distances vtkSmartPointer PolyDataDistance = vtkSmartPointer::New(); PolyDataDistance->SetInputConnection(0, PolyDataIntersection->GetOutputPort(1)); PolyDataDistance->SetInputConnection(1, PolyDataIntersection->GetOutputPort(2)); PolyDataDistance->ComputeSecondDistanceOn(); PolyDataDistance->SetContainerAlgorithm(this); PolyDataDistance->Update(); vtkPolyData* pd0 = PolyDataDistance->GetOutput(); vtkPolyData* pd1 = PolyDataDistance->GetSecondDistanceOutput(); pd0->BuildCells(); pd0->BuildLinks(); pd1->BuildCells(); pd1->BuildLinks(); // Set up field lists of both points and cells that are shared by // the input data sets. vtkDataSetAttributes::FieldList pointFields(2); pointFields.InitializeFieldList(pd0->GetPointData()); pointFields.IntersectFieldList(pd1->GetPointData()); vtkDataSetAttributes::FieldList cellFields(2); cellFields.InitializeFieldList(pd0->GetCellData()); cellFields.IntersectFieldList(pd1->GetCellData()); // Sort union/intersection. vtkSmartPointer interList = vtkSmartPointer::New(); vtkSmartPointer unionList = vtkSmartPointer::New(); this->SortPolyData(pd0, interList, unionList); outputSurface->AllocateCopy(pd0); outputSurface->GetPointData()->CopyAllocate(pointFields); outputSurface->GetCellData()->CopyAllocate(cellFields); if (this->Operation == VTK_UNION || this->Operation == VTK_DIFFERENCE) { this->CopyCells(pd0, outputSurface, 0, pointFields, cellFields, unionList, false); } else if (this->Operation == VTK_INTERSECTION) { this->CopyCells(pd0, outputSurface, 0, pointFields, cellFields, interList, false); } // Label sources for each point and cell. vtkSmartPointer pointSourceLabel = vtkSmartPointer::New(); pointSourceLabel->SetNumberOfComponents(1); pointSourceLabel->SetName("PointSource"); pointSourceLabel->SetNumberOfTuples(outputSurface->GetNumberOfPoints()); for (vtkIdType ii = 0; ii < outputSurface->GetNumberOfPoints(); ii++) { pointSourceLabel->InsertValue(ii, 0); } vtkSmartPointer cellSourceLabel = vtkSmartPointer::New(); cellSourceLabel->SetNumberOfComponents(1); cellSourceLabel->SetName("CellSource"); cellSourceLabel->SetNumberOfValues(outputSurface->GetNumberOfCells()); for (vtkIdType ii = 0; ii < outputSurface->GetNumberOfCells(); ii++) { cellSourceLabel->InsertValue(ii, 0); } interList->Reset(); unionList->Reset(); this->SortPolyData(pd1, interList, unionList); if (this->Operation == VTK_UNION) { this->CopyCells(pd1, outputSurface, 1, pointFields, cellFields, unionList, false); } else if (this->Operation == VTK_INTERSECTION || this->Operation == VTK_DIFFERENCE) { this->CopyCells(pd1, outputSurface, 1, pointFields, cellFields, interList, (this->ReorientDifferenceCells == 1 && this->Operation == VTK_DIFFERENCE)); } vtkIdType i; i = pointSourceLabel->GetNumberOfTuples(); pointSourceLabel->Resize(outputSurface->GetNumberOfPoints()); for (; i < outputSurface->GetNumberOfPoints(); i++) { pointSourceLabel->InsertValue(i, 1); } i = cellSourceLabel->GetNumberOfTuples(); cellSourceLabel->Resize(outputSurface->GetNumberOfCells()); for (; i < outputSurface->GetNumberOfCells(); i++) { cellSourceLabel->InsertValue(i, 1); } outputSurface->GetPointData()->AddArray(pointSourceLabel); outputSurface->GetCellData()->AddArray(cellSourceLabel); outputSurface->Squeeze(); outputSurface->GetPointData()->Squeeze(); outputSurface->GetCellData()->Squeeze(); return 1; } //------------------------------------------------------------------------------ void vtkBooleanOperationPolyDataFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Tolerance: " << this->Tolerance << "\n"; os << indent << "Operation: "; switch (this->Operation) { case VTK_UNION: os << "UNION"; break; case VTK_INTERSECTION: os << "INTERSECTION"; break; case VTK_DIFFERENCE: os << "DIFFERENCE"; break; } os << "\n"; os << indent << "ReorientDifferenceCells: " << this->ReorientDifferenceCells << "\n"; } //------------------------------------------------------------------------------ int vtkBooleanOperationPolyDataFilter::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; } //------------------------------------------------------------------------------ void vtkBooleanOperationPolyDataFilter ::CopyCells(vtkPolyData* in, vtkPolyData* out, int idx, vtkDataSetAttributes::FieldList& pointFieldList, vtkDataSetAttributes::FieldList& cellFieldList, vtkIdList* cellIds, bool reverseCells) { // Largely copied from vtkPolyData::CopyCells, but modified to // use the special form of CopyData that uses a field list to // determine which data values to copy over. vtkPointData* outPD = out->GetPointData(); vtkCellData* outCD = out->GetCellData(); vtkFloatArray* outNormals = nullptr; if (reverseCells) { outNormals = vtkArrayDownCast(outPD->GetArray("Normals")); } vtkIdType numPts = in->GetNumberOfPoints(); if (out->GetPoints() == nullptr) { vtkSmartPointer points = vtkSmartPointer::New(); out->SetPoints(points); } vtkPoints* newPoints = out->GetPoints(); vtkSmartPointer pointMap = vtkSmartPointer::New(); pointMap->SetNumberOfIds(numPts); for (vtkIdType i = 0; i < numPts; i++) { pointMap->SetId(i, -1); } // Filter the cells vtkSmartPointer cell = vtkSmartPointer::New(); vtkSmartPointer newCellPts = vtkSmartPointer::New(); for (vtkIdType cellId = 0; cellId < cellIds->GetNumberOfIds(); cellId++) { if (this->CheckAbort()) { break; } in->GetCell(cellIds->GetId(cellId), cell); vtkIdList* cellPts = cell->GetPointIds(); vtkIdType numCellPts = cell->GetNumberOfPoints(); for (vtkIdType i = 0; i < numCellPts; i++) { vtkIdType ptId = cellPts->GetId(i); vtkIdType newId = pointMap->GetId(ptId); if (newId < 0) { double x[3]; in->GetPoint(ptId, x); newId = newPoints->InsertNextPoint(x); pointMap->SetId(ptId, newId); outPD->CopyData(pointFieldList, in->GetPointData(), idx, ptId, newId); if (reverseCells && outNormals) { float normal[3]; outNormals->GetTypedTuple(newId, normal); normal[0] *= -1.0; normal[1] *= -1.0; normal[2] *= -1.0; outNormals->SetTypedTuple(newId, normal); } } newCellPts->InsertId(i, newId); } if (reverseCells) { for (vtkIdType i = 0; i < newCellPts->GetNumberOfIds() / 2; i++) { vtkIdType i1 = i; vtkIdType i2 = newCellPts->GetNumberOfIds() - i - 1; vtkIdType id = newCellPts->GetId(i1); newCellPts->SetId(i1, newCellPts->GetId(i2)); newCellPts->SetId(i2, id); } } vtkIdType newCellId = out->InsertNextCell(cell->GetCellType(), newCellPts); outCD->CopyData(cellFieldList, in->GetCellData(), idx, cellIds->GetId(cellId), newCellId); newCellPts->Reset(); } // for all cells } VTK_ABI_NAMESPACE_END