You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1521 lines
49 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkCellArray.h
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.
=========================================================================*/
/**
* @class vtkCellArray
* @brief object to represent cell connectivity
*
* vtkCellArray stores dataset topologies as an explicit connectivity table
* listing the point ids that make up each cell.
*
* Internally, the connectivity table is represented as two arrays: Offsets and
* Connectivity.
*
* Offsets is an array of [numCells+1] values indicating the index in the
* Connectivity array where each cell's points start. The last value is always
* the length of the Connectivity array.
*
* The Connectivity array stores the lists of point ids for each cell.
*
* Thus, for a dataset consisting of 2 triangles, a quad, and a line, the
* internal arrays will appear as follows:
*
* ```
* Topology:
* ---------
* Cell 0: Triangle | point ids: {0, 1, 2}
* Cell 1: Triangle | point ids: {5, 7, 2}
* Cell 2: Quad | point ids: {3, 4, 6, 7}
* Cell 4: Line | point ids: {5, 8}
*
* vtkCellArray (current):
* -----------------------
* Offsets: {0, 3, 6, 10, 12}
* Connectivity: {0, 1, 2, 5, 7, 2, 3, 4, 6, 7, 5, 8}
* ```
*
* While this class provides traversal methods (the legacy InitTraversal(),
* GetNextCell() methods, and the newer method GetCellAtId()) these are in
* general not thread-safe. Whenever possible it is preferrable to use a
* local thread-safe, vtkCellArrayIterator object, which can be obtained via:
*
* ```
* auto iter = vtk::TakeSmartPointer(cellArray->NewIterator());
* for (iter->GoToFirstCell(); !iter->IsDoneWithTraversal(); iter->GoToNextCell())
* {
* // do work with iter
* }
* ```
* (Note however that depending on the type and structure of internal
* storage, a cell array iterator may be significantly slower than direct
* traversal over the cell array due to extra data copying. Factors of 3-4X
* are not uncommon. See vtkCellArrayIterator for more information. Also note
* that an iterator may become invalid if the internal vtkCellArray storage
* is modified.)
*
* Other methods are also available for allocation and memory-related
* management; insertion of new cells into the vtkCellArray; and limited
* editing operations such as replacing one cell with a new cell of the
* same size.
*
* The internal arrays may store either 32- or 64-bit values, though most of
* the API will prefer to use vtkIdType to refer to items in these
* arrays. This enables significant memory savings when vtkIdType is 64-bit,
* but 32 bits are sufficient to store all of the values in the connectivity
* table. Using 64-bit storage with a 32-bit vtkIdType is permitted, but
* values too large to fit in a 32-bit signed integer will be truncated when
* accessed through the API. (The particular internal storage type has
* implications on performance depending on vtkIdType. If the internal
* storage is equivalent to vtkIdType, then methods that return pointers to
* arrays of point ids can share the internal storage; otherwise a copy of
* internal memory must be performed.)
*
* Methods for managing the storage type are:
*
* - `bool IsStorage64Bit()`
* - `bool IsStorageShareable() // Can pointers to internal storage be shared`
* - `void Use32BitStorage()`
* - `void Use64BitStorage()`
* - `void UseDefaultStorage() // Depends on vtkIdType`
* - `bool CanConvertTo32BitStorage()`
* - `bool CanConvertTo64BitStorage()`
* - `bool CanConvertToDefaultStorage() // Depends on vtkIdType`
* - `bool ConvertTo32BitStorage()`
* - `bool ConvertTo64BitStorage()`
* - `bool ConvertToDefaultStorage() // Depends on vtkIdType`
* - `bool ConvertToSmallestStorage() // Depends on current values in arrays`
*
* Note that some legacy methods are still available that reflect the
* previous storage format of this data, which embedded the cell sizes into
* the Connectivity array:
*
* ```
* vtkCellArray (legacy):
* ----------------------
* Connectivity: {3, 0, 1, 2, 3, 5, 7, 2, 4, 3, 4, 6, 7, 2, 5, 8}
* |--Cell 0--||--Cell 1--||----Cell 2---||--C3-|
* ```
*
* The methods require an external lookup table to allow random access, which
* was historically stored in the vtkCellTypes object. The following methods in
* vtkCellArray still support this style of indexing for compatibility
* purposes, but these are slow as they must perform some complex computations
* to convert the old "location" into the new "offset" and should be avoided.
* These methods (and their modern equivalents) are:
*
* - GetCell (Prefer GetCellAtId)
* - GetInsertLocation (Prefer GetNumberOfCells)
* - GetTraversalLocation (Prefer GetTraversalCellId, or better, NewIterator)
* - SetTraversalLocation (Prefer SetTraversalLocation, or better, NewIterator)
* - ReverseCell (Prefer ReverseCellAtId)
* - ReplaceCell (Prefer ReplaceCellAtId)
* - SetCells (Use ImportLegacyFormat, or SetData)
* - GetData (Use ExportLegacyFormat, or Get[Offsets|Connectivity]Array[|32|64])
*
* Some other legacy methods were completely removed, such as GetPointer() /
* WritePointer(), since they are cannot be effectively emulated under the
* current design. If external code needs to support both the old and new
* version of the vtkCellArray API, the VTK_CELL_ARRAY_V2 preprocessor
* definition may be used to detect which API is being compiled against.
*
* @sa vtkCellTypes vtkCellLinks
*/
#ifndef vtkCellArray_h
#define vtkCellArray_h
#include "vtkCommonDataModelModule.h" // For export macro
#include "vtkObject.h"
#include "vtkAOSDataArrayTemplate.h" // Needed for inline methods
#include "vtkCell.h" // Needed for inline methods
#include "vtkDataArrayRange.h" // Needed for inline methods
#include "vtkSmartPointer.h" // For vtkSmartPointer
#include "vtkTypeInt32Array.h" // Needed for inline methods
#include "vtkTypeInt64Array.h" // Needed for inline methods
#include "vtkTypeList.h" // Needed for ArrayList definition
#include <cassert> // for assert
#include <initializer_list> // for API
#include <type_traits> // for std::is_same
#include <utility> // for std::forward
/**
* @def VTK_CELL_ARRAY_V2
* @brief This preprocessor definition indicates that the updated vtkCellArray
* is being used. It may be used to conditionally switch between old and new
* API when both must be supported.
*
* For example:
*
* ```
* vtkIdType npts;
*
* #ifdef VTK_CELL_ARRAY_V2
* const vtkIdType *pts;
* #else // VTK_CELL_ARRAY_V2
* vtkIdType *pts'
* #endif // VTK_CELL_ARRAY_V2
*
* cellArray->GetCell(legacyLocation, npts, pts);
* ```
*/
#define VTK_CELL_ARRAY_V2
class vtkCellArrayIterator;
class vtkIdTypeArray;
class VTKCOMMONDATAMODEL_EXPORT vtkCellArray : public vtkObject
{
public:
using ArrayType32 = vtkTypeInt32Array;
using ArrayType64 = vtkTypeInt64Array;
//@{
/**
* Standard methods for instantiation, type information, and
* printing.
*/
static vtkCellArray* New();
vtkTypeMacro(vtkCellArray, vtkObject);
void PrintSelf(ostream& os, vtkIndent indent) override;
void PrintDebug(ostream& os);
//@}
/**
* List of possible array types used for storage. May be used with
* vtkArrayDispatch::Dispatch[2]ByArray to process internal arrays.
* Both the Connectivity and Offset arrays are guaranteed to have the same
* type.
*
* @sa vtkCellArray::Visit() for a simpler mechanism.
*/
using StorageArrayList = vtkTypeList::Create<ArrayType32, ArrayType64>;
/**
* List of possible ArrayTypes that are compatible with internal storage.
* Single component AOS-layout arrays holding one of these types may be
* passed to the method SetData to setup the cell array state.
*
* This can be used with vtkArrayDispatch::DispatchByArray, etc to
* check input arrays before assigning them to a cell array.
*/
using InputArrayList =
typename vtkTypeList::Unique<vtkTypeList::Create<vtkAOSDataArrayTemplate<int>,
vtkAOSDataArrayTemplate<long>, vtkAOSDataArrayTemplate<long long> > >::Result;
/**
* Allocate memory.
*
* This currently allocates both the offsets and connectivity arrays to @a sz.
*
* @note It is preferrable to use AllocateEstimate(numCells, maxCellSize)
* or AllocateExact(numCells, connectivitySize) instead.
*/
vtkTypeBool Allocate(vtkIdType sz, vtkIdType vtkNotUsed(ext) = 1000)
{
return this->AllocateExact(sz, sz) ? 1 : 0;
}
/**
* @brief Pre-allocate memory in internal data structures. Does not change
* the number of cells, only the array capacities. Existing data is NOT
* preserved.
* @param numCells The number of expected cells in the dataset.
* @param maxCellSize The number of points per cell to allocate memory for.
* @return True if allocation succeeds.
* @sa Squeeze AllocateExact AllocateCopy
*/
bool AllocateEstimate(vtkIdType numCells, vtkIdType maxCellSize)
{
return this->AllocateExact(numCells, numCells * maxCellSize);
}
/**
* @brief Pre-allocate memory in internal data structures. Does not change
* the number of cells, only the array capacities. Existing data is NOT
* preserved.
* @param numCells The number of expected cells in the dataset.
* @param connectivitySize The total number of pointIds stored for all cells.
* @return True if allocation succeeds.
* @sa Squeeze AllocateEstimate AllocateCopy
*/
bool AllocateExact(vtkIdType numCells, vtkIdType connectivitySize);
/**
* @brief Pre-allocate memory in internal data structures to match the used
* size of the input vtkCellArray. Does not change
* the number of cells, only the array capacities. Existing data is NOT
* preserved.
* @param other The vtkCellArray to use as a reference.
* @return True if allocation succeeds.
* @sa Squeeze AllocateEstimate AllocateExact
*/
bool AllocateCopy(vtkCellArray* other)
{
return this->AllocateExact(other->GetNumberOfCells(), other->GetNumberOfConnectivityIds());
}
/**
* @brief ResizeExact() resizes the internal structures to hold @a numCells
* total cell offsets and @a connectivitySize total pointIds. Old data is
* preserved, and newly-available memory is not initialized.
*
* @warning For advanced use only. You probably want an Allocate method.
*
* @return True if allocation succeeds.
*/
bool ResizeExact(vtkIdType numCells, vtkIdType connectivitySize);
/**
* Free any memory and reset to an empty state.
*/
void Initialize();
/**
* Reuse list. Reset to initial state without freeing memory.
*/
void Reset();
/**
* Reclaim any extra memory while preserving data.
*
* @sa ConvertToSmallestStorage
*/
void Squeeze();
/**
* Check that internal storage is consistent and in a valid state.
*
* Specifically, this function returns true if and only if:
* - The offset and connectivity arrays have exactly one component.
* - The offset array has at least one value and starts at 0.
* - The offset array values never decrease.
* - The connectivity array has as many entries as the last value in the
* offset array.
*/
bool IsValid();
/**
* Get the number of cells in the array.
*/
vtkIdType GetNumberOfCells() const
{
if (this->Storage.Is64Bit())
{
return this->Storage.GetArrays64().Offsets->GetNumberOfValues() - 1;
}
else
{
return this->Storage.GetArrays32().Offsets->GetNumberOfValues() - 1;
}
}
/**
* Get the number of elements in the offsets array. This will be the number of
* cells + 1.
*/
vtkIdType GetNumberOfOffsets() const
{
if (this->Storage.Is64Bit())
{
return this->Storage.GetArrays64().Offsets->GetNumberOfValues();
}
else
{
return this->Storage.GetArrays32().Offsets->GetNumberOfValues();
}
}
/**
* Get the size of the connectivity array that stores the point ids.
* @note Do not confuse this with the deprecated
* GetNumberOfConnectivityEntries(), which refers to the legacy memory
* layout.
*/
vtkIdType GetNumberOfConnectivityIds() const
{
if (this->Storage.Is64Bit())
{
return this->Storage.GetArrays64().Connectivity->GetNumberOfValues();
}
else
{
return this->Storage.GetArrays32().Connectivity->GetNumberOfValues();
}
}
/**
* @brief NewIterator returns a new instance of vtkCellArrayIterator that
* is initialized to point at the first cell's data. The caller is responsible
* for Delete()'ing the object.
*/
VTK_NEWINSTANCE vtkCellArrayIterator* NewIterator();
#ifndef __VTK_WRAP__ // The wrappers have issues with some of these templates
/**
* Set the internal data arrays to the supplied offsets and connectivity
* arrays.
*
* Note that the input arrays may be copied and not used directly. To avoid
* copying, use vtkIdTypeArray, vtkCellArray::ArrayType32, or
* vtkCellArray::ArrayType64.
*
* @{
*/
void SetData(vtkTypeInt32Array* offsets, vtkTypeInt32Array* connectivity);
void SetData(vtkTypeInt64Array* offsets, vtkTypeInt64Array* connectivity);
void SetData(vtkIdTypeArray* offsets, vtkIdTypeArray* connectivity);
void SetData(vtkAOSDataArrayTemplate<int>* offsets, vtkAOSDataArrayTemplate<int>* connectivity);
void SetData(vtkAOSDataArrayTemplate<long>* offsets, vtkAOSDataArrayTemplate<long>* connectivity);
void SetData(
vtkAOSDataArrayTemplate<long long>* offsets, vtkAOSDataArrayTemplate<long long>* connectivity);
/**@}*/
#endif // __VTK_WRAP__
/**
* Sets the internal arrays to the supplied offsets and connectivity arrays.
*
* This is a convenience method, and may fail if the following conditions
* are not met:
*
* - Both arrays must be of the same type.
* - The array type must be one of the types in InputArrayList.
*
* If invalid arrays are passed in, an error is logged and the function
* will return false.
*/
bool SetData(vtkDataArray* offsets, vtkDataArray* connectivity);
/**
* @return True if the internal storage is using 64 bit arrays. If false,
* the storage is using 32 bit arrays.
*/
bool IsStorage64Bit() const { return this->Storage.Is64Bit(); }
/**
* @return True if the internal storage can be shared as a
* pointer to vtkIdType, i.e., the type and organization of internal
* storage is such that copying of data can be avoided, and instead
* a pointer to vtkIdType can be used.
*/
bool IsStorageShareable() const
{
if (this->Storage.Is64Bit())
{
return this->Storage.GetArrays64().ValueTypeIsSameAsIdType;
}
else
{
return this->Storage.GetArrays32().ValueTypeIsSameAsIdType;
}
}
/**
* Initialize internal data structures to use 32- or 64-bit storage.
* If selecting default storage, the storage depends on the VTK_USE_64BIT_IDS
* setting.
*
* All existing data is erased.
* @{
*/
void Use32BitStorage();
void Use64BitStorage();
void UseDefaultStorage();
/**@}*/
/**
* Check if the existing data can safely be converted to use 32- or 64- bit
* storage. Ensures that all values can be converted to the target storage
* without truncating.
* If selecting default storage, the storage depends on the VTK_USE_64BIT_IDS
* setting.
* @{
*/
bool CanConvertTo32BitStorage() const;
bool CanConvertTo64BitStorage() const;
bool CanConvertToDefaultStorage() const;
/**@}*/
/**
* Convert internal data structures to use 32- or 64-bit storage.
*
* If selecting default storage, the storage depends on the VTK_USE_64BIT_IDS
* setting.
*
* If selecting smallest storage, the data is checked to see what the smallest
* safe storage for the existing data is, and then converts to it.
*
* Existing data is preserved.
*
* @return True on success, false on failure. If this algorithm fails, the
* cell array will be in an unspecified state.
*
* @{
*/
bool ConvertTo32BitStorage();
bool ConvertTo64BitStorage();
bool ConvertToDefaultStorage();
bool ConvertToSmallestStorage();
/**@}*/
/**
* Return the array used to store cell offsets. The 32/64 variants are only
* valid when IsStorage64Bit() returns the appropriate value.
* @{
*/
vtkDataArray* GetOffsetsArray()
{
if (this->Storage.Is64Bit())
{
return this->GetOffsetsArray64();
}
else
{
return this->GetOffsetsArray32();
}
}
ArrayType32* GetOffsetsArray32() { return this->Storage.GetArrays32().Offsets; }
ArrayType64* GetOffsetsArray64() { return this->Storage.GetArrays64().Offsets; }
/**@}*/
/**
* Return the array used to store the point ids that define the cells'
* connectivity. The 32/64 variants are only valid when IsStorage64Bit()
* returns the appropriate value.
* @{
*/
vtkDataArray* GetConnectivityArray()
{
if (this->Storage.Is64Bit())
{
return this->GetConnectivityArray64();
}
else
{
return this->GetConnectivityArray32();
}
}
ArrayType32* GetConnectivityArray32() { return this->Storage.GetArrays32().Connectivity; }
ArrayType64* GetConnectivityArray64() { return this->Storage.GetArrays64().Connectivity; }
/**@}*/
/**
* Check if all cells have the same number of vertices.
*
* The return value is coded as:
* * -1 = heterogeneous
* * 0 = Cell array empty
* * n (positive integer) = homogeneous array of cell size n
*/
vtkIdType IsHomogeneous();
/**
* @warning This method is not thread-safe. Consider using the NewIterator()
* iterator instead.
*
* InitTraversal() initializes the traversal of the list of cells.
*
* @note This method is not thread-safe and has tricky syntax to use
* correctly. Prefer the use of vtkCellArrayIterator (see NewIterator()).
*/
void InitTraversal();
/**
* @warning This method is not thread-safe. Consider using the NewIterator()
* iterator instead.
*
* GetNextCell() gets the next cell in the list. If end of list
* is encountered, 0 is returned. A value of 1 is returned whenever
* npts and pts have been updated without error.
*
* Do not modify the returned @a pts pointer, as it may point to shared
* memory.
*
* @note This method is not thread-safe and has tricky syntax to use
* correctly. Prefer the use of vtkCellArrayIterator (see NewIterator()).
*/
int GetNextCell(vtkIdType& npts, vtkIdType const*& pts) VTK_SIZEHINT(pts, npts);
/**
* @warning This method is not thread-safe. Consider using the NewIterator()
* iterator instead.
*
* GetNextCell() gets the next cell in the list. If end of list is
* encountered, 0 is returned.
*
* @note This method is not thread-safe and has tricky syntax to use
* correctly. Prefer the use of vtkCellArrayIterator (see NewIterator()).
*/
int GetNextCell(vtkIdList* pts);
/**
* Return the point ids for the cell at @a cellId.
*
* @warning Subsequent calls to this method may invalidate previous call
* results if the internal storage type is not the same as vtkIdType and
* cannot be shared through the @a cellPoints pointer. In other words, the
* method may not be thread safe. Check if shareable (using
* IsStorageShareable()), or use a vtkCellArrayIterator to guarantee thread
* safety.
*/
void GetCellAtId(vtkIdType cellId, vtkIdType& cellSize, vtkIdType const*& cellPoints)
VTK_SIZEHINT(cellPoints, cellSize) VTK_EXPECTS(0 <= cellId && cellId < GetNumberOfCells());
/**
* Return the point ids for the cell at @a cellId. This always copies
* the cell ids (i.e., the list of points @a pts into the supplied
* vtkIdList). This method is thread safe.
*/
void GetCellAtId(vtkIdType cellId, vtkIdList* pts)
VTK_EXPECTS(0 <= cellId && cellId < GetNumberOfCells());
/**
* Return the size of the cell at @a cellId.
*/
vtkIdType GetCellSize(const vtkIdType cellId) const;
/**
* Insert a cell object. Return the cell id of the cell.
*/
vtkIdType InsertNextCell(vtkCell* cell);
/**
* Create a cell by specifying the number of points and an array of point
* id's. Return the cell id of the cell.
*/
vtkIdType InsertNextCell(vtkIdType npts, const vtkIdType* pts) VTK_SIZEHINT(pts, npts);
/**
* Create a cell by specifying a list of point ids. Return the cell id of
* the cell.
*/
vtkIdType InsertNextCell(vtkIdList* pts);
/**
* Overload that allows `InsertNextCell({0, 1, 2})` syntax.
*
* @warning This approach is useful for testing, but beware that trying to
* pass a single value (eg. `InsertNextCell({3})`) will call the
* `InsertNextCell(int)` overload instead.
*/
vtkIdType InsertNextCell(const std::initializer_list<vtkIdType>& cell)
{
return this->InsertNextCell(static_cast<vtkIdType>(cell.size()), cell.begin());
}
/**
* Create cells by specifying a count of total points to be inserted, and
* then adding points one at a time using method InsertCellPoint(). If you
* don't know the count initially, use the method UpdateCellCount() to
* complete the cell. Return the cell id of the cell.
*/
vtkIdType InsertNextCell(int npts);
/**
* Used in conjunction with InsertNextCell(npts) to add another point
* to the list of cells.
*/
void InsertCellPoint(vtkIdType id);
/**
* Used in conjunction with InsertNextCell(int npts) and InsertCellPoint() to
* update the number of points defining the cell.
*/
void UpdateCellCount(int npts);
/**
* Get/Set the current cellId for traversal.
*
* @note This method is not thread-safe and has tricky syntax to use
* correctly. Prefer the use of vtkCellArrayIterator (see NewIterator()).
* @{
*/
vtkIdType GetTraversalCellId();
void SetTraversalCellId(vtkIdType cellId);
/**@}*/
/**
* Reverses the order of the point ids for the specified cell.
*/
void ReverseCellAtId(vtkIdType cellId) VTK_EXPECTS(0 <= cellId && cellId < GetNumberOfCells());
/**
* Replaces the point ids for the specified cell with the supplied list.
*
* @warning This can ONLY replace the cell if the size does not change.
* Attempting to change cell size through this method will have undefined
* results.
* @{
*/
void ReplaceCellAtId(vtkIdType cellId, vtkIdList* list);
void ReplaceCellAtId(vtkIdType cellId, vtkIdType cellSize, const vtkIdType* cellPoints)
VTK_EXPECTS(0 <= cellId && cellId < GetNumberOfCells()) VTK_SIZEHINT(cellPoints, cellSize);
/**@}*/
/**
* Overload that allows `ReplaceCellAtId(cellId, {0, 1, 2})` syntax.
*
* @warning This can ONLY replace the cell if the size does not change.
* Attempting to change cell size through this method will have undefined
* results.
*/
void ReplaceCellAtId(vtkIdType cellId, const std::initializer_list<vtkIdType>& cell)
{
return this->ReplaceCellAtId(cellId, static_cast<vtkIdType>(cell.size()), cell.begin());
}
/**
* Returns the size of the largest cell. The size is the number of points
* defining the cell.
*/
int GetMaxCellSize();
/**
* Perform a deep copy (no reference counting) of the given cell array.
*/
void DeepCopy(vtkCellArray* ca);
/**
* Shallow copy @a ca into this cell array.
*/
void ShallowCopy(vtkCellArray* ca);
/**
* Append cells from src into this. Point ids are offset by @a pointOffset.
*/
void Append(vtkCellArray* src, vtkIdType pointOffset = 0);
/**
* Fill @a data with the old-style vtkCellArray data layout, e.g.
*
* ```
* { n0, p0_0, p0_1, ..., p0_n, n1, p1_0, p1_1, ..., p1_n, ... }
* ```
*
* where `n0` is the number of points in cell 0, and `pX_Y` is the Y'th point
* in cell X.
*/
void ExportLegacyFormat(vtkIdTypeArray* data);
/**
* Import an array of data with the legacy vtkCellArray layout, e.g.:
*
* ```
* { n0, p0_0, p0_1, ..., p0_n, n1, p1_0, p1_1, ..., p1_n, ... }
* ```
*
* where `n0` is the number of points in cell 0, and `pX_Y` is the Y'th point
* in cell X.
* @{
*/
void ImportLegacyFormat(vtkIdTypeArray* data);
void ImportLegacyFormat(const vtkIdType* data, vtkIdType len) VTK_SIZEHINT(data, len);
/** @} */
/**
* Append an array of data with the legacy vtkCellArray layout, e.g.:
*
* ```
* { n0, p0_0, p0_1, ..., p0_n, n1, p1_0, p1_1, ..., p1_n, ... }
* ```
*
* where `n0` is the number of points in cell 0, and `pX_Y` is the Y'th point
* in cell X.
* @{
*/
void AppendLegacyFormat(vtkIdTypeArray* data, vtkIdType ptOffset = 0);
void AppendLegacyFormat(const vtkIdType* data, vtkIdType len, vtkIdType ptOffset = 0)
VTK_SIZEHINT(data, len);
/** @} */
/**
* Return the memory in kibibytes (1024 bytes) consumed by this cell array. Used to
* support streaming and reading/writing data. The value returned is
* guaranteed to be greater than or equal to the memory required to
* actually represent the data represented by this object. The
* information returned is valid only after the pipeline has
* been updated.
*/
unsigned long GetActualMemorySize() const;
// The following code is used to support
// The wrappers get understandably confused by some of the template code below
#ifndef __VTK_WRAP__
// Holds connectivity and offset arrays of the given ArrayType.
template <typename ArrayT>
struct VisitState
{
using ArrayType = ArrayT;
using ValueType = typename ArrayType::ValueType;
using CellRangeType = decltype(vtk::DataArrayValueRange<1>(std::declval<ArrayType>()));
// We can't just use is_same here, since binary compatible representations
// (e.g. int and long) are distinct types. Instead, ensure that ValueType
// is a signed integer the same size as vtkIdType.
// If this value is true, ValueType pointers may be safely converted to
// vtkIdType pointers via reinterpret cast.
static constexpr bool ValueTypeIsSameAsIdType = std::is_integral<ValueType>::value &&
std::is_signed<ValueType>::value && (sizeof(ValueType) == sizeof(vtkIdType));
ArrayType* GetOffsets() { return this->Offsets; }
const ArrayType* GetOffsets() const { return this->Offsets; }
ArrayType* GetConnectivity() { return this->Connectivity; }
const ArrayType* GetConnectivity() const { return this->Connectivity; }
vtkIdType GetNumberOfCells() const;
vtkIdType GetBeginOffset(vtkIdType cellId) const;
vtkIdType GetEndOffset(vtkIdType cellId) const;
vtkIdType GetCellSize(vtkIdType cellId) const;
CellRangeType GetCellRange(vtkIdType cellId);
friend class vtkCellArray;
protected:
VisitState()
: Connectivity(vtkSmartPointer<ArrayType>::New())
, Offsets(vtkSmartPointer<ArrayType>::New())
{
this->Offsets->InsertNextValue(0);
}
~VisitState() = default;
vtkSmartPointer<ArrayType> Connectivity;
vtkSmartPointer<ArrayType> Offsets;
private:
VisitState(const VisitState&) = delete;
VisitState& operator=(const VisitState&) = delete;
};
private: // Helpers that allow Visit to return a value:
template <typename Functor, typename... Args>
using GetReturnType = decltype(
std::declval<Functor>()(std::declval<VisitState<ArrayType32>&>(), std::declval<Args>()...));
template <typename Functor, typename... Args>
struct ReturnsVoid : std::is_same<GetReturnType<Functor, Args...>, void>
{
};
public:
/**
* @warning Advanced use only.
*
* The Visit methods allow efficient bulk modification of the vtkCellArray
* internal arrays by dispatching a functor with the current storage arrays.
* The simplest functor is of the form:
*
* ```
* // Functor definition:
* struct Worker
* {
* template <typename CellStateT>
* void operator()(CellStateT &state)
* {
* // Do work on state object
* }
* };
*
* // Functor usage:
* vtkCellArray *cellArray = ...;
* cellArray->Visit(Worker{});
* ```
*
* where `state` is an instance of the vtkCellArray::VisitState<ArrayT> class,
* instantiated for the current storage type of the cell array. See that
* class for usage details.
*
* The functor may also:
* - Return a value from `operator()`
* - Pass additional arguments to `operator()`
* - Hold state.
*
* A more advanced functor that does these things is shown below, along
* with its usage. This functor scans a range of cells and returns the largest
* cell's id:
*
* ```
* struct FindLargestCellInRange
* {
* template <typename CellStateT>
* vtkIdType operator()(CellStateT &state,
* vtkIdType rangeBegin,
* vtkIdType rangeEnd)
* {
* vtkIdType largest = rangeBegin;
* vtkIdType largestSize = state.GetCellSize(rangeBegin);
* ++rangeBegin;
* for (; rangeBegin < rangeEnd; ++rangeBegin)
* {
* const vtkIdType curSize = state.GetCellSize(rangeBegin);
* if (curSize > largestSize)
* {
* largest = rangeBegin;
* largestSize = curSize;
* }
* }
*
* return largest;
* }
* };
*
* // Usage:
* // Scan cells in range [128, 1024) and return the id of the largest.
* vtkCellArray cellArray = ...;
* vtkIdType largest = cellArray->Visit(FindLargestCellInRange{},
* 128, 1024);
* ```
* @{
*/
template <typename Functor, typename... Args,
typename = typename std::enable_if<ReturnsVoid<Functor, Args...>::value>::type>
void Visit(Functor&& functor, Args&&... args)
{
if (this->Storage.Is64Bit())
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);
}
else
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);
}
}
template <typename Functor, typename... Args,
typename = typename std::enable_if<ReturnsVoid<Functor, Args...>::value>::type>
void Visit(Functor&& functor, Args&&... args) const
{
if (this->Storage.Is64Bit())
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);
}
else
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);
}
}
template <typename Functor, typename... Args,
typename = typename std::enable_if<!ReturnsVoid<Functor, Args...>::value>::type>
GetReturnType<Functor, Args...> Visit(Functor&& functor, Args&&... args)
{
if (this->Storage.Is64Bit())
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
return functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);
}
else
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
return functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);
}
}
template <typename Functor, typename... Args,
typename = typename std::enable_if<!ReturnsVoid<Functor, Args...>::value>::type>
GetReturnType<Functor, Args...> Visit(Functor&& functor, Args&&... args) const
{
if (this->Storage.Is64Bit())
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
return functor(this->Storage.GetArrays64(), std::forward<Args>(args)...);
}
else
{
// If you get an error on the next line, a call to Visit(functor, Args...)
// is being called with arguments that do not match the functor's call
// signature. See the Visit documentation for details.
return functor(this->Storage.GetArrays32(), std::forward<Args>(args)...);
}
}
/** @} */
#endif // __VTK_WRAP__
//=================== Begin Legacy Methods ===================================
// These should be deprecated at some point as they are confusing or very slow
/**
* Set the number of cells in the array.
* DO NOT do any kind of allocation, advanced use only.
*
* @note This call has no effect.
*/
virtual void SetNumberOfCells(vtkIdType);
/**
* Utility routines help manage memory of cell array. EstimateSize()
* returns a value used to initialize and allocate memory for array based
* on number of cells and maximum number of points making up cell. If
* every cell is the same size (in terms of number of points), then the
* memory estimate is guaranteed exact. (If not exact, use Squeeze() to
* reclaim any extra memory.)
*
* @note This method was often misused (e.g. called alone and then
* discarding the result). Use AllocateEstimate directly instead.
*/
vtkIdType EstimateSize(vtkIdType numCells, int maxPtsPerCell);
/**
* Get the size of the allocated connectivity array.
*
* @warning This returns the allocated capacity of the internal arrays as a
* number of elements, NOT the number of elements in use.
*
* @note Method incompatible with current internal storage.
*/
vtkIdType GetSize();
/**
* Return the size of the array that would be returned from
* ExportLegacyFormat().
*
* @note Method incompatible with current internal storage.
*/
vtkIdType GetNumberOfConnectivityEntries();
/**
* Internal method used to retrieve a cell given a legacy offset location.
*
* @warning Subsequent calls to this method may invalidate previous call
* results.
*
* @note The location-based API is now a super-slow compatibility layer.
* Prefer GetCellAtId.
*/
void GetCell(vtkIdType loc, vtkIdType& npts, const vtkIdType*& pts)
VTK_EXPECTS(0 <= loc && loc < GetNumberOfConnectivityEntries()) VTK_SIZEHINT(pts, npts);
/**
* Internal method used to retrieve a cell given a legacy offset location.
*
* @note The location-based API is now a super-slow compatibility layer.
* Prefer GetCellAtId.
*/
void GetCell(vtkIdType loc, vtkIdList* pts)
VTK_EXPECTS(0 <= loc && loc < GetNumberOfConnectivityEntries());
/**
* Computes the current legacy insertion location within the internal array.
* Used in conjunction with GetCell(int loc,...).
*
* @note The location-based API is now a super-slow compatibility layer.
*/
vtkIdType GetInsertLocation(int npts);
/**
* Get/Set the current traversal legacy location.
*
* @note The location-based API is now a super-slow compatibility layer.
* Prefer Get/SetTraversalCellId.
* @{
*/
vtkIdType GetTraversalLocation();
vtkIdType GetTraversalLocation(vtkIdType npts);
void SetTraversalLocation(vtkIdType loc);
/**@}*/
/**
* Special method inverts ordering of cell at the specified legacy location.
* Must be called carefully or the cell topology may be corrupted.
*
* @note The location-based API is now a super-slow compatibility layer.
* Prefer ReverseCellAtId;
*/
void ReverseCell(vtkIdType loc) VTK_EXPECTS(0 <= loc && loc < GetNumberOfConnectivityEntries());
/**
* Replace the point ids of the cell at the legacy location with a different
* list of point ids. Calling this method does not mark the vtkCellArray as
* modified. This is the responsibility of the caller and may be done after
* multiple calls to ReplaceCell. This call does not support changing the
* number of points in the cell -- the caller must ensure that the target
* cell has npts points.
*
* @note The location-based API is now a super-slow compatibility layer.
* Prefer ReplaceCellAtId.
*/
void ReplaceCell(vtkIdType loc, int npts, const vtkIdType pts[])
VTK_EXPECTS(0 <= loc && loc < GetNumberOfConnectivityEntries()) VTK_SIZEHINT(pts, npts);
/**
* Define multiple cells by providing a connectivity list. The list is in
* the form (npts,p0,p1,...p(npts-1), repeated for each cell). Be careful
* using this method because it discards the old cells, and anything
* referring these cells becomes invalid (for example, if BuildCells() has
* been called see vtkPolyData). The traversal location is reset to the
* beginning of the list; the insertion location is set to the end of the
* list.
*
* @warning The vtkCellArray will not hold a reference to `cells`. This
* function merely calls ImportLegacyFormat.
*
* @note Use ImportLegacyFormat or SetData instead.
*/
void SetCells(vtkIdType ncells, vtkIdTypeArray* cells);
/**
* Return the underlying data as a data array.
*
* @warning The returned array is not the actual internal representation used
* by vtkCellArray. Modifications to the returned array will not change the
* vtkCellArray's topology.
*
* @note Use ExportLegacyFormat, or GetOffsetsArray/GetConnectivityArray
* instead.
*/
vtkIdTypeArray* GetData();
//=================== End Legacy Methods =====================================
friend class vtkCellArrayIterator;
protected:
vtkCellArray();
~vtkCellArray() override;
// Encapsulates storage of the internal arrays as a discriminated union
// between 32-bit and 64-bit storage.
struct Storage
{
// Union type that switches 32 and 64 bit array storage
union ArraySwitch {
ArraySwitch() {} // handled by Storage
~ArraySwitch() {} // handle by Storage
VisitState<ArrayType32> Int32;
VisitState<ArrayType64> Int64;
};
Storage()
{
// Default to the compile-time setting:
#ifdef VTK_USE_64BIT_IDS
new (&this->Arrays.Int64) VisitState<ArrayType64>;
this->StorageIs64Bit = true;
#else // VTK_USE_64BIT_IDS
new (&this->Arrays.Int32) VisitState<ArrayType32>;
this->StorageIs64Bit = false;
#endif // VTK_USE_64BIT_IDS
}
~Storage()
{
if (this->StorageIs64Bit)
{
this->Arrays.Int64.~VisitState();
}
else
{
this->Arrays.Int32.~VisitState();
}
}
// Switch the internal arrays to be 32-bit. Any old data is lost. Returns
// true if the storage changes.
bool Use32BitStorage()
{
if (!this->StorageIs64Bit)
{
return false;
}
this->Arrays.Int64.~VisitState();
new (&this->Arrays.Int32) VisitState<ArrayType32>;
this->StorageIs64Bit = false;
return true;
}
// Switch the internal arrays to be 64-bit. Any old data is lost. Returns
// true if the storage changes.
bool Use64BitStorage()
{
if (this->StorageIs64Bit)
{
return false;
}
this->Arrays.Int32.~VisitState();
new (&this->Arrays.Int64) VisitState<ArrayType64>;
this->StorageIs64Bit = true;
return true;
}
// Returns true if the storage is currently configured to be 64 bit.
bool Is64Bit() const { return this->StorageIs64Bit; }
// Get the VisitState for 32-bit arrays
VisitState<ArrayType32>& GetArrays32()
{
assert(!this->StorageIs64Bit);
return this->Arrays.Int32;
}
const VisitState<ArrayType32>& GetArrays32() const
{
assert(!this->StorageIs64Bit);
return this->Arrays.Int32;
}
// Get the VisitState for 64-bit arrays
VisitState<ArrayType64>& GetArrays64()
{
assert(this->StorageIs64Bit);
return this->Arrays.Int64;
}
const VisitState<ArrayType64>& GetArrays64() const
{
assert(this->StorageIs64Bit);
return this->Arrays.Int64;
}
private:
// Access restricted to ensure proper union construction/destruction thru
// API.
ArraySwitch Arrays;
bool StorageIs64Bit;
};
Storage Storage;
vtkNew<vtkIdList> TempCell;
vtkIdType TraversalCellId{ 0 };
vtkNew<vtkIdTypeArray> LegacyData; // For GetData().
private:
vtkCellArray(const vtkCellArray&) = delete;
void operator=(const vtkCellArray&) = delete;
};
template <typename ArrayT>
vtkIdType vtkCellArray::VisitState<ArrayT>::GetNumberOfCells() const
{
return this->Offsets->GetNumberOfValues() - 1;
}
template <typename ArrayT>
vtkIdType vtkCellArray::VisitState<ArrayT>::GetBeginOffset(vtkIdType cellId) const
{
return static_cast<vtkIdType>(this->Offsets->GetValue(cellId));
}
template <typename ArrayT>
vtkIdType vtkCellArray::VisitState<ArrayT>::GetEndOffset(vtkIdType cellId) const
{
return static_cast<vtkIdType>(this->Offsets->GetValue(cellId + 1));
}
template <typename ArrayT>
vtkIdType vtkCellArray::VisitState<ArrayT>::GetCellSize(vtkIdType cellId) const
{
return this->GetEndOffset(cellId) - this->GetBeginOffset(cellId);
}
template <typename ArrayT>
typename vtkCellArray::VisitState<ArrayT>::CellRangeType
vtkCellArray::VisitState<ArrayT>::GetCellRange(vtkIdType cellId)
{
return vtk::DataArrayValueRange<1>(
this->GetConnectivity(), this->GetBeginOffset(cellId), this->GetEndOffset(cellId));
}
namespace vtkCellArray_detail
{
struct InsertNextCellImpl
{
// Insert full cell
template <typename CellStateT>
vtkIdType operator()(CellStateT& state, const vtkIdType npts, const vtkIdType pts[])
{
using ValueType = typename CellStateT::ValueType;
auto* conn = state.GetConnectivity();
auto* offsets = state.GetOffsets();
const vtkIdType cellId = offsets->GetNumberOfValues() - 1;
offsets->InsertNextValue(static_cast<ValueType>(conn->GetNumberOfValues() + npts));
for (vtkIdType i = 0; i < npts; ++i)
{
conn->InsertNextValue(static_cast<ValueType>(pts[i]));
}
return cellId;
}
// Just update offset table (for incremental API)
template <typename CellStateT>
vtkIdType operator()(CellStateT& state, const vtkIdType npts)
{
using ValueType = typename CellStateT::ValueType;
auto* conn = state.GetConnectivity();
auto* offsets = state.GetOffsets();
const vtkIdType cellId = offsets->GetNumberOfValues() - 1;
offsets->InsertNextValue(static_cast<ValueType>(conn->GetNumberOfValues() + npts));
return cellId;
}
};
// for incremental API:
struct UpdateCellCountImpl
{
template <typename CellStateT>
void operator()(CellStateT& state, const vtkIdType npts)
{
using ValueType = typename CellStateT::ValueType;
auto* offsets = state.GetOffsets();
const ValueType cellBegin = offsets->GetValue(offsets->GetMaxId() - 1);
offsets->SetValue(offsets->GetMaxId(), static_cast<ValueType>(cellBegin + npts));
}
};
struct GetCellSizeImpl
{
template <typename CellStateT>
vtkIdType operator()(CellStateT& state, const vtkIdType cellId)
{
return state.GetCellSize(cellId);
}
};
struct GetCellAtIdImpl
{
template <typename CellStateT>
void operator()(CellStateT& state, const vtkIdType cellId, vtkIdList* ids)
{
using ValueType = typename CellStateT::ValueType;
const auto cellPts = state.GetCellRange(cellId);
ids->SetNumberOfIds(cellPts.size());
vtkIdType* idPtr = ids->GetPointer(0);
for (ValueType ptId : cellPts)
{
*idPtr++ = static_cast<vtkIdType>(ptId);
}
}
// SFINAE helper to check if a VisitState's connectivity array's memory
// can be used as a vtkIdType*.
template <typename CellStateT>
struct CanShareConnPtr
{
private:
using ValueType = typename CellStateT::ValueType;
using ArrayType = typename CellStateT::ArrayType;
using AOSArrayType = vtkAOSDataArrayTemplate<ValueType>;
static constexpr bool ValueTypeCompat = CellStateT::ValueTypeIsSameAsIdType;
static constexpr bool ArrayTypeCompat = std::is_base_of<AOSArrayType, ArrayType>::value;
public:
static constexpr bool value = ValueTypeCompat && ArrayTypeCompat;
};
template <typename CellStateT>
typename std::enable_if<CanShareConnPtr<CellStateT>::value, void>::type operator()(
CellStateT& state, const vtkIdType cellId, vtkIdType& cellSize, vtkIdType const*& cellPoints,
vtkIdList* vtkNotUsed(temp))
{
const vtkIdType beginOffset = state.GetBeginOffset(cellId);
const vtkIdType endOffset = state.GetEndOffset(cellId);
cellSize = endOffset - beginOffset;
// This is safe, see CanShareConnPtr helper above.
cellPoints = reinterpret_cast<vtkIdType*>(state.GetConnectivity()->GetPointer(beginOffset));
}
template <typename CellStateT>
typename std::enable_if<!CanShareConnPtr<CellStateT>::value, void>::type operator()(
CellStateT& state, const vtkIdType cellId, vtkIdType& cellSize, vtkIdType const*& cellPoints,
vtkIdList* temp)
{
using ValueType = typename CellStateT::ValueType;
const auto cellPts = state.GetCellRange(cellId);
cellSize = cellPts.size();
// ValueType differs from vtkIdType, so we have to copy into a temporary
// buffer:
temp->SetNumberOfIds(cellSize);
vtkIdType* tempPtr = temp->GetPointer(0);
for (ValueType ptId : cellPts)
{
*tempPtr++ = static_cast<vtkIdType>(ptId);
}
cellPoints = temp->GetPointer(0);
}
};
struct ResetImpl
{
template <typename CellStateT>
void operator()(CellStateT& state)
{
state.GetOffsets()->Reset();
state.GetConnectivity()->Reset();
state.GetOffsets()->InsertNextValue(0);
}
};
} // end namespace vtkCellArray_detail
//----------------------------------------------------------------------------
inline void vtkCellArray::InitTraversal()
{
this->TraversalCellId = 0;
}
//----------------------------------------------------------------------------
inline int vtkCellArray::GetNextCell(vtkIdType& npts, vtkIdType const*& pts) VTK_SIZEHINT(pts, npts)
{
if (this->TraversalCellId < this->GetNumberOfCells())
{
this->GetCellAtId(this->TraversalCellId, npts, pts);
++this->TraversalCellId;
return 1;
}
npts = 0;
pts = nullptr;
return 0;
}
//----------------------------------------------------------------------------
inline int vtkCellArray::GetNextCell(vtkIdList* pts)
{
if (this->TraversalCellId < this->GetNumberOfCells())
{
this->GetCellAtId(this->TraversalCellId, pts);
++this->TraversalCellId;
return 1;
}
pts->Reset();
return 0;
}
//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::GetCellSize(const vtkIdType cellId) const
{
return this->Visit(vtkCellArray_detail::GetCellSizeImpl{}, cellId);
}
//----------------------------------------------------------------------------
inline void vtkCellArray::GetCellAtId(vtkIdType cellId, vtkIdType& cellSize,
vtkIdType const*& cellPoints) VTK_SIZEHINT(cellPoints, cellSize)
{
this->Visit(vtkCellArray_detail::GetCellAtIdImpl{}, cellId, cellSize, cellPoints, this->TempCell);
}
//----------------------------------------------------------------------------
inline void vtkCellArray::GetCellAtId(vtkIdType cellId, vtkIdList* pts)
{
this->Visit(vtkCellArray_detail::GetCellAtIdImpl{}, cellId, pts);
}
//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkIdType npts, const vtkIdType pts[])
VTK_SIZEHINT(pts, npts)
{
return this->Visit(vtkCellArray_detail::InsertNextCellImpl{}, npts, pts);
}
//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(int npts)
{
return this->Visit(vtkCellArray_detail::InsertNextCellImpl{}, npts);
}
//----------------------------------------------------------------------------
inline void vtkCellArray::InsertCellPoint(vtkIdType id)
{
if (this->Storage.Is64Bit())
{
using ValueType = typename ArrayType64::ValueType;
this->Storage.GetArrays64().Connectivity->InsertNextValue(static_cast<ValueType>(id));
}
else
{
using ValueType = typename ArrayType32::ValueType;
this->Storage.GetArrays32().Connectivity->InsertNextValue(static_cast<ValueType>(id));
}
}
//----------------------------------------------------------------------------
inline void vtkCellArray::UpdateCellCount(int npts)
{
this->Visit(vtkCellArray_detail::UpdateCellCountImpl{}, npts);
}
//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkIdList* pts)
{
return this->Visit(
vtkCellArray_detail::InsertNextCellImpl{}, pts->GetNumberOfIds(), pts->GetPointer(0));
}
//----------------------------------------------------------------------------
inline vtkIdType vtkCellArray::InsertNextCell(vtkCell* cell)
{
vtkIdList* pts = cell->GetPointIds();
return this->Visit(
vtkCellArray_detail::InsertNextCellImpl{}, pts->GetNumberOfIds(), pts->GetPointer(0));
}
//----------------------------------------------------------------------------
inline void vtkCellArray::Reset()
{
this->Visit(vtkCellArray_detail::ResetImpl{});
}
#endif // vtkCellArray.h