// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkHyperTree.h" #include "vtkBitArray.h" #include "vtkHyperTreeGridScales.h" #include "vtkIdList.h" #include "vtkObjectFactory.h" #include "vtkTypeInt64Array.h" #include #include #include #include #include #include //------------------------------------------------------------------------------ VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkHyperTree); vtkHyperTree::vtkHyperTree() { this->Initialize(2, 3); } //------------------------------------------------------------------------------ void vtkHyperTree::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Dimension: " << static_cast(this->Dimension) << "\n"; os << indent << "BranchFactor: " << static_cast(this->BranchFactor) << "\n"; os << indent << "NumberOfChildren: " << static_cast(this->NumberOfChildren) << "\n"; os << indent << "NumberOfLevels: " << this->Datas->NumberOfLevels << "\n"; os << indent << "NumberOfVertices (coarse and leaves): " << this->Datas->NumberOfVertices << "\n"; os << indent << "NumberOfNodes (coarse): " << this->Datas->NumberOfNodes << "\n"; if (this->IsGlobalIndexImplicit()) { os << indent << "Implicit global index mapping\n"; os << indent << "GlobalIndexStart: " << this->Datas->GlobalIndexStart << "\n"; } else { os << indent << "Explicit global index mapping\n"; } os << indent << "ParentToElderChild: " << this->Datas->ParentToElderChild.size() << endl; for (unsigned int i = 0; i < this->Datas->ParentToElderChild.size(); ++i) { os << this->Datas->ParentToElderChild[i] << " "; } os << endl; os << indent << "GlobalIndexTable: "; for (unsigned int i = 0; i < this->Datas->GlobalIndexTable.size(); ++i) { os << " " << this->Datas->GlobalIndexTable[i]; } os << endl; } //------------------------------------------------------------------------------ void vtkHyperTree::Initialize( unsigned char branchFactor, unsigned char dimension, unsigned char vtkNotUsed(numberOfChildren)) { this->Initialize(branchFactor, dimension); } //------------------------------------------------------------------------------ bool vtkHyperTree::Initialize(const unsigned char branchFactor, const unsigned char dimension) { if (branchFactor < 2 || 3 < branchFactor) { vtkGenericWarningMacro("Bad branching factor " << branchFactor); return false; } if (dimension < 1 || 3 < dimension) { vtkGenericWarningMacro("Bad dimension " << static_cast(dimension)); return false; } this->BranchFactor = branchFactor; this->Dimension = dimension; this->NumberOfChildren = pow(branchFactor, dimension); this->Datas = std::make_shared(); this->Datas->TreeIndex = -1; this->Datas->NumberOfLevels = 1; this->Datas->NumberOfVertices = 1; this->Datas->NumberOfNodes = 0; // By default, nothing is used // No GlobalIndexStart, no GlobalIndexFromLocal this->Datas->GlobalIndexStart = -1; this->Datas->ParentToElderChild.resize(1); this->Datas->ParentToElderChild[0] = std::numeric_limits::max(); // By default, the root doesn't have a parent this->Datas->GlobalIndexTable.clear(); this->Scales = nullptr; return true; } //------------------------------------------------------------------------------ void vtkHyperTree::CopyStructure(vtkHyperTree* ht) { assert("pre: ht_exists" && ht != nullptr); // Copy ht data this->Datas = std::make_shared(*ht->Datas); this->SetScales(std::make_shared( ht->Scales->GetBranchFactor(), ht->Scales->ComputeScale(0))); this->BranchFactor = ht->BranchFactor; this->Dimension = ht->Dimension; this->NumberOfChildren = ht->NumberOfChildren; } //------------------------------------------------------------------------------ std::shared_ptr vtkHyperTree::InitializeScales( const double* scales, bool reinitialize) { if (this->Scales == nullptr || reinitialize) { this->SetScales(std::make_shared(this->BranchFactor, scales)); } return this->Scales; } //------------------------------------------------------------------------------ void vtkHyperTree::GetScale(double s[3]) const { assert("pre: scales_exists" && this->Scales != nullptr); const double* scale = this->Scales->ComputeScale(0); memcpy(s, scale, 3 * sizeof(double)); } //------------------------------------------------------------------------------ double vtkHyperTree::GetScale(unsigned int d) const { assert("pre: scales_exists" && this->Scales != nullptr); const double* scale = this->Scales->ComputeScale(0); return scale[d]; } //--------------------------------------------------------------------------- void vtkHyperTree::ComputeBreadthFirstOrderDescriptor(const unsigned int depthLimiter, vtkBitArray* inputMask, vtkTypeInt64Array* numberOfVerticesPerDepth, vtkBitArray* descriptor, vtkIdList* breadthFirstIdMap) { int maxDepth = this->GetNumberOfLevels(); std::vector> descriptorPerDepth(maxDepth); std::vector> breadthFirstOrderIdMapPerDepth(maxDepth); this->ComputeBreadthFirstOrderDescriptorImpl( depthLimiter, inputMask, 0, 0, descriptorPerDepth, breadthFirstOrderIdMapPerDepth); // Reducing maxDepth to squeeze out depths in which all subtrees are // entirely masked. // NOLINTNEXTLINE(bugprone-inc-dec-in-conditions) while (maxDepth && breadthFirstOrderIdMapPerDepth[--maxDepth].empty()) ; ++maxDepth; for (int idepth = 0; idepth < maxDepth; ++idepth) { numberOfVerticesPerDepth->InsertNextValue( static_cast(breadthFirstOrderIdMapPerDepth[idepth].size())); for (const vtkIdType& idg : breadthFirstOrderIdMapPerDepth[idepth]) { breadthFirstIdMap->InsertNextId(idg); } } // We ignore last depth for the descriptor, as we already know that no // vertices have children. // However, we are careful not treating trees with only one depth. There // is no need to describe such trivial trees. for (int idepth = 0; idepth < maxDepth - 1; ++idepth) { for (const bool state : descriptorPerDepth[idepth]) { descriptor->InsertNextValue(state); } } } //--------------------------------------------------------------------------- void vtkHyperTree::BuildFromBreadthFirstOrderDescriptor( vtkBitArray* descriptor, vtkIdType numberOfBits, vtkIdType startIndex) { this->Datas->ParentToElderChild.clear(); int numberOfDepths = 1; vtkIdType numberOfCoarseVertices = 0; vtkIdType numberOfVertices = 1; if (!numberOfBits) { this->Datas->ParentToElderChild.emplace_back(std::numeric_limits::max()); } else { vtkIdType currentDepthSize = 1; vtkIdType nextDepthSize = 0; vtkIdType currentPositionAtDepth = 0; for (vtkIdType id = startIndex; id < startIndex + numberOfBits; ++id) { if (descriptor->GetValue(id)) { this->Datas->ParentToElderChild.emplace_back(numberOfVertices); numberOfVertices += this->NumberOfChildren; ++numberOfCoarseVertices; nextDepthSize += this->NumberOfChildren; } else { this->Datas->ParentToElderChild.emplace_back(std::numeric_limits::max()); } if (++currentPositionAtDepth == currentDepthSize) { ++numberOfDepths; currentDepthSize = nextDepthSize; nextDepthSize = 0; currentPositionAtDepth = 0; } } } this->Datas->NumberOfLevels = numberOfDepths; this->Datas->NumberOfNodes = numberOfCoarseVertices; this->Datas->NumberOfVertices = numberOfVertices; } //--------------------------------------------------------------------------- void vtkHyperTree::InitializeForReader(vtkIdType numberOfLevels, vtkIdType nbVertices, vtkIdType nbVerticesOfLastdepth, vtkBitArray* isParent, vtkBitArray* isMasked, vtkBitArray* outIsMasked) { if (isParent == nullptr) { this->Datas->ParentToElderChild.resize(1); this->Datas->ParentToElderChild[0] = std::numeric_limits::max(); if (isMasked) { vtkIdType nbIsMasked = isMasked->GetNumberOfTuples(); if (nbIsMasked) { assert(isMasked->GetNumberOfComponents() == 1); outIsMasked->InsertValue(this->GetGlobalIndexFromLocal(0), isMasked->GetValue(0)); } } return; } vtkIdType nbIsParent = isParent->GetNumberOfTuples(); assert(isParent->GetNumberOfComponents() == 1); vtkIdType firstOffsetLastdepth = nbVertices - nbVerticesOfLastdepth; firstOffsetLastdepth = std::min(nbIsParent, firstOffsetLastdepth); this->Datas->ParentToElderChild.resize(firstOffsetLastdepth); vtkIdType nbCoarses = isParent->GetValue(0); if (nbCoarses) { vtkIdType off = 1; this->Datas->ParentToElderChild.resize( std::max(static_cast(1), firstOffsetLastdepth)); this->Datas->ParentToElderChild[0] = off; for (vtkIdType i = 1; i < firstOffsetLastdepth; ++i) { if (isParent->GetValue(i)) { off += this->NumberOfChildren; this->Datas->ParentToElderChild[i] = off; ++nbCoarses; } else { this->Datas->ParentToElderChild[i] = std::numeric_limits::max(); } } } else { this->Datas->ParentToElderChild.resize(1); this->Datas->ParentToElderChild[0] = std::numeric_limits::max(); } if (isMasked) { vtkIdType nbIsMasked = isMasked->GetNumberOfTuples(); assert(isMasked->GetNumberOfComponents() == 1); vtkIdType i = 0; for (; i < nbIsMasked && i < nbVertices; ++i) { outIsMasked->InsertValue(this->GetGlobalIndexFromLocal(i), isMasked->GetValue(i)); } // By convention, the final values not explicitly described // by the isMasked parameter are False. for (; i < nbVertices; ++i) { outIsMasked->InsertValue(this->GetGlobalIndexFromLocal(i), false); } } this->Datas->NumberOfLevels = numberOfLevels; this->Datas->NumberOfNodes = nbCoarses; this->Datas->NumberOfVertices = nbVertices; } //--------------------------------------------------------------------------- bool vtkHyperTree::IsGlobalIndexImplicit() { return this->Datas->GlobalIndexStart == -1; } //--------------------------------------------------------------------------- void vtkHyperTree::SetGlobalIndexStart(vtkIdType start) { this->Datas->GlobalIndexStart = start; } //--------------------------------------------------------------------------- void vtkHyperTree::SetGlobalIndexFromLocal(vtkIdType index, vtkIdType global) { assert("pre: not_globalindex_from_local_if_use_globalindex_start" && this->Datas->GlobalIndexStart < 0); // If local index outside map range, resize the latter if (static_cast(this->Datas->GlobalIndexTable.size()) <= index) { this->Datas->GlobalIndexTable.resize(index + 1, -1); } // This service allows the value of the global index to be positioned // several times in order to take into account a first description, // a priori, incomplete followed by a more detailed description. // The last call overwrites, replaces what was written previously. this->Datas->GlobalIndexTable[index] = global; } //--------------------------------------------------------------------------- vtkIdType vtkHyperTree::GetGlobalIndexFromLocal(vtkIdType index) const { if (!this->Datas->GlobalIndexTable.empty()) { // Case explicit global node index assert("pre: not_validindex" && index >= 0 && index < (vtkIdType)this->Datas->GlobalIndexTable.size()); assert("pre: not_positive_globalindex" && this->Datas->GlobalIndexTable[index] >= 0); return this->Datas->GlobalIndexTable[index]; } // Case implicit global node index assert("pre: not_positive_startindex" && this->Datas->GlobalIndexStart >= 0); assert("pre: not_validindex" && index >= 0); return this->Datas->GlobalIndexStart + index; } //--------------------------------------------------------------------------- vtkIdType vtkHyperTree::GetGlobalNodeIndexMax() const { if (static_cast(!this->Datas->GlobalIndexTable.empty())) { // Case explicit global node index const std::vector::iterator it_end = this->Datas->GlobalIndexTable.end(); const std::vector::iterator elt_found = std::max_element(this->Datas->GlobalIndexTable.begin(), it_end); assert("pre: not_positive_globalindex" && (*std::max_element(this->Datas->GlobalIndexTable.begin(), it_end)) >= 0); return *elt_found; } // Case implicit global node index assert("pre: not_positive_startindex" && this->Datas->GlobalIndexStart >= 0); return this->Datas->GlobalIndexStart + this->Datas->NumberOfVertices - 1; } //--------------------------------------------------------------------------- // Description: // Public only for entry: vtkHyperTreeGridEntry, vtkHyperTreeGridGeometryEntry, // vtkHyperTreeGridGeometryDepthEntry vtkIdType vtkHyperTree::GetElderChildIndex(unsigned int index_parent) const { assert( "pre: valid_range" && index_parent < static_cast(this->Datas->NumberOfVertices)); return this->Datas->ParentToElderChild[index_parent]; } //--------------------------------------------------------------------------- // Description: // Access to the internals of the tree. Should be used for consulting, // not modification. const unsigned int* vtkHyperTree::GetElderChildIndexArray(size_t& nbElements) const { nbElements = this->Datas->ParentToElderChild.size(); return this->Datas->ParentToElderChild.data(); } //--------------------------------------------------------------------------- void vtkHyperTree::SubdivideLeaf(vtkIdType index, unsigned int depth) { assert("pre: not_validindex" && index < static_cast(this->Datas->NumberOfVertices)); assert("pre: not_leaf" && this->IsLeaf(index)); // The leaf becomes a node and is not anymore a leaf // Nodes get constructed with leaf flags set to 1. if (static_cast(this->Datas->ParentToElderChild.size()) <= index) { this->Datas->ParentToElderChild.resize(index + 1, std::numeric_limits::max()); } // The first new child this->Datas->ParentToElderChild[index] = static_cast(this->Datas->NumberOfVertices); // Add the new leaves to the number of leaves at the next depth. if (depth + 1 == this->Datas->NumberOfLevels) // >= { // We have a new depth. ++this->Datas->NumberOfLevels; } // Update the number of non-leaf and all vertices this->Datas->NumberOfNodes += 1; this->Datas->NumberOfVertices += this->NumberOfChildren; } //--------------------------------------------------------------------------- unsigned long vtkHyperTree::GetActualMemorySizeBytes() { // in bytes // NOLINTNEXTLINE(readability-redundant-casting): needed on Windows return static_cast(sizeof(unsigned int) * this->Datas->ParentToElderChild.size() + sizeof(vtkIdType) * this->Datas->GlobalIndexTable.size() + 3 * sizeof(unsigned char) + 6 * sizeof(vtkIdType)); } //--------------------------------------------------------------------------- bool vtkHyperTree::IsChildLeaf(vtkIdType index_parent, unsigned int ichild) const { assert("pre: valid_range" && index_parent >= 0 && index_parent < this->Datas->NumberOfVertices); if (static_cast(index_parent) >= this->Datas->ParentToElderChild.size()) { return false; } assert("pre: valid_range" && ichild < this->NumberOfChildren); vtkIdType index_child = this->Datas->ParentToElderChild[index_parent] + ichild; return static_cast(index_child) >= this->Datas->ParentToElderChild.size() || this->Datas->ParentToElderChild[index_child] == std::numeric_limits::max(); } //--------------------------------------------------------------------------- bool vtkHyperTree::IsTerminalNode(vtkIdType index) const { assert("pre: valid_range" && index >= 0 && index < this->Datas->NumberOfVertices); if (static_cast(index) >= this->Datas->ParentToElderChild.size()) { return false; } for (unsigned int ichild = 0; ichild < this->NumberOfChildren; ++ichild) { if (!this->IsChildLeaf(index, ichild)) { return false; } } return true; } //--------------------------------------------------------------------------- bool vtkHyperTree::IsLeaf(vtkIdType index) const { assert("pre: valid_range" && index >= 0 && index < this->Datas->NumberOfVertices); return static_cast(index) >= this->Datas->ParentToElderChild.size() || this->Datas->ParentToElderChild[index] == std::numeric_limits::max() || this->Datas->NumberOfVertices == 1; } //--------------------------------------------------------------------------- void vtkHyperTree::ComputeBreadthFirstOrderDescriptorImpl(const unsigned int depthLimiter, vtkBitArray* inputMask, const unsigned int depth, vtkIdType index, std::vector>& descriptorPerDepth, std::vector>& breadthFirstOrderIdMapPerDepth) { vtkIdType idg = this->GetGlobalIndexFromLocal(index); bool mask = inputMask ? inputMask->GetValue(idg) : false; breadthFirstOrderIdMapPerDepth[depth].emplace_back(idg); assert("pre: depth valid" && depth < std::numeric_limits::max()); if (!this->IsLeaf(index) && !mask && depth < depthLimiter) { descriptorPerDepth[depth].push_back(true); for (int iChild = 0; iChild < this->NumberOfChildren; ++iChild) { this->ComputeBreadthFirstOrderDescriptorImpl(depthLimiter, inputMask, depth + 1, this->GetElderChildIndex(index) + iChild, descriptorPerDepth, breadthFirstOrderIdMapPerDepth); } } else { descriptorPerDepth[depth].push_back(false); } } // VTK_DEPRECATED_IN_9_6_0 vtkHyperTree* vtkHyperTree::CreateInstance(unsigned char factor, unsigned char dimension) { vtkHyperTree* ht = vtkHyperTree::New(); return ht->Initialize(factor, dimension) ? ht : nullptr; } VTK_ABI_NAMESPACE_END