// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkCellArray.h" #include "vtkArrayDispatch.h" #include "vtkArrayDispatchDataSetArrayList.h" #include "vtkCellArrayIterator.h" #include "vtkDataArrayRange.h" #include "vtkIdTypeArray.h" #include "vtkObjectFactory.h" #include "vtkSMPThreadLocal.h" #include "vtkSMPTools.h" #include "vtkSmartPointer.h" #include #include #include namespace { // These implementations are for methods that will be deprecated in the future: namespace deprec { // Given a legacy Location, find the corresponding cellId. The location // *must* refer to a [numPts] entry in the old connectivity array, or the // returned CellId will be -1. struct LocationToCellIdFunctor : public vtkCellArray::DispatchUtilities { template void operator()( OffsetsT* offsets, ConnectivityT* vtkNotUsed(conn), vtkIdType location, vtkIdType& cellId) const { using ValueType = GetAPIType; const auto offsetsRange = GetRange(offsets); // Use a binary-search to find the location: auto it = this->BinarySearchOffset( offsetsRange.begin(), offsetsRange.end() - 1, static_cast(location)); cellId = std::distance(offsetsRange.begin(), it); if (it == offsetsRange.end() - 1 /* no match found */ || static_cast(*it + cellId) != location /* `location` not at cell head */) { // Location invalid. cellId = -1; return; } } template IterT BinarySearchOffset(const IterT& beginIter, const IterT& endIter, const typename std::iterator_traits::value_type& targetLocation) const { using ValueType = typename std::iterator_traits::value_type; using DifferenceType = typename std::iterator_traits::difference_type; DifferenceType roiSize = std::distance(beginIter, endIter); IterT roiBegin = beginIter; while (roiSize > 0) { IterT it = roiBegin; const DifferenceType step = roiSize / 2; std::advance(it, step); // This differs from a generic binary search in the following line: // Adding the distance from the start of the array to the current // iterator will account for the cellSize entries in the old cell array // format, such that curLocation would be the offset in the old style // connectivity array. const ValueType curLocation = *it + std::distance(beginIter, it); if (curLocation < targetLocation) { roiBegin = ++it; roiSize -= step + 1; } else { roiSize = step; } } return roiBegin; } }; struct CellIdToLocationFunctor : public vtkCellArray::DispatchUtilities { template void operator()( OffsetsT* offsets, ConnectivityT* vtkNotUsed(conn), vtkIdType cellId, vtkIdType& loc) const { // Adding the cellId to the offset of that cell id gives us the cell // location in the old-style vtkCellArray connectivity array. loc = static_cast(GetRange(offsets)[cellId]) + cellId; } }; struct GetInsertLocationImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType& insertLoc) const { // The insert location used to just be the tail of the connectivity array. // Compute the equivalent value: insertLoc = offsets->GetNumberOfValues() - 1 + conn->GetNumberOfValues(); } }; } // end namespace deprec struct PrintDebugImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, std::ostream& os) { using ValueType = GetAPIType; const vtkIdType numCells = offsets->GetNumberOfValues() - 1; for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { os << "cell " << cellId << ": "; const auto cellRange = GetCellRange(offsets, conn, cellId); for (ValueType ptId : cellRange) { os << ptId << " "; } os << "\n"; } } }; struct InitializeImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn) const { using ValueType = GetAPIType; using AccessorType = vtkDataArrayAccessor; conn->Initialize(); if (offsets->GetArrayType() == vtkArrayTypes::VTK_AFFINE_ARRAY) { // AffineArray's Initialize destroys the backend of the offsets array, // so we only set the number of values to 0 to preserve the backend. offsets->SetNumberOfValues(0); } else { offsets->Initialize(); } AccessorType accessor(offsets); ValueType firstOffset = 0; accessor.InsertNext(firstOffset); } }; struct SqueezeImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn) const { offsets->Squeeze(); conn->Squeeze(); } }; struct IsValidImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, bool& isValid) const { using ValueType = GetAPIType; // Both arrays must be single component if (offsets->GetNumberOfComponents() != 1 || conn->GetNumberOfComponents() != 1) { isValid = false; return; } auto offsetsRange = GetRange(offsets); // Offsets must have at least one value, and the first value must be zero if (offsetsRange.size() == 0 || *offsetsRange.cbegin() != 0) { isValid = false; return; } // Values in offsets must not decrease auto it = std::adjacent_find(offsetsRange.cbegin(), offsetsRange.cend(), [](const ValueType a, const ValueType b) -> bool { return a > b; }); if (it != offsetsRange.cend()) { isValid = false; return; } // The last value in offsets must be the size of the connectivity array. if (conn->GetNumberOfValues() != static_cast(*(offsetsRange.cend() - 1))) { isValid = false; return; } isValid = true; } }; template struct CanConvert : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, bool& canConvert) const { using ValueType = GetAPIType; // offsets are sorted, so just check the last value, but we have to compute // the full range of the connectivity array. auto offsetsRange = GetRange(offsets); if (offsetsRange.size() > 0 && !this->CheckValue(ValueType(*(offsetsRange.end() - 1)))) { canConvert = false; return; } auto connRange = GetRange(conn); if (connRange.size() > 0) { auto [minIt, maxIt] = std::minmax_element(connRange.begin(), connRange.end()); std::array connValueRange = { *minIt, *maxIt }; if (!this->CheckValue(ValueType(connValueRange[0])) || !this->CheckValue(ValueType(connValueRange[1]))) { canConvert = false; return; } } canConvert = true; } template bool CheckValue(const U& val) const { return val == static_cast(static_cast(val)); } }; struct ExtractAndInitialize : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* vtkNotUsed(inOffsets), ConnectivityT* inConn, TargetConnectivityT* conn, bool& result) const { result = this->Process(inConn, conn); } template void operator()(OffsetsT* inOffsets, ConnectivityT* inConn, TargetOffsetsT* outOffsets, TargetConnectivityT* outConn, bool& result) const { result = this->Process(inOffsets, outOffsets) && this->Process(inConn, outConn); } template bool Process(SourceArrayT* src, TargetArrayT* dst) const { // check if we can shallow copy it first if (src->IsA(dst->GetClassName())) { dst->ShallowCopy(src); return true; } // Check that allocation succeeds: if (!dst->Resize(src->GetNumberOfTuples())) { return false; } // Copy data: dst->DeepCopy(src); // Free old memory: src->Resize(0); return true; } }; struct IsHomogeneousImpl : public vtkCellArray::DispatchUtilities { template void operator()( OffsetsT* offsets, ConnectivityT* vtkNotUsed(conn), vtkIdType& isHomogeneous) const { using ValueType = GetAPIType; const vtkIdType numCells = offsets->GetNumberOfValues() - 1; if (numCells == 0) { isHomogeneous = 0; return; } // Initialize using the first cell: const vtkIdType firstCellSize = GetCellSize(offsets, 0); // Verify the rest: auto offsetsRange = GetRange(offsets); auto it = std::adjacent_find(offsetsRange.begin() + 1, offsetsRange.end(), [&](const ValueType a, const ValueType b) -> bool { return static_cast(b - a) != firstCellSize; }); if (it != offsetsRange.end()) { // Found a cell that doesn't match the size of the first cell: isHomogeneous = -1; return; } isHomogeneous = firstCellSize; } }; struct AllocateExactImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType numCells, vtkIdType connectivitySize, bool& result) const { using ValueType = GetAPIType; using AccessorType = vtkDataArrayAccessor; result = (offsets->Allocate(numCells + 1) && conn->Allocate(connectivitySize)); if (result) { AccessorType accessor(offsets); ValueType firstOffset = 0; accessor.InsertNext(firstOffset); } } }; struct ResizeExactImpl { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType numCells, vtkIdType connectivitySize, bool& result) const { result = offsets->SetNumberOfValues(numCells + 1) && conn->SetNumberOfValues(connectivitySize); } }; struct FindMaxCell // SMP functor { vtkCellArray* CellArray; vtkIdType Result{ 0 }; vtkSMPThreadLocal LocalResult; FindMaxCell(vtkCellArray* array) : CellArray{ array } { } void Initialize() { this->LocalResult.Local() = 0; } struct Impl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* vtkNotUsed(conn), vtkIdType cellId, const vtkIdType endCellId, vtkIdType& maxCellSize) const { maxCellSize = 0; auto offsetsRange = GetRange(offsets); for (; cellId < endCellId; ++cellId) { maxCellSize = std::max(maxCellSize, offsetsRange[cellId + 1] - offsetsRange[cellId]); } } }; void operator()(vtkIdType cellId, vtkIdType endCellId) { vtkIdType& lval = this->LocalResult.Local(); vtkIdType maxCellSize; this->CellArray->Dispatch(Impl{}, cellId, endCellId, maxCellSize); lval = std::max(lval, maxCellSize); } void Reduce() { for (const vtkIdType lResult : this->LocalResult) { this->Result = std::max(this->Result, lResult); } } }; struct PrintSelfImpl { template void operator()(OffsetsT* offsets, ConnectivityT* conn, ostream& os, vtkIndent indent) const { os << indent << "Offsets:\n"; offsets->PrintSelf(os, indent.GetNextIndent()); os << indent << "Connectivity:\n"; conn->PrintSelf(os, indent.GetNextIndent()); } }; struct GetLegacyDataSizeImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType& size) const { size = offsets->GetNumberOfValues() - 1 + conn->GetNumberOfValues(); } }; struct ReverseCellAtIdImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType cellId) const { auto cellRange = GetCellRange(offsets, conn, cellId); std::reverse(cellRange.begin(), cellRange.end()); } }; struct ReplaceCellPointAtIdImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType cellId, vtkIdType cellPointIndex, vtkIdType newPointId) const { using ValueType = GetAPIType; GetRange(conn)[GetBeginOffset(offsets, cellId) + cellPointIndex] = static_cast(newPointId); } }; struct ReplaceCellAtIdImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, vtkIdType cellId, vtkIdType cellSize, const vtkIdType* cellPoints) const { using ValueType = GetAPIType; auto cellRange = GetCellRange(offsets, conn, cellId); assert(cellRange.size() == cellSize); for (vtkIdType i = 0; i < cellSize; ++i) { cellRange[i] = static_cast(cellPoints[i]); } } }; struct AppendLegacyFormatImpl : public vtkCellArray::DispatchUtilities { template void operator()(OffsetsT* offsets, ConnectivityT* conn, const vtkIdType* data, const vtkIdType len, const vtkIdType ptOffset) const { using ValueType = GetAPIType; using OffsetsAccessorType = vtkDataArrayAccessor; using ConnectivityAccessorType = vtkDataArrayAccessor; OffsetsAccessorType offsetsAccessor(offsets); ConnectivityAccessorType connAccessor(conn); ValueType offset = static_cast(conn->GetNumberOfValues()); const vtkIdType* const dataEnd = data + len; while (data < dataEnd) { vtkIdType numPts = *data++; offset += static_cast(numPts); offsetsAccessor.InsertNext(offset); while (numPts-- > 0) { connAccessor.InsertNext(static_cast(*data++ + ptOffset)); } } } }; struct AppendImpl { // Call this signature: template void operator()(DstOffsetsT* dstOffsets, DstConnectivityT* dstConn, vtkCellArray* src, vtkIdType pointOffset) const { // dispatch on src: src->Dispatch(*this, dstOffsets, dstConn, pointOffset); } // Above signature calls this operator in Visit: template void operator()(SrcOffsetsT* srcOffsets, SrcConnectivityT* srcConn, DstOffsetsT* dstOffsets, DstConnectivityT* dstConn, vtkIdType pointOffsets) const { this->AppendArrayWithOffset(srcOffsets, dstOffsets, dstConn->GetNumberOfValues(), true); this->AppendArrayWithOffset(srcConn, dstConn, pointOffsets, false); } // Assumes both arrays are 1 component. src's data is appended to dst with // offset added to each value. template void AppendArrayWithOffset( SrcArrayT* srcArray, DstArrayT* dstArray, vtkIdType offset, bool skipFirst) const { VTK_ASSUME(srcArray->GetNumberOfComponents() == 1); VTK_ASSUME(dstArray->GetNumberOfComponents() == 1); using SrcValueType = vtk::GetAPIType; using DstValueType = vtk::GetAPIType; const vtkIdType srcSize = skipFirst ? srcArray->GetNumberOfValues() - 1 : srcArray->GetNumberOfValues(); const vtkIdType dstBegin = dstArray->GetNumberOfValues(); const vtkIdType dstEnd = dstBegin + srcSize; // This extends the allocation of dst to ensure we have enough space // allocated: { vtkDataArrayAccessor dstAccessor(dstArray); dstAccessor.Insert(dstEnd - 1, static_cast(0)); } const auto srcRange = vtk::DataArrayValueRange<1, vtkIdType>(srcArray, skipFirst ? 1 : 0); auto dstRange = vtk::DataArrayValueRange<1, vtkIdType>(dstArray, dstBegin, dstEnd); assert(srcRange.size() == dstRange.size()); const DstValueType dOffset = static_cast(offset); std::transform(srcRange.cbegin(), srcRange.cend(), dstRange.begin(), [&](SrcValueType x) -> DstValueType { return static_cast(x) + dOffset; }); } }; } // end anon namespace VTK_ABI_NAMESPACE_BEGIN vtkCellArray::vtkCellArray() { // Default can be changed, to save memory if (vtkCellArray::GetDefaultStorageIs64Bit()) { this->Offsets = vtkSmartPointer::New(); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAOSArray64()->InsertNextValue(0); this->StorageType = StorageTypes::Int64; } else { this->Offsets = vtkSmartPointer::New(); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAOSArray32()->InsertNextValue(0); this->StorageType = StorageTypes::Int32; } } vtkCellArray::~vtkCellArray() = default; vtkStandardNewMacro(vtkCellArray); #ifdef VTK_USE_64BIT_IDS bool vtkCellArray::DefaultStorageIs64Bit = true; #else bool vtkCellArray::DefaultStorageIs64Bit = false; #endif //=================== Begin Legacy Methods =================================== // These should be deprecated at some point as they are confusing or very slow //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetSize() { // We can still compute roughly the same result, so go ahead and do that. return this->GetOffsetsArray()->GetSize() + this->GetConnectivityArray()->GetSize(); } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetNumberOfConnectivityEntries() { // We can still compute roughly the same result, so go ahead and do that. vtkIdType size; this->Dispatch(GetLegacyDataSizeImpl{}, size); return size; } //------------------------------------------------------------------------------ void vtkCellArray::GetCell(vtkIdType loc, vtkIdType& npts, const vtkIdType*& pts) { vtkIdType cellId; this->Dispatch(deprec::LocationToCellIdFunctor{}, loc, cellId); if (cellId < 0) { vtkErrorMacro("Invalid location."); npts = 0; pts = nullptr; return; } this->GetCellAtId(cellId, this->TempCell); npts = this->TempCell->GetNumberOfIds(); pts = this->TempCell->GetPointer(0); } //------------------------------------------------------------------------------ void vtkCellArray::GetCell(vtkIdType loc, vtkIdList* pts) { vtkIdType cellId; this->Dispatch(deprec::LocationToCellIdFunctor{}, loc, cellId); if (cellId < 0) { vtkErrorMacro("Invalid location."); pts->Reset(); return; } this->GetCellAtId(cellId, pts); } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetInsertLocation(int npts) { // It looks like the original implementation of this actually returned the // location of the last cell (of size npts), not the current insert location. vtkIdType insertLoc; this->Dispatch(deprec::GetInsertLocationImpl{}, insertLoc); return insertLoc - npts - 1; } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetTraversalLocation() { vtkIdType loc; this->Dispatch(deprec::CellIdToLocationFunctor{}, this->GetTraversalCellId(), loc); return loc; } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetTraversalLocation(vtkIdType npts) { vtkIdType loc; this->Dispatch(deprec::CellIdToLocationFunctor{}, this->GetTraversalCellId(), loc); return loc - npts - 1; } //------------------------------------------------------------------------------ void vtkCellArray::SetTraversalLocation(vtkIdType loc) { vtkIdType cellId; this->Dispatch(deprec::LocationToCellIdFunctor{}, loc, cellId); if (cellId < 0) { vtkErrorMacro("Invalid location, ignoring."); return; } this->SetTraversalCellId(cellId); } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::EstimateSize(vtkIdType numCells, int maxPtsPerCell) { return numCells * (1 + maxPtsPerCell); } //------------------------------------------------------------------------------ void vtkCellArray::SetNumberOfCells(vtkIdType) { // no-op } //------------------------------------------------------------------------------ void vtkCellArray::ReverseCell(vtkIdType loc) { vtkIdType cellId; this->Dispatch(deprec::LocationToCellIdFunctor{}, loc, cellId); if (cellId < 0) { vtkErrorMacro("Invalid location, ignoring."); return; } this->ReverseCellAtId(cellId); } //------------------------------------------------------------------------------ void vtkCellArray::ReplaceCell(vtkIdType loc, int npts, const vtkIdType pts[]) { vtkIdType cellId; this->Dispatch(deprec::LocationToCellIdFunctor{}, loc, cellId); if (cellId < 0) { vtkErrorMacro("Invalid location, ignoring."); return; } this->ReplaceCellAtId(cellId, static_cast(npts), pts); } //------------------------------------------------------------------------------ vtkIdTypeArray* vtkCellArray::GetData() { this->ExportLegacyFormat(this->LegacyData); return this->LegacyData; } //------------------------------------------------------------------------------ // Specify a group of cells. void vtkCellArray::SetCells(vtkIdType ncells, vtkIdTypeArray* cells) { this->AllocateExact(ncells, cells->GetNumberOfValues() - ncells); this->ImportLegacyFormat(cells); } //=================== End Legacy Methods ===================================== //------------------------------------------------------------------------------ void vtkCellArray::DeepCopy(vtkAbstractCellArray* ca) { auto other = vtkCellArray::SafeDownCast(ca); if (!other) { vtkErrorMacro("Cannot copy from non-vtkCellArray."); return; } if (other == this) { return; } switch (other->GetStorageType()) { case StorageTypes::FixedSizeInt32: { this->UseFixedSize32BitStorage(1 /*dummy, ImplicitDeepCopy will fix it*/); this->GetOffsetsAffineArray32()->ImplicitDeepCopy( AffineArray32::FastDownCast(other->Offsets)); this->Connectivity->DeepCopy(other->Connectivity); this->Modified(); break; } case StorageTypes::FixedSizeInt64: { this->UseFixedSize64BitStorage(1 /*dummy, ImplicitDeepCopy will fix it*/); this->GetOffsetsAffineArray64()->ImplicitDeepCopy( AffineArray64::FastDownCast(other->Offsets)); this->Connectivity->DeepCopy(other->Connectivity); this->Modified(); break; } case StorageTypes::Int32: case StorageTypes::Int64: case StorageTypes::Generic: default: { this->Offsets = vtk::TakeSmartPointer(other->Offsets->NewInstance()); this->Offsets->DeepCopy(other->Offsets); this->Connectivity = vtk::TakeSmartPointer(other->Connectivity->NewInstance()); this->Connectivity->DeepCopy(other->Connectivity); this->StorageType = other->StorageType; this->Modified(); break; } } } //------------------------------------------------------------------------------ void vtkCellArray::ShallowCopy(vtkAbstractCellArray* ca) { auto other = vtkCellArray::SafeDownCast(ca); if (!other) { vtkErrorMacro("Cannot shallow copy from a non-vtkCellArray."); return; } if (other == this) { return; } if (this->Offsets != other->Offsets) { this->Offsets = other->Offsets; this->Modified(); } if (this->Connectivity != other->Connectivity) { this->Connectivity = other->Connectivity; this->Modified(); } this->StorageType = other->StorageType; } //------------------------------------------------------------------------------ void vtkCellArray::Append(vtkCellArray* src, vtkIdType pointOffset) { if (src->GetNumberOfCells() > 0) { this->Dispatch(AppendImpl{}, src, pointOffset); } } //------------------------------------------------------------------------------ void vtkCellArray::Initialize() { this->Dispatch(InitializeImpl{}); this->LegacyData->Initialize(); } //------------------------------------------------------------------------------ vtkCellArrayIterator* vtkCellArray::NewIterator() { vtkCellArrayIterator* iter = vtkCellArrayIterator::New(); iter->SetCellArray(this); iter->GoToFirstCell(); return iter; } //------------------------------------------------------------------------------ void vtkCellArray::SetData(AOSArray32* offsets, AOSArray32* connectivity) { if (!offsets || !connectivity) { vtkErrorMacro("Empty offsets or connectivity array."); return; } if (offsets->GetNumberOfComponents() != 1 || connectivity->GetNumberOfComponents() != 1) { vtkErrorMacro("Only single component arrays may be used for vtkCellArray " "storage."); return; } if (this->Offsets.Get() != offsets) { this->Offsets = offsets; this->Modified(); } if (this->Connectivity.Get() != connectivity) { this->Connectivity = connectivity; this->Modified(); } this->StorageType = StorageTypes::Int32; } //------------------------------------------------------------------------------ void vtkCellArray::SetData(AOSArray64* offsets, AOSArray64* connectivity) { if (!offsets || !connectivity) { vtkErrorMacro("Empty offsets or connectivity array."); return; } if (offsets->GetNumberOfComponents() != 1 || connectivity->GetNumberOfComponents() != 1) { vtkErrorMacro("Only single component arrays may be used for vtkCellArray " "storage."); return; } if (this->Offsets.Get() != offsets) { this->Offsets = offsets; this->Modified(); } if (this->Connectivity.Get() != connectivity) { this->Connectivity = connectivity; this->Modified(); } this->StorageType = StorageTypes::Int64; } //------------------------------------------------------------------------------ void vtkCellArray::SetData(AffineArray32* offsets, AOSArray32* connectivity) { if (!offsets || !connectivity) { vtkErrorMacro("Empty offsets or connectivity array."); return; } if (offsets->GetNumberOfComponents() != 1 || connectivity->GetNumberOfComponents() != 1) { vtkErrorMacro("Only single component arrays may be used for vtkCellArray " "storage."); return; } if (this->Offsets.Get() != offsets) { this->Offsets = offsets; this->Modified(); } if (this->Connectivity.Get() != connectivity) { this->Connectivity = connectivity; this->Modified(); } this->StorageType = StorageTypes::FixedSizeInt32; } //------------------------------------------------------------------------------ void vtkCellArray::SetData(AffineArray64* offsets, AOSArray64* connectivity) { if (!offsets || !connectivity) { vtkErrorMacro("Empty offsets or connectivity array."); return; } if (offsets->GetNumberOfComponents() != 1 || connectivity->GetNumberOfComponents() != 1) { vtkErrorMacro("Only single component arrays may be used for vtkCellArray " "storage."); return; } if (this->Offsets.Get() != offsets) { this->Offsets = offsets; this->Modified(); } if (this->Connectivity.Get() != connectivity) { this->Connectivity = connectivity; this->Modified(); } this->StorageType = StorageTypes::FixedSizeInt64; } VTK_ABI_NAMESPACE_END namespace { struct SetDataGenericImpl { vtkCellArray* CellArray; template void operator()(OffsetsT* offsets, ConnectivityT* connectivity) { this->CellArray->SetData(offsets, connectivity); } }; } // end anon namespace VTK_ABI_NAMESPACE_BEGIN //------------------------------------------------------------------------------ bool vtkCellArray::SetData(vtkDataArray* offsets, vtkDataArray* connectivity) { if (!offsets || !connectivity) { vtkErrorMacro("Empty offsets or connectivity array."); return false; } if (offsets->GetNumberOfComponents() != 1 || connectivity->GetNumberOfComponents() != 1) { vtkErrorMacro("Only single component arrays may be used for vtkCellArray " "storage."); return false; } SetDataGenericImpl worker{ this }; using Dispatcher = vtkArrayDispatch::Dispatch2ByArrayWithSameValueType; if (!Dispatcher::Execute(offsets, connectivity, worker)) { if (this->Offsets.Get() != offsets) { this->Offsets = offsets; this->Modified(); } if (this->Connectivity.Get() != connectivity) { this->Connectivity = connectivity; this->Modified(); } this->StorageType = StorageTypes::Generic; } return true; } namespace { template void SetDataFixedCellSizeImpl( vtkCellArray* cellArray, vtkIdType cellSize, vtkDataArray* connectivity, bool& success) { vtkNew> offsets; offsets->ConstructBackend(cellSize, 0); offsets->SetNumberOfTuples((connectivity->GetNumberOfValues() / cellSize) + 1); success = cellArray->SetData(offsets, connectivity); } } //------------------------------------------------------------------------------ bool vtkCellArray::SetData(vtkIdType cellSize, vtkDataArray* connectivity) { if (connectivity == nullptr || cellSize <= 0) { vtkErrorMacro("Invalid cellSize or connectivity array."); return false; } if ((connectivity->GetNumberOfTuples() % cellSize) != 0) { vtkErrorMacro("Connectivity array size is not suitable for chosen cellSize"); return false; } bool success = false; switch (connectivity->GetDataType()) { vtkTemplateMacro(SetDataFixedCellSizeImpl(this, cellSize, connectivity, success)); default: vtkErrorMacro("Unsupported connectivity array type: " << connectivity->GetDataType()); break; } return success; } //------------------------------------------------------------------------------ void vtkCellArray::Use32BitStorage() { if (this->IsStorage32Bit()) { this->Initialize(); return; } this->Offsets = vtkSmartPointer::New(); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAOSArray32()->InsertNextValue(0); this->StorageType = StorageTypes::Int32; this->Modified(); } //------------------------------------------------------------------------------ void vtkCellArray::Use64BitStorage() { if (this->IsStorage64Bit()) { this->Initialize(); return; } this->Offsets = vtkSmartPointer::New(); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAOSArray64()->InsertNextValue(0); this->StorageType = StorageTypes::Int64; this->Modified(); } //------------------------------------------------------------------------------ void vtkCellArray::UseDefaultStorage() { if (this->GetDefaultStorageIs64Bit()) { this->Use64BitStorage(); } else { this->Use32BitStorage(); } } //------------------------------------------------------------------------------ void vtkCellArray::UseFixedSize32BitStorage(vtkIdType cellSize) { if (this->IsStorageFixedSize32Bit() && this->GetOffsetsAffineArray32()->GetBackend()->Slope == cellSize) { this->Initialize(); return; } this->Offsets = vtkSmartPointer::New(); this->GetOffsetsAffineArray32()->ConstructBackend(cellSize, 0); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAffineArray32()->InsertNextValue(0); this->StorageType = StorageTypes::FixedSizeInt32; this->Modified(); } //------------------------------------------------------------------------------ void vtkCellArray::UseFixedSize64BitStorage(vtkIdType cellSize) { if (this->IsStorageFixedSize64Bit() && this->GetOffsetsAffineArray64()->GetBackend()->Slope == cellSize) { this->Initialize(); return; } this->Offsets = vtkSmartPointer::New(); this->GetOffsetsAffineArray64()->ConstructBackend(cellSize, 0); this->Connectivity = vtkSmartPointer::New(); this->GetOffsetsAffineArray64()->InsertNextValue(0); this->StorageType = StorageTypes::FixedSizeInt64; this->Modified(); } //------------------------------------------------------------------------------ void vtkCellArray::UseFixedSizeDefaultStorage(vtkIdType cellSize) { if (this->GetDefaultStorageIs64Bit()) { this->UseFixedSize64BitStorage(cellSize); } else { this->UseFixedSize32BitStorage(cellSize); } } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertTo32BitStorage() const { if (this->IsStorage32Bit()) { return true; } bool canConvert; this->Dispatch(CanConvert{}, canConvert); return canConvert; } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertTo64BitStorage() const { return true; } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertToFixedSize32BitStorage() const { if (this->IsStorageFixedSize32Bit()) { return true; } const vtkIdType homogeneousCellSize = this->IsHomogeneous(); if (homogeneousCellSize >= 0) { bool result; this->Dispatch(CanConvert{}, result); return result; } return false; } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertToFixedSize64BitStorage() const { if (this->IsStorageFixedSize64Bit()) { return true; } return this->IsHomogeneous() >= 0; } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertToDefaultStorage() const { if (this->GetDefaultStorageIs64Bit()) { return this->CanConvertTo64BitStorage(); } else { return this->CanConvertTo32BitStorage(); } } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertTo32BitStorage() { if (this->IsStorage32Bit()) { return true; } vtkNew offsets; vtkNew conn; bool result; this->Dispatch(ExtractAndInitialize{}, offsets.Get(), conn.Get(), result); if (!result) { return false; } this->SetData(offsets, conn); return true; } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertTo64BitStorage() { if (this->IsStorage64Bit()) { return true; } vtkNew offsets; vtkNew conn; bool result; this->Dispatch(ExtractAndInitialize{}, offsets.Get(), conn.Get(), result); if (!result) { return false; } this->SetData(offsets, conn); return true; } //------------------------------------------------------------------------------ bool vtkCellArray::CanConvertToFixedSizeDefaultStorage() const { if (this->GetDefaultStorageIs64Bit()) { return this->CanConvertToFixedSize64BitStorage(); } else { return this->CanConvertToFixedSize32BitStorage(); } } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertToDefaultStorage() { if (this->GetDefaultStorageIs64Bit()) { return this->ConvertTo64BitStorage(); } else { return this->ConvertTo32BitStorage(); } } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertToFixedSize32BitStorage() { if (this->IsStorageFixedSize32Bit()) { return true; } vtkNew offsets; offsets->ConstructBackend(this->GetCellSize(0), 0); offsets->SetNumberOfValues(this->GetNumberOfCells() + 1); vtkNew conn; bool result; this->Dispatch(ExtractAndInitialize{}, conn.Get(), result); if (!result) { return false; } this->SetData(offsets, conn); return true; } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertToFixedSize64BitStorage() { if (this->IsStorageFixedSize64Bit()) { return true; } vtkNew offsets; offsets->ConstructBackend(this->GetCellSize(0), 0); offsets->SetNumberOfValues(this->GetNumberOfCells() + 1); vtkNew conn; bool result; this->Dispatch(ExtractAndInitialize{}, conn.Get(), result); if (!result) { return false; } this->SetData(offsets, conn); return true; } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertToFixedSizeDefaultStorage() { if (this->GetDefaultStorageIs64Bit()) { return this->ConvertToFixedSize64BitStorage(); } else { return this->ConvertToFixedSize32BitStorage(); } } //------------------------------------------------------------------------------ bool vtkCellArray::ConvertToSmallestStorage() { bool isHomogeneous = this->IsHomogeneous() >= 0; if (!isHomogeneous) { if (this->IsStorage64Bit() && this->CanConvertTo32BitStorage()) { return this->ConvertTo32BitStorage(); } // Already at the smallest possible. return true; } else { if (this->IsStorage64Bit() || this->IsStorageFixedSize64Bit()) { if (this->CanConvertTo32BitStorage()) // Avoid checking for homogeneity again { return this->ConvertToFixedSize32BitStorage(); } return this->ConvertToFixedSize64BitStorage(); } else if (this->IsStorage32Bit()) { return this->ConvertToFixedSize32BitStorage(); } // Already at the smallest possible. return true; } } //------------------------------------------------------------------------------ bool vtkCellArray::AllocateExact(vtkIdType numCells, vtkIdType connectivitySize) { bool result; this->Dispatch(AllocateExactImpl{}, numCells, connectivitySize, result); return result; } //------------------------------------------------------------------------------ bool vtkCellArray::ResizeExact(vtkIdType numCells, vtkIdType connectivitySize) { bool result; this->Dispatch(ResizeExactImpl{}, numCells, connectivitySize, result); return result; } //------------------------------------------------------------------------------ // Returns the size of the largest cell. The size is the number of points // defining the cell. int vtkCellArray::GetMaxCellSize() { const vtkIdType numCells = this->GetNumberOfCells(); // We use THRESHOLD to test if the data size is small enough // to execute the functor serially. This is faster. // and also potentially avoids nested multithreading which creates race conditions. FindMaxCell finder{ this }; vtkSMPTools::For(0, numCells, vtkSMPTools::THRESHOLD, finder); return static_cast(finder.Result); } //------------------------------------------------------------------------------ unsigned long vtkCellArray::GetActualMemorySize() const { return this->GetOffsetsArray()->GetActualMemorySize() + this->GetConnectivityArray()->GetActualMemorySize(); } //------------------------------------------------------------------------------ void vtkCellArray::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "StorageType: "; switch (this->GetStorageType()) { case StorageTypes::Int32: os << "Int32\n"; break; case StorageTypes::Int64: os << "Int64\n"; break; case StorageTypes::FixedSizeInt32: os << "FixedSizeInt32\n"; break; case StorageTypes::FixedSizeInt64: os << "FixedSizeInt64\n"; break; case StorageTypes::Generic: default: os << "Generic\n"; break; } PrintSelfImpl functor; this->Dispatch(functor, os, indent); } //------------------------------------------------------------------------------ void vtkCellArray::PrintDebug(std::ostream& os) { this->Print(os); this->Dispatch(PrintDebugImpl{}, os); } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::GetTraversalCellId() { return this->TraversalCellId; } //------------------------------------------------------------------------------ void vtkCellArray::SetTraversalCellId(vtkIdType cellId) { this->TraversalCellId = cellId; } //------------------------------------------------------------------------------ void vtkCellArray::ReverseCellAtId(vtkIdType cellId) { this->Dispatch(ReverseCellAtIdImpl{}, cellId); } //------------------------------------------------------------------------------ void vtkCellArray::ReplaceCellAtId(vtkIdType cellId, vtkIdList* list) { this->Dispatch(ReplaceCellAtIdImpl{}, cellId, list->GetNumberOfIds(), list->GetPointer(0)); } //------------------------------------------------------------------------------ void vtkCellArray::ReplaceCellAtId( vtkIdType cellId, vtkIdType cellSize, const vtkIdType cellPoints[]) { this->Dispatch(ReplaceCellAtIdImpl{}, cellId, cellSize, cellPoints); } //------------------------------------------------------------------------------ void vtkCellArray::ReplaceCellPointAtId( vtkIdType cellId, vtkIdType cellPointIndex, vtkIdType newPointId) { this->Dispatch(ReplaceCellPointAtIdImpl{}, cellId, cellPointIndex, newPointId); } //------------------------------------------------------------------------------ void vtkCellArray::ExportLegacyFormat(vtkIdTypeArray* data) { vtkIdType size; this->Dispatch(GetLegacyDataSizeImpl{}, size); data->Allocate(size); auto it = vtk::TakeSmartPointer(this->NewIterator()); vtkIdType cellSize; const vtkIdType* cellPoints; for (it->GoToFirstCell(); !it->IsDoneWithTraversal(); it->GoToNextCell()) { it->GetCurrentCell(cellSize, cellPoints); data->InsertNextValue(cellSize); for (vtkIdType i = 0; i < cellSize; ++i) { data->InsertNextValue(cellPoints[i]); } } } //------------------------------------------------------------------------------ void vtkCellArray::ImportLegacyFormat(vtkIdTypeArray* data) { this->ImportLegacyFormat(data->GetPointer(0), data->GetNumberOfValues()); } //------------------------------------------------------------------------------ void vtkCellArray::ImportLegacyFormat(const vtkIdType* data, vtkIdType len) { this->Reset(); this->AppendLegacyFormat(data, len, 0); } //------------------------------------------------------------------------------ void vtkCellArray::AppendLegacyFormat(vtkIdTypeArray* data, vtkIdType ptOffset) { this->AppendLegacyFormat(data->GetPointer(0), data->GetNumberOfValues(), ptOffset); } //------------------------------------------------------------------------------ void vtkCellArray::AppendLegacyFormat(const vtkIdType* data, vtkIdType len, vtkIdType ptOffset) { this->Dispatch(AppendLegacyFormatImpl{}, data, len, ptOffset); } //------------------------------------------------------------------------------ void vtkCellArray::Squeeze() { this->Dispatch(SqueezeImpl{}); // Just delete the legacy buffer. this->LegacyData->Initialize(); } //------------------------------------------------------------------------------ bool vtkCellArray::IsValid() { bool isValid; this->Dispatch(IsValidImpl{}, isValid); return isValid; } //------------------------------------------------------------------------------ vtkIdType vtkCellArray::IsHomogeneous() const { if (this->IsStorageFixedSize32Bit() || this->IsStorageFixedSize64Bit()) { return this->GetNumberOfCells() == 0 ? 0 : this->GetCellSize(0); } vtkIdType isHomogeneous; this->Dispatch(IsHomogeneousImpl{}, isHomogeneous); return isHomogeneous; } VTK_ABI_NAMESPACE_END