/*========================================================================= Program: Visualization Toolkit Module: vtkDataAssemblyUtilities.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkDataAssemblyUtilities.h" #include "vtkDataAssembly.h" #include "vtkDataAssemblyVisitor.h" #include "vtkDataObjectTypes.h" #include "vtkInformation.h" #include "vtkLogger.h" #include "vtkMultiBlockDataSet.h" #include "vtkMultiPieceDataSet.h" #include "vtkObjectFactory.h" #include "vtkPartitionedDataSetCollection.h" #include "vtkSMPTools.h" #include "vtkUniformGrid.h" #include "vtkUniformGridAMR.h" #include #include #include #include #include #include #include namespace { std::pair GetBlockNameAndLabel( vtkInformation* metadata, const std::string& defaultName) { if (metadata && metadata->Has(vtkCompositeDataSet::NAME()) && metadata->Get(vtkCompositeDataSet::NAME())) { std::string label = metadata->Get(vtkCompositeDataSet::NAME()); std::string name = vtkDataAssembly::MakeValidNodeName(label.c_str()); return std::make_pair(name, label); } else { return std::make_pair(defaultName, std::string()); } } // String used as the attribute name for data assembly nodes to identify // data-assembly instances that represent a hierarchy. constexpr const char* CATEGORY_ATTRIBUTE_NAME = "vtk_category"; // Value used for CATEGORY_ATTRIBUTE_NAME attribute on the generate data assembly // when it represents the hierarchy for the input dataset. constexpr const char* CATEGORY_HIERARCHY = "hierarchy"; // Value used for CATEGORY_ATTRIBUTE_NAME attribute on the generated data assembly // when the data assembly representation a hierarchy for the input with dataset indices // pointing to the transformed `vtkPartitionedDataSetCollection` rather than the input. constexpr const char* CATEGORY_TRANSFORMED_HIERARCHY = "xformed_hierarchy"; } vtkStandardNewMacro(vtkDataAssemblyUtilities); //---------------------------------------------------------------------------- vtkDataAssemblyUtilities::vtkDataAssemblyUtilities() = default; //---------------------------------------------------------------------------- vtkDataAssemblyUtilities::~vtkDataAssemblyUtilities() = default; //---------------------------------------------------------------------------- void vtkDataAssemblyUtilities::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } //---------------------------------------------------------------------------- vtkSmartPointer vtkDataAssemblyUtilities::GetDataAssembly( const char* name, vtkCompositeDataSet* cd) { if (name == nullptr || cd == nullptr) { return nullptr; } else if (strcmp(name, vtkDataAssemblyUtilities::HierarchyName()) == 0) { vtkNew assembly; if (vtkDataAssemblyUtilities::GenerateHierarchy(cd, assembly)) { return assembly; } } else if (auto pdc = vtkPartitionedDataSetCollection::SafeDownCast(cd)) { // TODO: use name here; we don't support named assemblies yet on PDC. return pdc->GetDataAssembly(); } return nullptr; } //---------------------------------------------------------------------------- bool vtkDataAssemblyUtilities::GenerateHierarchy(vtkCompositeDataSet* input, vtkDataAssembly* hierarchy, vtkPartitionedDataSetCollection* output /*=nullptr*/) { if (hierarchy == nullptr || input == nullptr) { return false; } hierarchy->Initialize(); if (output) { output->Initialize(); } if (auto amr = vtkUniformGridAMR::SafeDownCast(input)) { return vtkDataAssemblyUtilities::GenerateHierarchyInternal(amr, hierarchy, output); } else if (auto mb = vtkMultiBlockDataSet::SafeDownCast(input)) { return vtkDataAssemblyUtilities::GenerateHierarchyInternal(mb, hierarchy, output); } else if (auto pdc = vtkPartitionedDataSetCollection::SafeDownCast(input)) { return vtkDataAssemblyUtilities::GenerateHierarchyInternal(pdc, hierarchy, output); } return false; } //---------------------------------------------------------------------------- bool vtkDataAssemblyUtilities::GenerateHierarchyInternal( vtkUniformGridAMR* amr, vtkDataAssembly* hierarchy, vtkPartitionedDataSetCollection* output) { assert(amr != nullptr && hierarchy != nullptr); hierarchy->SetRootNodeName("Root"); hierarchy->SetAttribute(vtkDataAssembly::GetRootNode(), "vtk_type", amr->GetDataObjectType()); hierarchy->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_HIERARCHY); hierarchy->SetAttribute(vtkDataAssembly::GetRootNode(), "label", amr->GetClassName()); if (output) { output->SetNumberOfPartitionedDataSets(amr->GetNumberOfLevels()); } std::map output_node2dataset_map; for (unsigned int level = 0, numLevels = amr->GetNumberOfLevels(); level < numLevels; ++level) { const auto label = "Level " + std::to_string(level); const auto name = vtkDataAssembly::MakeValidNodeName(label.c_str()); auto node = hierarchy->AddNode(name.c_str()); hierarchy->SetAttribute(node, "label", label.c_str()); hierarchy->SetAttribute(node, "amr_level", level); const auto numDataSets = amr->GetNumberOfDataSets(level); if (numDataSets > 0) { // since a level doesn't have a composite index (see vtkAMRInformation::GetIndex), // we add composite indices for all datasets within a level. hierarchy->AddDataSetIndex(node, amr->GetCompositeIndex(level, 0)); } hierarchy->SetAttribute(node, "number_of_datasets", numDataSets); if (output) { output_node2dataset_map[node] = level; output->SetNumberOfPartitions(level, numDataSets); for (unsigned int cc = 0; cc < numDataSets; ++cc) { output->SetPartition(level, cc, amr->GetDataSet(level, cc)); } } } if (output) { // if output is non-null, create a vtkDataAssembly that represents the // hierarchy for the input and update dataset indices in it to point to the // partitioned-dataset index in the output. vtkNew clone; clone->DeepCopy(hierarchy); clone->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_TRANSFORMED_HIERARCHY); clone->RemoveAllDataSetIndices(0, /*traverse_subtree=*/true); for (auto& pair : output_node2dataset_map) { clone->AddDataSetIndex(pair.first, pair.second); } output->SetDataAssembly(clone); } return true; } //---------------------------------------------------------------------------- bool vtkDataAssemblyUtilities::GenerateHierarchyInternal( vtkMultiBlockDataSet* input, vtkDataAssembly* hierarchy, vtkPartitionedDataSetCollection* output) { assert(input != nullptr && hierarchy != nullptr); std::map output_node2dataset_map; auto appendToOutput = [&](vtkDataObject* dobj, vtkInformation* metadata, int nodeid) { if (!output) { return; } const auto oid = output->GetNumberOfPartitionedDataSets(); output->SetNumberOfPartitionedDataSets(oid + 1); output_node2dataset_map[nodeid] = oid; if (metadata) { output->GetMetaData(oid)->Copy(metadata); } if (auto pdc = vtkPartitionedDataSetCollection::SafeDownCast(dobj)) { output->GetPartitionedDataSet(oid)->ShallowCopy(pdc); } else if (auto mp = vtkMultiPieceDataSet::SafeDownCast(dobj)) { auto pd = output->GetPartitionedDataSet(oid); for (unsigned int cc = 0; cc < mp->GetNumberOfPieces(); ++cc) { pd->SetPartition(pd->GetNumberOfPartitions(), mp->GetPieceAsDataObject(cc)); } } else if (dobj) { output->SetPartition(oid, 0, dobj); } }; unsigned int cid = 0; std::function f; f = [&](vtkDataObject* dobj, int nodeid, vtkInformation* dobjMetaData) { // in a hierarchy, the dataset-index corresponds to the composite index; // we add the "cid" attribute, however, to enable users to build selectors // using cid. hierarchy->SetAttribute(nodeid, "cid", cid); hierarchy->AddDataSetIndex(nodeid, cid); ++cid; if (auto mb = vtkMultiBlockDataSet::SafeDownCast(dobj)) { hierarchy->SetAttribute(nodeid, "vtk_type", dobj->GetDataObjectType()); for (unsigned int bidx = 0, numBlocks = mb->GetNumberOfBlocks(); bidx < numBlocks; ++bidx) { auto metadata = mb->HasMetaData(bidx) ? mb->GetMetaData(bidx) : nullptr; std::string label, name; std::tie(name, label) = ::GetBlockNameAndLabel(metadata, "Block" + std::to_string(bidx)); auto child = hierarchy->AddNode(name.c_str(), nodeid); if (!label.empty()) { hierarchy->SetAttribute(child, "label", label.c_str()); } auto block = mb->GetBlock(bidx); f(block, child, metadata); } } else if (auto mp = vtkMultiPieceDataSet::SafeDownCast(dobj)) { hierarchy->SetAttribute(nodeid, "vtk_type", dobj->GetDataObjectType()); hierarchy->SetAttribute(nodeid, "vtk_num_pieces", mp->GetNumberOfPieces()); appendToOutput(mp, dobjMetaData, nodeid); cid += mp->GetNumberOfPieces(); } else { assert(vtkCompositeDataSet::SafeDownCast(dobj) == nullptr); // a leaf node. appendToOutput(dobj, dobjMetaData, nodeid); } }; hierarchy->SetRootNodeName("Root"); hierarchy->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_HIERARCHY); hierarchy->SetAttribute(vtkDataAssembly::GetRootNode(), "label", input->GetClassName()); f(input, vtkDataAssembly::GetRootNode(), nullptr); if (output) { // if output is non-null, create a vtkDataAssembly that represents the // hierarchy for the input and update dataset indices in it to point to the // partitioned-dataset index in the output. vtkNew clone; clone->DeepCopy(hierarchy); clone->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_TRANSFORMED_HIERARCHY); clone->RemoveAllDataSetIndices(0, /*traverse_subtree=*/true); for (auto& pair : output_node2dataset_map) { clone->AddDataSetIndex(pair.first, pair.second); } output->SetDataAssembly(clone); } return true; } //---------------------------------------------------------------------------- bool vtkDataAssemblyUtilities::GenerateHierarchyInternal(vtkPartitionedDataSetCollection* input, vtkDataAssembly* hierarchy, vtkPartitionedDataSetCollection* output) { assert(input != nullptr && hierarchy != nullptr); if (output) { output->ShallowCopy(input); } std::map output_node2dataset_map; unsigned int cid = 0; hierarchy->SetRootNodeName("Root"); hierarchy->SetAttribute(vtkDataAssembly::GetRootNode(), "vtk_type", input->GetDataObjectType()); hierarchy->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_HIERARCHY); hierarchy->SetAttribute(vtkDataAssembly::GetRootNode(), "label", input->GetClassName()); hierarchy->AddDataSetIndex(vtkDataAssembly::GetRootNode(), cid++); for (unsigned int p = 0; p < input->GetNumberOfPartitionedDataSets(); ++p) { auto metadata = input->HasMetaData(p) ? input->GetMetaData(p) : nullptr; std::string name, label; std::tie(name, label) = ::GetBlockNameAndLabel(metadata, "Block" + std::to_string(p)); auto node = hierarchy->AddNode(name.c_str()); // dataset index in a hierarchy represents the composite index. hierarchy->AddDataSetIndex(node, cid++); if (!label.empty()) { hierarchy->SetAttribute(node, "label", label.c_str()); } hierarchy->SetAttribute(node, "number_of_partitions", input->GetNumberOfPartitions(p)); if (output) { output_node2dataset_map[node] = p; } cid += input->GetNumberOfPartitions(p); } if (output) { // if output is non-null, create a vtkDataAssembly that represents the // hierarchy for the input and update dataset indices in it to point to the // partitioned-dataset index in the output. vtkNew clone; clone->DeepCopy(hierarchy); clone->SetAttribute( vtkDataAssembly::GetRootNode(), CATEGORY_ATTRIBUTE_NAME, CATEGORY_TRANSFORMED_HIERARCHY); clone->RemoveAllDataSetIndices(0, /*traverse_subtree=*/true); for (auto& pair : output_node2dataset_map) { clone->AddDataSetIndex(pair.first, pair.second); } output->SetDataAssembly(clone); } return true; } namespace { class vtkVisitor : public vtkDataAssemblyVisitor { vtkMultiBlockDataSet* Output; vtkPartitionedDataSetCollection* Input; std::vector Stack; public: static vtkVisitor* New(); vtkTypeMacro(vtkVisitor, vtkDataAssemblyVisitor); void SetOutput(vtkMultiBlockDataSet* mb) { this->Output = mb; } void SetInput(vtkPartitionedDataSetCollection* pdc) { this->Input = pdc; } void Visit(int nodeid) override { auto hierarchy = this->GetAssembly(); const auto dataType = hierarchy->GetAttributeOrDefault(nodeid, "vtk_type", -1); if (nodeid == 0) { // sanity check. assert(dataType == this->Output->GetDataObjectType()); this->Stack.push_back(this->Output); this->TraverseSubtree = true; } else if (dataType == VTK_MULTIBLOCK_DATA_SET) { assert(this->Stack.size() > 0); auto top = this->Stack.back(); vtkNew block; this->Stack.push_back(block); const unsigned int index = top->GetNumberOfBlocks(); top->SetBlock(index, block); if (hierarchy->HasAttribute(nodeid, "label")) { top->GetMetaData(index)->Set( vtkCompositeDataSet::NAME(), hierarchy->GetAttributeOrDefault(nodeid, "label", "")); } this->TraverseSubtree = true; } else if (dataType == VTK_MULTIPIECE_DATA_SET) { assert(this->Stack.size() > 0); assert(hierarchy->GetNumberOfChildren(nodeid) == 0); auto top = this->Stack.back(); vtkNew mp; mp->SetNumberOfPieces(hierarchy->GetAttributeOrDefault(nodeid, "vtk_num_pieces", 0u)); const unsigned int index = top->GetNumberOfBlocks(); top->SetBlock(index, mp); if (hierarchy->HasAttribute(nodeid, "label")) { top->GetMetaData(index)->Set( vtkCompositeDataSet::NAME(), hierarchy->GetAttributeOrDefault(nodeid, "label", "")); } unsigned int next = 0; for (auto idx : this->GetCurrentDataSetIndices()) { auto pd = this->Input->GetPartitionedDataSet(idx); for (unsigned int cc = 0; cc < pd->GetNumberOfPartitions(); ++cc) { mp->SetPartition(next++, pd->GetPartitionAsDataObject(cc)); } } this->TraverseSubtree = false; } else { assert(this->Stack.size() > 0); assert(hierarchy->GetNumberOfChildren(nodeid) == 0); auto top = this->Stack.back(); const unsigned int index = top->GetNumberOfBlocks(); auto datasets = this->GetCurrentDataSetIndices(); if (datasets.size() == 1) { const auto num_partitions = this->Input->GetNumberOfPartitions(datasets[0]); if (num_partitions == 1) { top->SetBlock(index, this->Input->GetPartitionAsDataObject(datasets[0], 0)); } else if (num_partitions > 1) { // if more than 1 partition, we can't really put it in the output MB // without changing the structure of we only consider 1st one and warn // about it. vtkErrorMacro( "More than 1 partition encountered. Only 1st will be copied over (num_partitions=" << num_partitions << ")."); top->SetBlock(index, this->Input->GetPartitionAsDataObject(datasets[0], 0)); } else { assert(num_partitions == 0); top->SetBlock(index, nullptr); } } else { assert(datasets.size() == 0); top->SetBlock(index, nullptr); } if (hierarchy->HasAttribute(nodeid, "label")) { top->GetMetaData(index)->Set( vtkCompositeDataSet::NAME(), hierarchy->GetAttributeOrDefault(nodeid, "label", "")); } this->TraverseSubtree = false; } } bool GetTraverseSubtree(int) override { return this->TraverseSubtree; } void BeginSubTree(int vtkNotUsed(nodeid)) override {} void EndSubTree(int vtkNotUsed(nodeid)) override { assert(this->Stack.size() > 0); this->Stack.pop_back(); } protected: vtkVisitor() = default; ~vtkVisitor() override = default; private: vtkVisitor(const vtkVisitor&) = delete; void operator=(const vtkVisitor&) = delete; bool TraverseSubtree = false; }; vtkStandardNewMacro(vtkVisitor); } //---------------------------------------------------------------------------- vtkSmartPointer vtkDataAssemblyUtilities::GenerateCompositeDataSetFromHierarchy( vtkPartitionedDataSetCollection* input, vtkDataAssembly* hierarchy) { if (!input || !hierarchy) { return nullptr; } const auto root = vtkDataAssembly::GetRootNode(); if (strcmp(hierarchy->GetAttributeOrDefault(root, CATEGORY_ATTRIBUTE_NAME, ""), CATEGORY_TRANSFORMED_HIERARCHY) != 0) { vtkLogF( ERROR, "Input hierarchy not generated using `vtkDataAssemblyUtilities` is not supported!"); return nullptr; } const auto dataType = hierarchy->GetAttributeOrDefault(root, "vtk_type", -1); if (vtkDataObjectTypes::TypeIdIsA(dataType, VTK_UNIFORM_GRID_AMR)) { std::vector blocks_per_level; for (const auto child : hierarchy->GetChildNodes(root, /*traverse_subtree=*/false)) { auto level = hierarchy->GetAttributeOrDefault(child, "amr_level", 0u); const auto indices = hierarchy->GetDataSetIndices(child); assert(indices.size() == 0 || indices.size() == 1); const int count = indices.size() == 1 ? input->GetNumberOfPartitions(indices[0]) : 0; if (level >= static_cast(blocks_per_level.size())) { blocks_per_level.resize(level + 1); } blocks_per_level[level] = count; } vtkSmartPointer amr; amr.TakeReference(vtkUniformGridAMR::SafeDownCast(vtkDataObjectTypes::NewDataObject(dataType))); amr->Initialize(static_cast(blocks_per_level.size()), !blocks_per_level.empty() ? &blocks_per_level[0] : nullptr); for (const auto child : hierarchy->GetChildNodes(root, /*traverse_subtree=*/false)) { auto level = hierarchy->GetAttributeOrDefault(child, "amr_level", 0u); const auto indices = hierarchy->GetDataSetIndices(child); assert(indices.size() == 0 || indices.size() == 1); if (indices.size() == 1) { for (unsigned int cc = 0, max = input->GetNumberOfPartitions(indices[0]); cc < max; ++cc) { amr->SetDataSet( level, cc, vtkUniformGrid::SafeDownCast(input->GetPartition(indices[0], cc))); } } } return amr; } else if (vtkDataObjectTypes::TypeIdIsA(dataType, VTK_MULTIBLOCK_DATA_SET)) { vtkNew mb; vtkNew builder; builder->SetOutput(mb); builder->SetInput(input); hierarchy->Visit(builder, vtkDataAssembly::TraversalOrder::DepthFirst); return mb; } return nullptr; } //---------------------------------------------------------------------------- namespace { class vtkGenerateIdsVisitor : public vtkDataAssemblyVisitor { public: static vtkGenerateIdsVisitor* New(); vtkTypeMacro(vtkGenerateIdsVisitor, vtkDataAssemblyVisitor); std::set SelectedNodes; std::vector CompositeIndices; void Visit(int nodeid) override { auto assembly = this->GetAssembly(); assert(assembly->HasAttribute(nodeid, "cid")); const auto cid = assembly->GetAttributeOrDefault(nodeid, "cid", 0u); const auto type = assembly->GetAttributeOrDefault(nodeid, "vtk_type", 0); if (!this->EnabledStack.empty() || this->SelectedNodes.find(nodeid) != this->SelectedNodes.end()) { if (vtkDataObjectTypes::TypeIdIsA(type, VTK_COMPOSITE_DATA_SET)) { if (vtkDataObjectTypes::TypeIdIsA(type, VTK_MULTIPIECE_DATA_SET)) { // add indices for all parts. for (unsigned int cc = 0, max = assembly->GetAttributeOrDefault(nodeid, "vtk_num_pieces", 0u); cc < max; ++cc) { this->CompositeIndices.push_back(cid + 1 + cc); } } else { // push to enable stack so that all our children are treated as // selected. this->EnabledStack.push_back(nodeid); } } else { this->CompositeIndices.push_back(cid); } } } void EndSubTree(int nodeid) override { if (!this->EnabledStack.empty() && this->EnabledStack.back() == nodeid) { this->EnabledStack.pop_back(); } } protected: vtkGenerateIdsVisitor() = default; ~vtkGenerateIdsVisitor() override = default; private: vtkGenerateIdsVisitor(const vtkGenerateIdsVisitor&) = delete; void operator=(const vtkGenerateIdsVisitor&) = delete; std::vector EnabledStack; }; vtkStandardNewMacro(vtkGenerateIdsVisitor); } //---------------------------------------------------------------------------- std::vector vtkDataAssemblyUtilities::GetSelectedCompositeIds( const std::vector& selectors, vtkDataAssembly* hierarchyOrAssembly, vtkPartitionedDataSetCollection* data, bool leaf_nodes_only) { if (hierarchyOrAssembly == nullptr || selectors.empty()) { return {}; } const auto root = vtkDataAssembly::GetRootNode(); const bool isHierarchy = (strcmp(hierarchyOrAssembly->GetAttributeOrDefault(root, CATEGORY_ATTRIBUTE_NAME, ""), CATEGORY_HIERARCHY) == 0); if (!isHierarchy && data == nullptr) { vtkLogF(ERROR, "Missing required `data` argument."); return {}; } if (isHierarchy && leaf_nodes_only) { const auto dataType = hierarchyOrAssembly->GetAttributeOrDefault(root, "vtk_type", -1); // for now we only support MBs. we could support AMR and PDC, // but I don't see the point in doing so right now. if (!vtkDataObjectTypes::TypeIdIsA(dataType, VTK_MULTIBLOCK_DATA_SET)) { vtkLogF(ERROR, "Hierarchy does not represent a supported composite dataset type (%s)", vtkDataObjectTypes::GetClassNameFromTypeId(dataType)); return {}; } // the worst case: we need to traverse the hierarchy and determine composite // ids. const auto nodes = hierarchyOrAssembly->SelectNodes(selectors); vtkNew visitor; std::copy(nodes.begin(), nodes.end(), std::inserter(visitor->SelectedNodes, visitor->SelectedNodes.end())); hierarchyOrAssembly->Visit(visitor); return visitor->CompositeIndices; } // here, we only traverse the subtree if not a hierarchy. Otherwise, the // dataset indices are directly composite ids so we don't need to traverse // substree. auto dsIndices = hierarchyOrAssembly->GetDataSetIndices( hierarchyOrAssembly->SelectNodes(selectors), /*traverse_subtree=*/!isHierarchy); if (isHierarchy) { assert(leaf_nodes_only == false); // in this case, dsIndices directly correspond to the composite ids; // nothing more to do. return dsIndices; } else if (!isHierarchy && !leaf_nodes_only) { // convert partitioned dataset index to composite index. assert(data != nullptr); std::vector cids(dsIndices.size()); vtkSMPTools::Transform(dsIndices.begin(), dsIndices.end(), cids.begin(), [data](unsigned int partitionIdx) { return data->GetCompositeIndex(partitionIdx); }); return cids; } else { assert(isHierarchy == false); assert(leaf_nodes_only == true); assert(data != nullptr); // convert partitioned dataset index to composite index for individual // partitions. std::vector cids; for (const auto& partitionIdx : dsIndices) { for (unsigned int cc = 0; cc < data->GetNumberOfPartitions(partitionIdx); ++cc) { cids.push_back(data->GetCompositeIndex(partitionIdx, cc)); } } return cids; } } //---------------------------------------------------------------------------- std::string vtkDataAssemblyUtilities::GetSelectorForCompositeId( unsigned int id, vtkDataAssembly* hierarchy) { std::vector ids; ids.push_back(id); auto selectors = vtkDataAssemblyUtilities::GetSelectorsForCompositeIds(ids, hierarchy); return selectors.empty() ? std::string() : selectors.front(); } //---------------------------------------------------------------------------- namespace { class vtkSelectorsForCompositeIdsVisitor : public vtkDataAssemblyVisitor { public: static vtkSelectorsForCompositeIdsVisitor* New(); vtkTypeMacro(vtkSelectorsForCompositeIdsVisitor, vtkDataAssemblyVisitor); std::vector Selectors; std::set CompositeIds; protected: vtkSelectorsForCompositeIdsVisitor() = default; ~vtkSelectorsForCompositeIdsVisitor() override = default; void Visit(int nodeid) override { const auto ids = this->GetCurrentDataSetIndices(); if (ids.size() != 1) { // this happens for AMR if a level has no datasets. return; } const auto assembly = this->GetAssembly(); const unsigned int cid = ids.front(); unsigned int childCount = assembly->GetAttributeOrDefault(nodeid, "number_of_partitions", 0u); childCount = assembly->GetAttributeOrDefault(nodeid, "vtk_num_pieces", childCount); const auto cid_range = std::make_pair(cid, cid + 1 + childCount); for (auto id = cid_range.first; id < cid_range.second; ++id) { if (this->CompositeIds.find(id) != this->CompositeIds.end()) { this->Selectors.push_back(assembly->GetNodePath(nodeid)); break; } } } private: vtkSelectorsForCompositeIdsVisitor(const vtkSelectorsForCompositeIdsVisitor&) = delete; void operator=(const vtkSelectorsForCompositeIdsVisitor&) = delete; }; vtkStandardNewMacro(vtkSelectorsForCompositeIdsVisitor); } // namespace {} //---------------------------------------------------------------------------- std::vector vtkDataAssemblyUtilities::GetSelectorsForCompositeIds( const std::vector& ids, vtkDataAssembly* hierarchy) { const auto root = vtkDataAssembly::GetRootNode(); if (strcmp(hierarchy->GetAttributeOrDefault(root, CATEGORY_ATTRIBUTE_NAME, ""), CATEGORY_HIERARCHY) != 0) { vtkLogF(ERROR, "GetSelectorForCompositeId is only supported on a data-assembly representation a hierarchy."); return {}; } const auto dataType = hierarchy->GetAttributeOrDefault(root, "vtk_type", -1); if (vtkDataObjectTypes::TypeIdIsA(dataType, VTK_PARTITIONED_DATA_SET_COLLECTION) || vtkDataObjectTypes::TypeIdIsA(dataType, VTK_MULTIBLOCK_DATA_SET)) { vtkNew visitor; std::copy( ids.begin(), ids.end(), std::inserter(visitor->CompositeIds, visitor->CompositeIds.end())); hierarchy->Visit(visitor); return visitor->Selectors; } // in theory, this can work for AMR too, but I am leaving that until we have a // use-case. return {}; }