/*========================================================================= Program: Visualization Toolkit Module: vtkArrayListTemplate.h Copyright (c) Kitware, Inc. All rights reserved. See LICENSE file 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 vtkArrayListTemplate * @brief thread-safe and efficient data attribute processing * * * vtkArrayListTemplate supplements the vtkDataSetAttributes class to provide * threaded processing of data arrays. It is also more efficient for certain * interpolation operations. The expectation is that it will be replaced one * day once vtkPointData, vtkCellData, vtkDataSetAttributes, and vtkFieldData * properly support multithreading and/or are redesigned. Note that this * implementation does not support incremental operations (like InsertNext()). * * Generally the way this helper class is used is to first invoke * vtkDataSetAttributes::CopyInterpolate() or InterpolateAllocate() which * performs the initial magic of constructing input and output arrays. Then * the input attributes, and output attributes, are passed to initialize the * internal structures via the AddArrays() method. Essentially these internal * structures are templated pairs of arrays of the same type, which can be * efficiently accessed and assigned. The operations on these array pairs * (e.g., interpolation) occur using a typeless, virtual dispatch base class. * * @warning * vtkDataSetAttributes is not in general thread safe due to the use of its * vtkFieldData::BasicIterator RequiredArrays data member. This class augments * vtkDataSetAttributes for thread safety. * * @sa * vtkFieldData vtkDataSetAttributes vtkPointData vtkCellData */ #ifndef vtkArrayListTemplate_h #define vtkArrayListTemplate_h #include "vtkAbstractArray.h" #include "vtkDataSetAttributes.h" #include "vtkSmartPointer.h" #include "vtkStdString.h" #include #include // Create a generic class supporting virtual dispatch to type-specific // subclasses. struct BaseArrayPair { vtkIdType Num; int NumComp; vtkSmartPointer OutputArray; BaseArrayPair(vtkIdType num, int numComp, vtkAbstractArray* outArray) : Num(num) , NumComp(numComp) , OutputArray(outArray) { } virtual ~BaseArrayPair() = default; virtual void Copy(vtkIdType inId, vtkIdType outId) = 0; virtual void Interpolate( int numWeights, const vtkIdType* ids, const double* weights, vtkIdType outId) = 0; virtual void Average(int numPts, const vtkIdType* ids, vtkIdType outId) = 0; virtual void WeightedAverage( int numPts, const vtkIdType* ids, const double* weights, vtkIdType outId) = 0; virtual void InterpolateEdge(vtkIdType v0, vtkIdType v1, double t, vtkIdType outId) = 0; virtual void AssignNullValue(vtkIdType outId) = 0; virtual void Realloc(vtkIdType sze) = 0; }; // Type specific interpolation on a matched pair of data arrays template struct ArrayPair : public BaseArrayPair { T* Input; T* Output; T NullValue; ArrayPair(T* in, T* out, vtkIdType num, int numComp, vtkAbstractArray* outArray, T null) : BaseArrayPair(num, numComp, outArray) , Input(in) , Output(out) , NullValue(null) { } ~ArrayPair() override = default; // calm down some finicky compilers void Copy(vtkIdType inId, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { this->Output[outId * this->NumComp + j] = this->Input[inId * this->NumComp + j]; } } void Interpolate( int numWeights, const vtkIdType* ids, const double* weights, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numWeights; ++i) { v += weights[i] * static_cast(this->Input[ids[i] * this->NumComp + j]); } this->Output[outId * this->NumComp + j] = static_cast(v); } } void Average(int numPts, const vtkIdType* ids, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numPts; ++i) { v += static_cast(this->Input[ids[i] * this->NumComp + j]); } v /= static_cast(numPts); this->Output[outId * this->NumComp + j] = static_cast(v); } } void WeightedAverage( int numPts, const vtkIdType* ids, const double* weights, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numPts; ++i) { v += (weights[i] * static_cast(this->Input[ids[i] * this->NumComp + j])); } this->Output[outId * this->NumComp + j] = static_cast(v); } } void InterpolateEdge(vtkIdType v0, vtkIdType v1, double t, vtkIdType outId) override { double v; vtkIdType numComp = this->NumComp; for (int j = 0; j < numComp; ++j) { v = this->Input[v0 * numComp + j] + t * (this->Input[v1 * numComp + j] - this->Input[v0 * numComp + j]); this->Output[outId * numComp + j] = static_cast(v); } } void AssignNullValue(vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { this->Output[outId * this->NumComp + j] = this->NullValue; } } void Realloc(vtkIdType sze) override { this->OutputArray->Resize(sze); this->OutputArray->SetNumberOfTuples(sze); this->Output = static_cast(this->OutputArray->GetVoidPointer(0)); } }; // Type specific interpolation on a pair of data arrays with different types, where the // output type is expected to be a real type (i.e., float or double). template struct RealArrayPair : public BaseArrayPair { TInput* Input; TOutput* Output; TOutput NullValue; RealArrayPair( TInput* in, TOutput* out, vtkIdType num, int numComp, vtkAbstractArray* outArray, TOutput null) : BaseArrayPair(num, numComp, outArray) , Input(in) , Output(out) , NullValue(null) { } ~RealArrayPair() override = default; // calm down some finicky compilers void Copy(vtkIdType inId, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { this->Output[outId * this->NumComp + j] = static_cast(this->Input[inId * this->NumComp + j]); } } void Interpolate( int numWeights, const vtkIdType* ids, const double* weights, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numWeights; ++i) { v += weights[i] * static_cast(this->Input[ids[i] * this->NumComp + j]); } this->Output[outId * this->NumComp + j] = static_cast(v); } } void Average(int numPts, const vtkIdType* ids, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numPts; ++i) { v += static_cast(this->Input[ids[i] * this->NumComp + j]); } v /= static_cast(numPts); this->Output[outId * this->NumComp + j] = static_cast(v); } } void WeightedAverage( int numPts, const vtkIdType* ids, const double* weights, vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { double v = 0.0; for (vtkIdType i = 0; i < numPts; ++i) { v += (weights[i] * static_cast(this->Input[ids[i] * this->NumComp + j])); } this->Output[outId * this->NumComp + j] = static_cast(v); } } void InterpolateEdge(vtkIdType v0, vtkIdType v1, double t, vtkIdType outId) override { double v; vtkIdType numComp = this->NumComp; for (int j = 0; j < numComp; ++j) { v = this->Input[v0 * numComp + j] + t * (this->Input[v1 * numComp + j] - this->Input[v0 * numComp + j]); this->Output[outId * numComp + j] = static_cast(v); } } void AssignNullValue(vtkIdType outId) override { for (int j = 0; j < this->NumComp; ++j) { this->Output[outId * this->NumComp + j] = this->NullValue; } } void Realloc(vtkIdType sze) override { this->OutputArray->Resize(sze); this->OutputArray->SetNumberOfTuples(sze); this->Output = static_cast(this->OutputArray->GetVoidPointer(0)); } }; // Forward declarations. This makes working with vtkTemplateMacro easier. struct ArrayList; template void CreateArrayPair( ArrayList* list, T* inData, T* outData, vtkIdType numTuples, int numComp, T nullValue); // A list of the arrays to interpolate, and a method to invoke interpolation on the list struct ArrayList { // The list of arrays, and the arrays not to process std::vector Arrays; std::vector ExcludedArrays; // Add the arrays to interpolate here (from attribute data). Note that this method is // not thread-safe due to its use of vtkDataSetAttributes. void AddArrays(vtkIdType numOutPts, vtkDataSetAttributes* inPD, vtkDataSetAttributes* outPD, double nullValue = 0.0, vtkTypeBool promote = true); // Add an array that interpolates from its own attribute values void AddSelfInterpolatingArrays( vtkIdType numOutPts, vtkDataSetAttributes* attr, double nullValue = 0.0); // Add a pair of arrays (manual insertion). Returns the output array created, // if any. No array may be created if \c inArray was previously marked as // excluded using ExcludeArray(). vtkAbstractArray* AddArrayPair(vtkIdType numTuples, vtkAbstractArray* inArray, vtkStdString& outArrayName, double nullValue, vtkTypeBool promote); // Any array excluded here is not added by AddArrays() or AddArrayPair, hence not // processed. Also check whether an array is excluded. void ExcludeArray(vtkAbstractArray* da); vtkTypeBool IsExcluded(vtkAbstractArray* da); // Loop over the array pairs and copy data from one to another. This (and the following methods) // can be used within threads. void Copy(vtkIdType inId, vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->Copy(inId, outId); } } // Loop over the arrays and have them interpolate themselves void Interpolate(int numWeights, const vtkIdType* ids, const double* weights, vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->Interpolate(numWeights, ids, weights, outId); } } // Loop over the arrays and have them averaged void Average(int numPts, const vtkIdType* ids, vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->Average(numPts, ids, outId); } } // Loop over the arrays and weighted average the attributes. The weights should sum to 1.0. void WeightedAverage(int numPts, const vtkIdType* ids, const double* weights, vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->WeightedAverage(numPts, ids, weights, outId); } } // Loop over the arrays perform edge interpolation void InterpolateEdge(vtkIdType v0, vtkIdType v1, double t, vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->InterpolateEdge(v0, v1, t, outId); } } // Loop over the arrays and assign the null value void AssignNullValue(vtkIdType outId) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->AssignNullValue(outId); } } // Extend (realloc) the arrays void Realloc(vtkIdType sze) { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { (*it)->Realloc(sze); } } // Only you can prevent memory leaks! ~ArrayList() { for (std::vector::iterator it = Arrays.begin(); it != Arrays.end(); ++it) { delete (*it); } } // Return the number of arrays vtkIdType GetNumberOfArrays() { return static_cast(Arrays.size()); } }; #include "vtkArrayListTemplate.txx" #endif // VTK-HeaderTest-Exclude: vtkArrayListTemplate.h