// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkPOutlineFilterInternals.h" #include "vtkAppendPolyData.h" #include "vtkBoundingBox.h" #include "vtkCartesianGrid.h" #include "vtkCompositeDataIterator.h" #include "vtkDataObjectTree.h" #include "vtkGraph.h" #include "vtkMath.h" #include "vtkMultiProcessController.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkOutlineCornerSource.h" #include "vtkOutlineSource.h" #include "vtkOverlappingAMR.h" #include "vtkOverlappingAMRMetaData.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" //------------------------------------------------------------------------------ VTK_ABI_NAMESPACE_BEGIN class AddBoundsListOperator : public vtkCommunicator::Operation { // Description: // Performs a "B.AddBounds(A)" operation. void Function(const void* A, void* B, vtkIdType length, int datatype) override { (void)datatype; assert((datatype == VTK_DOUBLE) && (length % 6 == 0)); assert("pre: A vector is nullptr" && (A != nullptr)); assert("pre: B vector is nullptr" && (B != nullptr)); vtkBoundingBox box; const double* aPtr = reinterpret_cast(A); double* bPtr = reinterpret_cast(B); for (vtkIdType idx = 0; idx < length; idx += 6) { box.SetBounds(&bPtr[idx]); box.AddBounds(&aPtr[idx]); box.GetBounds(&bPtr[idx]); } } // Description: // Sets Commutative to true for this operation int Commutative() override { return 1; } }; //------------------------------------------------------------------------------ void vtkPOutlineFilterInternals::SetController(vtkMultiProcessController* controller) { this->Controller = controller; } //------------------------------------------------------------------------------ void vtkPOutlineFilterInternals::SetCornerFactor(double cornerFactor) { this->CornerFactor = cornerFactor; } //------------------------------------------------------------------------------ void vtkPOutlineFilterInternals::SetIsCornerSource(bool value) { this->IsCornerSource = value; } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkDataObject* input = vtkDataObject::GetData(inputVector[0], 0); vtkPolyData* output = vtkPolyData::GetData(outputVector, 0); if (input == nullptr || output == nullptr) { vtkGenericWarningMacro("Missing input or output."); return 0; } if (this->Controller == nullptr) { vtkGenericWarningMacro("Missing Controller."); return 0; } vtkOverlappingAMR* oamr = vtkOverlappingAMR::SafeDownCast(input); if (oamr) { return this->RequestData(oamr, output); } vtkAMRDataObject* amr = vtkAMRDataObject::SafeDownCast(input); if (amr) { return this->RequestData(amr, output); } vtkDataObjectTree* cd = vtkDataObjectTree::SafeDownCast(input); if (cd) { return this->RequestData(cd, output); } vtkDataSet* ds = vtkDataSet::SafeDownCast(input); if (ds) { return this->RequestData(ds, output); } vtkGraph* graph = vtkGraph::SafeDownCast(input); if (graph) { return this->RequestData(graph, output); } return 0; } //------------------------------------------------------------------------------ void vtkPOutlineFilterInternals::CollectCompositeBounds(vtkDataObject* input) { vtkDataSet* ds = vtkDataSet::SafeDownCast(input); vtkCompositeDataSet* compInput = vtkCompositeDataSet::SafeDownCast(input); if (ds != nullptr) { double bounds[6]; ds->GetBounds(bounds); this->BoundsList.emplace_back(bounds); } else if (compInput != nullptr) { vtkCompositeDataIterator* iter = compInput->NewIterator(); iter->SkipEmptyNodesOff(); iter->GoToFirstItem(); for (; !iter->IsDoneWithTraversal(); iter->GoToNextItem()) { this->CollectCompositeBounds(iter->GetCurrentDataObject()); } iter->Delete(); } else { double bounds[6]; vtkMath::UninitializeBounds(bounds); this->BoundsList.emplace_back(bounds); } } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkDataObjectTree* input, vtkPolyData* output) { // Collect local bounds. this->CollectCompositeBounds(input); // Make an array of bounds from collected bounds std::vector boundsList; boundsList.resize(6 * this->BoundsList.size()); for (size_t i = 0; i < this->BoundsList.size(); ++i) { this->BoundsList[i].GetBounds(&boundsList[i * 6]); } // Collect global bounds and copy into the array if (this->Controller && this->Controller->GetNumberOfProcesses() > 1) { AddBoundsListOperator operation; double* temp = new double[6 * this->BoundsList.size()]; this->Controller->Reduce( boundsList.data(), temp, static_cast(6 * this->BoundsList.size()), &operation, 0); memcpy(boundsList.data(), temp, 6 * this->BoundsList.size() * sizeof(double)); delete[] temp; if (this->Controller->GetLocalProcessId() > 0) { // only root node will produce the output. return 1; } } // Make output with collected bounds vtkNew appender; for (size_t i = 0; i < 6 * this->BoundsList.size(); i += 6) { appender->AddInputData(this->GenerateOutlineGeometry(&boundsList[i])); } appender->Update(); output->ShallowCopy(appender->GetOutput()); return 1; } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkOverlappingAMR* input, vtkPolyData* output) { // For Overlapping AMR, we have meta-data on all processes for the complete // AMR structure. Hence root node can build the outlines using that // meta-data, itself. int procid = this->Controller->GetLocalProcessId(); if (procid != 0) { // we only generate output on the root node. return 1; } vtkNew appender; for (unsigned int level = 0; level < input->GetNumberOfLevels(); ++level) { unsigned int num_datasets = input->GetNumberOfBlocks(level); for (unsigned int dataIdx = 0; dataIdx < num_datasets; ++dataIdx) { double bounds[6]; input->GetBounds(level, dataIdx, bounds); appender->AddInputData(this->GenerateOutlineGeometry(bounds)); } } appender->Update(); output->ShallowCopy(appender->GetOutput()); return 1; } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkAMRDataObject* input, vtkPolyData* output) { // All processes simply produce the outline for the non-null blocks that exist // on the process. vtkNew appender; unsigned int block_id = 0; for (unsigned int level = 0; level < input->GetNumberOfLevels(); ++level) { unsigned int num_datasets = input->GetNumberOfBlocks(level); for (unsigned int dataIdx = 0; dataIdx < num_datasets; ++dataIdx, block_id++) { vtkCartesianGrid* cg = input->GetDataSetAsCartesianGrid(level, dataIdx); if (cg) { double bounds[6]; cg->GetBounds(bounds); appender->AddInputData(this->GenerateOutlineGeometry(bounds)); } } } appender->Update(); output->ShallowCopy(appender->GetOutput()); return 1; } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkDataSet* input, vtkPolyData* output) { double bounds[6]; input->GetBounds(bounds); if (this->Controller->GetNumberOfProcesses() > 1) { double reduced_bounds[6]; int procid = this->Controller->GetLocalProcessId(); AddBoundsListOperator operation; this->Controller->Reduce(bounds, reduced_bounds, 6, &operation, 0); if (procid > 0) { // Satellite node return 1; } memcpy(bounds, reduced_bounds, 6 * sizeof(double)); } output->ShallowCopy(this->GenerateOutlineGeometry(bounds)); return 1; } //------------------------------------------------------------------------------ int vtkPOutlineFilterInternals::RequestData(vtkGraph* input, vtkPolyData* output) { double bounds[6]; input->GetBounds(bounds); if (this->Controller->GetNumberOfProcesses() > 1) { double reduced_bounds[6]; int procid = this->Controller->GetLocalProcessId(); AddBoundsListOperator operation; this->Controller->Reduce(bounds, reduced_bounds, 6, &operation, 0); if (procid > 0) { // Satellite node return 1; } memcpy(bounds, reduced_bounds, 6 * sizeof(double)); } output->ShallowCopy(this->GenerateOutlineGeometry(bounds)); return 1; } //------------------------------------------------------------------------------ vtkSmartPointer vtkPOutlineFilterInternals::GenerateOutlineGeometry(double bounds[6]) { vtkSmartPointer output = nullptr; if (vtkMath::AreBoundsInitialized(bounds)) { if (this->IsCornerSource) { vtkNew corner; corner->SetBounds(bounds); corner->SetCornerFactor(this->CornerFactor); corner->Update(); output = corner->GetOutput(); } else { vtkNew corner; corner->SetBounds(bounds); corner->Update(); output = corner->GetOutput(); } } return output; } VTK_ABI_NAMESPACE_END