/*========================================================================= 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 // for assert #include // for API #include // for std::is_same #include // 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; /** * 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, vtkAOSDataArrayTemplate, vtkAOSDataArrayTemplate > >::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* offsets, vtkAOSDataArrayTemplate* connectivity); void SetData(vtkAOSDataArrayTemplate* offsets, vtkAOSDataArrayTemplate* connectivity); void SetData( vtkAOSDataArrayTemplate* offsets, vtkAOSDataArrayTemplate* 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& cell) { return this->InsertNextCell(static_cast(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& cell) { return this->ReplaceCellAtId(cellId, static_cast(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 struct VisitState { using ArrayType = ArrayT; using ValueType = typename ArrayType::ValueType; using CellRangeType = decltype(vtk::DataArrayValueRange<1>(std::declval())); // 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::value && std::is_signed::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::New()) , Offsets(vtkSmartPointer::New()) { this->Offsets->InsertNextValue(0); } ~VisitState() = default; vtkSmartPointer Connectivity; vtkSmartPointer Offsets; private: VisitState(const VisitState&) = delete; VisitState& operator=(const VisitState&) = delete; }; private: // Helpers that allow Visit to return a value: template using GetReturnType = decltype( std::declval()(std::declval&>(), std::declval()...)); template struct ReturnsVoid : std::is_same, 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 * 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 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 * 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 ::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)...); } 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)...); } } template ::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)...); } 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)...); } } template ::value>::type> GetReturnType 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)...); } 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)...); } } template ::value>::type> GetReturnType 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)...); } 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)...); } } /** @} */ #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 Int32; VisitState Int64; }; Storage() { // Default to the compile-time setting: #ifdef VTK_USE_64BIT_IDS new (&this->Arrays.Int64) VisitState; this->StorageIs64Bit = true; #else // VTK_USE_64BIT_IDS new (&this->Arrays.Int32) VisitState; 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; 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; 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& GetArrays32() { assert(!this->StorageIs64Bit); return this->Arrays.Int32; } const VisitState& GetArrays32() const { assert(!this->StorageIs64Bit); return this->Arrays.Int32; } // Get the VisitState for 64-bit arrays VisitState& GetArrays64() { assert(this->StorageIs64Bit); return this->Arrays.Int64; } const VisitState& 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 TempCell; vtkIdType TraversalCellId{ 0 }; vtkNew LegacyData; // For GetData(). private: vtkCellArray(const vtkCellArray&) = delete; void operator=(const vtkCellArray&) = delete; }; template vtkIdType vtkCellArray::VisitState::GetNumberOfCells() const { return this->Offsets->GetNumberOfValues() - 1; } template vtkIdType vtkCellArray::VisitState::GetBeginOffset(vtkIdType cellId) const { return static_cast(this->Offsets->GetValue(cellId)); } template vtkIdType vtkCellArray::VisitState::GetEndOffset(vtkIdType cellId) const { return static_cast(this->Offsets->GetValue(cellId + 1)); } template vtkIdType vtkCellArray::VisitState::GetCellSize(vtkIdType cellId) const { return this->GetEndOffset(cellId) - this->GetBeginOffset(cellId); } template typename vtkCellArray::VisitState::CellRangeType vtkCellArray::VisitState::GetCellRange(vtkIdType cellId) { return vtk::DataArrayValueRange<1>( this->GetConnectivity(), this->GetBeginOffset(cellId), this->GetEndOffset(cellId)); } namespace vtkCellArray_detail { struct InsertNextCellImpl { // Insert full cell template 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(conn->GetNumberOfValues() + npts)); for (vtkIdType i = 0; i < npts; ++i) { conn->InsertNextValue(static_cast(pts[i])); } return cellId; } // Just update offset table (for incremental API) template 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(conn->GetNumberOfValues() + npts)); return cellId; } }; // for incremental API: struct UpdateCellCountImpl { template 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(cellBegin + npts)); } }; struct GetCellSizeImpl { template vtkIdType operator()(CellStateT& state, const vtkIdType cellId) { return state.GetCellSize(cellId); } }; struct GetCellAtIdImpl { template 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(ptId); } } // SFINAE helper to check if a VisitState's connectivity array's memory // can be used as a vtkIdType*. template struct CanShareConnPtr { private: using ValueType = typename CellStateT::ValueType; using ArrayType = typename CellStateT::ArrayType; using AOSArrayType = vtkAOSDataArrayTemplate; static constexpr bool ValueTypeCompat = CellStateT::ValueTypeIsSameAsIdType; static constexpr bool ArrayTypeCompat = std::is_base_of::value; public: static constexpr bool value = ValueTypeCompat && ArrayTypeCompat; }; template typename std::enable_if::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(state.GetConnectivity()->GetPointer(beginOffset)); } template typename std::enable_if::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(ptId); } cellPoints = temp->GetPointer(0); } }; struct ResetImpl { template 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(id)); } else { using ValueType = typename ArrayType32::ValueType; this->Storage.GetArrays32().Connectivity->InsertNextValue(static_cast(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