// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkGenericClip.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkDoubleArray.h" #include "vtkExecutive.h" #include "vtkGenericAdaptorCell.h" #include "vtkGenericAttribute.h" #include "vtkGenericAttributeCollection.h" #include "vtkGenericCell.h" #include "vtkGenericCellIterator.h" #include "vtkGenericCellTessellator.h" #include "vtkGenericDataSet.h" #include "vtkGenericPointIterator.h" #include "vtkIdTypeArray.h" #include "vtkImageData.h" #include "vtkImplicitFunction.h" #include "vtkIncrementalPointLocator.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMergePoints.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" #include #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkGenericClip); vtkCxxSetObjectMacro(vtkGenericClip, ClipFunction, vtkImplicitFunction); vtkCxxSetObjectMacro(vtkGenericClip, Locator, vtkIncrementalPointLocator); //------------------------------------------------------------------------------ // Construct with user-specified implicit function; InsideOut turned off; value // set to 0.0; and generate clip scalars turned off. vtkGenericClip::vtkGenericClip(vtkImplicitFunction* cf) { this->ClipFunction = cf; this->InsideOut = 0; this->Locator = nullptr; this->Value = 0.0; this->GenerateClipScalars = 0; this->GenerateClippedOutput = 0; this->MergeTolerance = 0.01; this->SetNumberOfOutputPorts(2); vtkUnstructuredGrid* output2 = vtkUnstructuredGrid::New(); this->GetExecutive()->SetOutputData(1, output2); output2->Delete(); this->InputScalarsSelection = nullptr; this->InternalPD = vtkPointData::New(); this->SecondaryPD = vtkPointData::New(); this->SecondaryCD = vtkCellData::New(); } //------------------------------------------------------------------------------ vtkGenericClip::~vtkGenericClip() { if (this->Locator) { this->Locator->UnRegister(this); this->Locator = nullptr; } this->SetClipFunction(nullptr); this->SetInputScalarsSelection(nullptr); this->InternalPD->Delete(); this->SecondaryPD->Delete(); this->SecondaryCD->Delete(); } //------------------------------------------------------------------------------ // Do not say we have two outputs unless we are generating the clipped output. int vtkGenericClip::GetNumberOfOutputs() { if (this->GenerateClippedOutput) { return 2; } return 1; } //------------------------------------------------------------------------------ // Overload standard modified time function. If Clip functions is modified, // then this object is modified as well. vtkMTimeType vtkGenericClip::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); vtkMTimeType time; if (this->ClipFunction != nullptr) { time = this->ClipFunction->GetMTime(); mTime = (time > mTime ? time : mTime); } if (this->Locator != nullptr) { time = this->Locator->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } //------------------------------------------------------------------------------ vtkUnstructuredGrid* vtkGenericClip::GetClippedOutput() { if (!this->GenerateClippedOutput) { return nullptr; } return vtkUnstructuredGrid::SafeDownCast(this->GetExecutive()->GetOutputData(1)); } //------------------------------------------------------------------------------ // // Clip through data generating surface. // int vtkGenericClip::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the info objects vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); // get the input and output vtkGenericDataSet* input = vtkGenericDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); if (input == nullptr) { return 1; } vtkUnstructuredGrid* clippedOutput = this->GetClippedOutput(); vtkIdType numPts = input->GetNumberOfPoints(); vtkIdType numCells = input->GetNumberOfCells(); vtkPointData* outPD = output->GetPointData(); vtkCellData* outCD[2]; vtkIdType npts = 0; const vtkIdType* pts; int cellType = 0; int j; vtkIdType estimatedSize; vtkUnsignedCharArray* types[2]; int numOutputs = 1; vtkGenericAdaptorCell* cell; outCD[0] = nullptr; outCD[1] = nullptr; vtkDebugMacro(<< "Clipping dataset"); // Initialize self; create output objects // if (numPts < 1) { vtkErrorMacro(<< "No data to clip"); return 1; } if (!this->ClipFunction && this->GenerateClipScalars) { vtkErrorMacro(<< "Cannot generate clip scalars if no clip function defined"); return 1; } // allocate the output and associated helper classes estimatedSize = numCells; estimatedSize = estimatedSize / 1024 * 1024; // multiple of 1024 estimatedSize = std::max(estimatedSize, 1024); vtkPoints* newPoints = vtkPoints::New(); newPoints->Allocate(numPts, numPts / 2); vtkCellArray* conn[2]; conn[0] = vtkCellArray::New(); conn[0]->AllocateEstimate(estimatedSize, 1); conn[0]->InitTraversal(); types[0] = vtkUnsignedCharArray::New(); types[0]->Allocate(estimatedSize, estimatedSize / 2); if (this->GenerateClippedOutput) { numOutputs = 2; conn[1] = vtkCellArray::New(); conn[1]->AllocateEstimate(estimatedSize, 1); conn[1]->InitTraversal(); types[1] = vtkUnsignedCharArray::New(); types[1]->Allocate(estimatedSize, estimatedSize / 2); } // locator used to merge potentially duplicate points if (this->Locator == nullptr) { this->CreateDefaultLocator(); } this->Locator->InitPointInsertion(newPoints, input->GetBounds()); // prepare the output attributes vtkGenericAttributeCollection* attributes = input->GetAttributes(); vtkGenericAttribute* attribute; vtkDataArray* attributeArray; int c = attributes->GetNumberOfAttributes(); vtkDataSetAttributes* secondaryAttributes; int attributeType; vtkIdType i; for (i = 0; i < c; ++i) { attribute = attributes->GetAttribute(i); attributeType = attribute->GetType(); if (attribute->GetCentering() == vtkPointCentered) { secondaryAttributes = this->SecondaryPD; attributeArray = vtkDataArray::CreateDataArray(attribute->GetComponentType()); attributeArray->SetNumberOfComponents(attribute->GetNumberOfComponents()); attributeArray->SetName(attribute->GetName()); this->InternalPD->AddArray(attributeArray); attributeArray->Delete(); if (this->InternalPD->GetAttribute(attributeType) == nullptr) { this->InternalPD->SetActiveAttribute( this->InternalPD->GetNumberOfArrays() - 1, attributeType); } } else // vtkCellCentered { secondaryAttributes = this->SecondaryCD; } attributeArray = vtkDataArray::CreateDataArray(attribute->GetComponentType()); attributeArray->SetNumberOfComponents(attribute->GetNumberOfComponents()); attributeArray->SetName(attribute->GetName()); secondaryAttributes->AddArray(attributeArray); attributeArray->Delete(); if (secondaryAttributes->GetAttribute(attributeType) == nullptr) { secondaryAttributes->SetActiveAttribute( secondaryAttributes->GetNumberOfArrays() - 1, attributeType); } } outPD->InterpolateAllocate(this->SecondaryPD, estimatedSize, estimatedSize / 2); outCD[0] = output->GetCellData(); outCD[0]->CopyAllocate(this->SecondaryCD, estimatedSize, estimatedSize / 2); if (this->GenerateClippedOutput) { outCD[1] = clippedOutput->GetCellData(); outCD[1]->CopyAllocate(this->SecondaryCD, estimatedSize, estimatedSize / 2); } // vtkGenericPointIterator *pointIt = input->GetPoints(); vtkGenericCellIterator* cellIt = input->NewCellIterator(); // explicit cell could be 2D or 3D // Process all cells and clip each in turn // bool abort = false; vtkIdType updateTime = numCells / 20 + 1; // update roughly every 5% int num[2]; num[0] = num[1] = 0; int numNew[2]; numNew[0] = numNew[1] = 0; vtkIdType cellId; input->GetTessellator()->InitErrorMetrics(input); for (cellId = 0, cellIt->Begin(); !cellIt->IsAtEnd() && !abort; cellId++, cellIt->Next()) { cell = cellIt->GetCell(); if (!(cellId % updateTime)) { this->UpdateProgress(static_cast(cellId) / numCells); abort = this->CheckAbort(); } // perform the clipping cell->Clip(this->Value, this->ClipFunction, input->GetAttributes(), input->GetTessellator(), this->InsideOut, this->Locator, conn[0], outPD, outCD[0], this->InternalPD, this->SecondaryPD, this->SecondaryCD); numNew[0] = conn[0]->GetNumberOfCells() - num[0]; num[0] = conn[0]->GetNumberOfCells(); if (this->GenerateClippedOutput) { cell->Clip(this->Value, this->ClipFunction, input->GetAttributes(), input->GetTessellator(), this->InsideOut, this->Locator, conn[1], outPD, outCD[1], this->InternalPD, this->SecondaryPD, this->SecondaryCD); numNew[1] = conn[1]->GetNumberOfCells() - num[1]; num[1] = conn[1]->GetNumberOfCells(); } for (i = 0; i < numOutputs; i++) // for both outputs { for (j = 0; j < numNew[i]; j++) { conn[i]->GetNextCell(npts, pts); // For each new cell added, got to set the type of the cell switch (cell->GetDimension()) { case 0: // points are generated-------------------------------- cellType = (npts > 1 ? VTK_POLY_VERTEX : VTK_VERTEX); break; case 1: // lines are generated--------------------------------- cellType = (npts > 2 ? VTK_POLY_LINE : VTK_LINE); break; case 2: // polygons are generated------------------------------ if (npts == 3) { cellType = VTK_TRIANGLE; } else if (npts == 4) { cellType = VTK_QUAD; } else { cellType = VTK_POLYGON; } break; case 3: // tetrahedra or wedges are generated------------------ cellType = (npts == 4 ? VTK_TETRA : VTK_WEDGE); break; } // switch types[i]->InsertNextValue(cellType); } // for each new cell } // for both outputs } // for each cell cellIt->Delete(); output->SetPoints(newPoints); output->SetCells(types[0], conn[0]); conn[0]->Delete(); types[0]->Delete(); if (this->GenerateClippedOutput) { clippedOutput->SetPoints(newPoints); clippedOutput->SetCells(types[1], conn[1]); conn[1]->Delete(); types[1]->Delete(); } newPoints->Delete(); this->Locator->Initialize(); // release any extra memory output->Squeeze(); return 1; } //------------------------------------------------------------------------------ // Specify a spatial locator for merging points. By default, // an instance of vtkMergePoints is used. void vtkGenericClip::CreateDefaultLocator() { if (this->Locator == nullptr) { this->Locator = vtkMergePoints::New(); this->Locator->Register(this); this->Locator->Delete(); } } //------------------------------------------------------------------------------ void vtkGenericClip::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Merge Tolerance: " << this->MergeTolerance << "\n"; if (this->ClipFunction) { os << indent << "Clip Function: " << this->ClipFunction << "\n"; } else { os << indent << "Clip Function: (none)\n"; } os << indent << "InsideOut: " << (this->InsideOut ? "On\n" : "Off\n"); os << indent << "Value: " << this->Value << "\n"; if (this->Locator) { os << indent << "Locator: " << this->Locator << "\n"; } else { os << indent << "Locator: (none)\n"; } os << indent << "Generate Clip Scalars: " << (this->GenerateClipScalars ? "On\n" : "Off\n"); os << indent << "Generate Clipped Output: " << (this->GenerateClippedOutput ? "On\n" : "Off\n"); if (this->InputScalarsSelection) { os << indent << "InputScalarsSelection: " << this->InputScalarsSelection << endl; } } //------------------------------------------------------------------------------ int vtkGenericClip::FillInputPortInformation(int port, vtkInformation* info) { if (!this->Superclass::FillInputPortInformation(port, info)) { return 0; } info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGenericDataSet"); return 1; } VTK_ABI_NAMESPACE_END