Commit f14fbdf6 authored by Utkarsh Ayachit's avatar Utkarsh Ayachit Committed by Kitware Robot
Browse files

Merge topic 'new-field-list'

ded78d2b Fix handling of global cell ids.
45a5778f Simply `vtkAppendFilter` to use `FieldList`.
426fc1f7 Make vtkAppendFilter respect copy flags for attributes.
e3f9c88d Simplify vtkAppendPolyData.
f4662727

 Refactor vtkDataSetAttributes::FieldList
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: Cory Quammen's avatarCory Quammen <cory.quammen@kitware.com>
Merge-request: !4469
parents b8ec9be1 ded78d2b
...@@ -40,6 +40,7 @@ set(Module_SRCS ...@@ -40,6 +40,7 @@ set(Module_SRCS
vtkDataObjectTree.cxx vtkDataObjectTree.cxx
vtkDataObjectTreeIterator.cxx vtkDataObjectTreeIterator.cxx
vtkDataSetAttributes.cxx vtkDataSetAttributes.cxx
vtkDataSetAttributesFieldList.cxx
vtkDataSetCollection.cxx vtkDataSetCollection.cxx
vtkDataSet.cxx vtkDataSet.cxx
vtkDirectedAcyclicGraph.cxx vtkDirectedAcyclicGraph.cxx
......
...@@ -12,6 +12,7 @@ vtk_add_test_cxx(vtkCommonDataModelCxxTests tests ...@@ -12,6 +12,7 @@ vtk_add_test_cxx(vtkCommonDataModelCxxTests tests
TestDataArrayDispatcher.cxx TestDataArrayDispatcher.cxx
TestDataObject.cxx TestDataObject.cxx
TestDispatchers.cxx TestDispatchers.cxx
TestFieldList.cxx
TestGenericCell.cxx TestGenericCell.cxx
TestGraph.cxx TestGraph.cxx
TestGraph2.cxx TestGraph2.cxx
......
#include "vtkDataSetAttributes.h"
#include "vtkDoubleArray.h"
#include "vtkIntArray.h"
#include "vtkNew.h"
#include "vtkSmartPointer.h"
#include <string>
namespace
{
template <typename T>
vtkSmartPointer<T> CreateArray(const char* aname, int num_comps, vtkIdType numTuples)
{
auto array = vtkSmartPointer<T>::New();
array->SetName(aname);
array->SetNumberOfComponents(num_comps);
array->SetNumberOfTuples(numTuples);
array->FillValue(typename T::ValueType());
return array;
}
#define EXPECT_THAT(v, m) \
if ((v) != (m)) \
{ \
cerr << "FAILED at line " << __LINE__ << ": \n " << #v << " must match " << #m << endl; \
return EXIT_FAILURE; \
}
}
int TestFieldList(int, char* [])
{
{
// For arrays without names, ensure we are doing a order-dependent matching.
// For attributes, the attribute flag is preserved if the same arrays is
// consistently flagged as an attribute on all DSA instances.
vtkNew<vtkDataSetAttributes> dsa0;
dsa0->SetScalars(CreateArray<vtkDoubleArray>(nullptr, 1, 20));
dsa0->AddArray(CreateArray<vtkDoubleArray>(nullptr, 2, 20));
dsa0->SetVectors(CreateArray<vtkDoubleArray>(nullptr, 3, 20));
EXPECT_THAT(dsa0->GetNumberOfArrays(), 3);
vtkNew<vtkDataSetAttributes> dsa1;
dsa1->SetScalars(CreateArray<vtkDoubleArray>(nullptr, 1, 20));
dsa1->AddArray(CreateArray<vtkDoubleArray>(nullptr, 3, 20));
dsa1->AddArray(CreateArray<vtkDoubleArray>(nullptr, 3, 20));
EXPECT_THAT(dsa1->GetNumberOfArrays(), 3);
vtkDataSetAttributes::FieldList fl;
fl.InitializeFieldList(dsa0);
fl.IntersectFieldList(dsa1);
vtkNew<vtkDataSetAttributes> output;
fl.CopyAllocate(output, vtkDataSetAttributes::COPYTUPLE, 0, 0);
EXPECT_THAT(output->GetNumberOfArrays(), 2);
EXPECT_THAT(output->GetArray(0)->GetNumberOfComponents(), 1);
EXPECT_THAT(output->GetArray(1)->GetNumberOfComponents(), 3);
EXPECT_THAT(output->GetVectors(), nullptr);
EXPECT_THAT(output->GetScalars() != nullptr, true);
fl.InitializeFieldList(dsa0);
fl.UnionFieldList(dsa1);
output->Initialize();
fl.CopyAllocate(output, vtkDataSetAttributes::COPYTUPLE, 0, 0);
EXPECT_THAT(output->GetNumberOfArrays(), 4);
EXPECT_THAT(output->GetArray(0)->GetNumberOfComponents(), 1);
EXPECT_THAT(output->GetArray(1)->GetNumberOfComponents(), 2);
EXPECT_THAT(output->GetArray(2)->GetNumberOfComponents(), 3);
EXPECT_THAT(output->GetArray(3)->GetNumberOfComponents(), 3);
EXPECT_THAT(output->GetVectors(), nullptr);
EXPECT_THAT(output->GetScalars() != nullptr, true);
// just to increase coverage.
fl.PrintSelf(cout, vtkIndent());
}
{
// If inputs arrays with different names for attributes,
// make sure output doesn't have either of the arrays flagged as attributes.
vtkNew<vtkDataSetAttributes> dsa0;
dsa0->SetScalars(CreateArray<vtkDoubleArray>("scalars", 1, 20));
dsa0->AddArray(CreateArray<vtkDoubleArray>("vectors", 3, 20));
dsa0->AddArray(CreateArray<vtkDoubleArray>("common", 1, 20));
dsa0->AddArray(CreateArray<vtkDoubleArray>("uncommon0", 1, 20));
vtkNew<vtkDataSetAttributes> dsa1;
dsa1->AddArray(CreateArray<vtkDoubleArray>("scalars", 1, 20));
dsa1->SetVectors(CreateArray<vtkDoubleArray>("vectors", 3, 20));
dsa1->AddArray(CreateArray<vtkDoubleArray>("common", 1, 20));
dsa0->AddArray(CreateArray<vtkDoubleArray>("uncommon1", 1, 20));
vtkDataSetAttributes::FieldList fl;
fl.InitializeFieldList(dsa0);
fl.IntersectFieldList(dsa1);
vtkNew<vtkDataSetAttributes> output;
fl.CopyAllocate(output, vtkDataSetAttributes::COPYTUPLE, 0, 0);
EXPECT_THAT(output->GetNumberOfArrays(), 3);
EXPECT_THAT(output->GetArray("uncommon0"), nullptr);
EXPECT_THAT(output->GetArray("uncommon1"), nullptr);
EXPECT_THAT(output->GetScalars(), nullptr);
EXPECT_THAT(output->GetVectors(), nullptr);
EXPECT_THAT(output->GetArray("scalars") != nullptr, true);
EXPECT_THAT(output->GetArray("vectors") != nullptr, true);
fl.InitializeFieldList(dsa0);
fl.UnionFieldList(dsa1);
output->Initialize();
fl.CopyAllocate(output, vtkDataSetAttributes::COPYTUPLE, 0, 0);
EXPECT_THAT(output->GetNumberOfArrays(), 5);
EXPECT_THAT(output->GetScalars(), nullptr);
EXPECT_THAT(output->GetVectors(), nullptr);
}
return EXIT_SUCCESS;
}
This diff is collapsed.
...@@ -44,7 +44,8 @@ ...@@ -44,7 +44,8 @@
#ifndef vtkDataSetAttributes_h #ifndef vtkDataSetAttributes_h
#define vtkDataSetAttributes_h #define vtkDataSetAttributes_h
#include "vtkCommonDataModelModule.h" // For export macro #include "vtkCommonDataModelModule.h" // For export macro
#include "vtkDataSetAttributesFieldList.h" // for vtkDataSetAttributesFieldList
#include "vtkFieldData.h" #include "vtkFieldData.h"
class vtkLookupTable; class vtkLookupTable;
...@@ -546,7 +547,7 @@ public: ...@@ -546,7 +547,7 @@ public:
vtkDataSetAttributes *from2, vtkDataSetAttributes *from2,
vtkIdType id, double t); vtkIdType id, double t);
class FieldList; using FieldList = vtkDataSetAttributesFieldList;
// field list copy operations ------------------------------------------ // field list copy operations ------------------------------------------
...@@ -591,8 +592,6 @@ public: ...@@ -591,8 +592,6 @@ public:
int idx, vtkIdType toId, int idx, vtkIdType toId,
vtkIdList *ids, double *weights); vtkIdList *ids, double *weights);
friend class vtkDataSetAttributes::FieldList;
protected: protected:
vtkDataSetAttributes(); vtkDataSetAttributes();
~vtkDataSetAttributes() override; ~vtkDataSetAttributes() override;
...@@ -604,11 +603,6 @@ protected: ...@@ -604,11 +603,6 @@ protected:
int shallowCopyArrays=0, int shallowCopyArrays=0,
bool createNewArrays=true); bool createNewArrays=true);
void InternalCopyAllocate(
vtkDataSetAttributes::FieldList& list,
int ctype,
vtkIdType sze, vtkIdType ext);
/** /**
* Initialize all of the object's data to nullptr * Initialize all of the object's data to nullptr
*/ */
...@@ -635,79 +629,7 @@ private: ...@@ -635,79 +629,7 @@ private:
vtkDataSetAttributes(const vtkDataSetAttributes&) = delete; vtkDataSetAttributes(const vtkDataSetAttributes&) = delete;
void operator=(const vtkDataSetAttributes&) = delete; void operator=(const vtkDataSetAttributes&) = delete;
public: friend class vtkDataSetAttributesFieldList;
// This public class is used to perform set operations, other misc.
// operations on fields. For example, vtkAppendFilter uses it to
// determine which attributes the input datasets share in common.
class vtkInternalComponentNames;
class VTKCOMMONDATAMODEL_EXPORT FieldList
{
public:
FieldList(int numInputs);
~FieldList();
void PrintSelf(ostream &os, vtkIndent indent);
void InitializeFieldList(vtkDataSetAttributes* dsa);
void IntersectFieldList(vtkDataSetAttributes* dsa);
/**
* Similar to IntersectFieldList() except that it builds a union of the
* array list. To determine the active attributes, it still, however, takes
* an intersection.
* WARNING!!!-IntersectFieldList() and UnionFieldList() should not be
* intermixed.
*/
void UnionFieldList(vtkDataSetAttributes* dsa);
//Determine whether data is available
int IsAttributePresent(int attrType); //true/false attributes specified
// Accessor methods.
int GetNumberOfFields() { return this->NumberOfFields; }
int GetFieldIndex(int i) { return this->FieldIndices[i]; }
const char* GetFieldName(int i) { return this->Fields[i]; }
int GetFieldComponents(int i) { return this->FieldComponents[i]; }
int GetDSAIndex(int index, int i) { return this->DSAIndices[index][i]; }
friend class vtkDataSetAttributes;
protected:
void SetFieldIndex(int i, int index)
{ this->FieldIndices[i] = index; }
private:
FieldList(const FieldList&) = delete;
void operator=(const FieldList&) = delete;
void SetField(int index, vtkAbstractArray *da);
void RemoveField(const char *name);
void ClearFields();
void GrowBy(unsigned int delta);
int NumberOfFields; //the number of fields (including five named attributes)
// These keep track of what is common across datasets. The first
// six items are always named attributes.
char** Fields; // the names of the fields
int *FieldTypes; // the types of the fields
int *FieldComponents; // the number of components in field
int *FieldIndices; // output data array index
vtkLookupTable **LUT; // luts associated with each array
vtkInformation **FieldInformation; // Information map associated with each array
vtkInternalComponentNames **FieldComponentsNames; // the name for each component in the field
vtkIdType NumberOfTuples; // a running total of values
//For every vtkDataSetAttributes that are processed, keep track of the
//indices into various things. The indices are organized so that the
//first NUM_ATTRIBUTES refer to attributes, the next refer to the
//non-attribute fields, for a total of NUM_ATTRIBUTES + NumberOfFields.
//CurrentInput is the current input being processed.
int **DSAIndices;
int NumberOfDSAIndices;
int CurrentInput;
};
}; };
#endif #endif
This diff is collapsed.
/*=========================================================================
Program: Visualization Toolkit
Module: vtkDataSetAttributesFieldList.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 vtkDataSetAttributesFieldList
* @brief helps manage arrays from multiple vtkDataSetAttributes.
*
* vtkDataSetAttributesFieldList, also called vtkDataSetAttributes::FieldList,
* is used to help with filters when dealing with arrays from multiple
* vtkDataSetAttributes instances, potentially from multiple inputs.
*
* Consider a filter that appends multiple inputs, e.g. vtkAppendPolyData.
* Besides appending mesh elements, such a filter also needs to combine field
* arrays (point, and cell data) from inputs to pass on to the output.
* Now if all the inputs had exactly the same set of arrays, we're all set.
* However, more often than not, the inputs will have different sets of arrays.
* The filter will need to match up from various inputs to combine together,
* potentially dropping arrays not in all inputs. Furthermore, it needs to
* ensure arrays in the output are flagged as attributes consistently. All of
* this can be done using vtkDataSetAttributesFieldList.
*
* @section Usage Usage
*
* Typical usage is as follows:
* 1. call `IntersectFieldList` or `UnionFieldList` for all input vtkDataSetAttributes
* instances,
* 2. allocate arrays for the output vtkDataSetAttributes by using `CopyAllocate`,
* 3. call `CopyData` per input (preserving the input order used in step 1) to
* copy tuple(s) from input to the output.
*
* `vtkDataSetAttributes::InitializeFieldList` is provided for API compatibility
* with previous implementation of this class and it not required to be called.
* Simply calling `UnionFieldList` or `IntersectFieldList` for the first
* vtkDataSetAttributes instance is sufficient.
*
* `CopyAllocate, `CopyData`, and `InterpolatePoint` methods on this class
* are called by similarly named variants on vtkDataSetAttributes that take in a
* FieldList instance as an argument. Hence, either forms may be used.
*
* Calls to `UnionFieldList` and `IntersectFieldList` cannot be mixed. Use
* `Reset` or `InitializeFieldList` to change mode and start reinitialization.
*/
#ifndef vtkDataSetAttributesFieldList_h
#define vtkDataSetAttributesFieldList_h
#include "vtkCommonDataModelModule.h" // For export macro
#include "vtkSystemIncludes.h"
#include "vtkSmartPointer.h" // for vtkSmartPointer
#include <memory> // for unique_ptr
class vtkAbstractArray;
class vtkDataSetAttributes;
class vtkIdList;
class VTKCOMMONDATAMODEL_EXPORT vtkDataSetAttributesFieldList
{
public:
/**
* `number_of_inputs` parameter is not required and only provided for
* backwards compatibility.
*/
vtkDataSetAttributesFieldList(int number_of_inputs = 0);
virtual ~vtkDataSetAttributesFieldList();
void PrintSelf(ostream& os, vtkIndent indent);
/**
* Initializes the field list to empty.
*/
void Reset();
/**
* Initialize the field list. This also adds the first input.
* Calling this method is optional. The first call to `IntersectFieldList` or
* `UnionFieldList` on a new instance or after calling `Reset()` will have the
* same effect.
*/
void InitializeFieldList(vtkDataSetAttributes* dsa);
/**
* Update the field list for an intersection of arrays registered so far and
* those in `dsa`.
*/
void IntersectFieldList(vtkDataSetAttributes* dsa);
/**
* Update the field list for an union of arrays registered so far and
* those in `dsa`.
*/
void UnionFieldList(vtkDataSetAttributes* dsa);
//@{
/**
* These methods can called to generate and update the output
* vtkDataSetAttributes. These match corresponding API on vtkDataSetAttributes
* and can be called via the output vtkDataSetAttributes instance
* instead as well.
*/
void CopyAllocate(vtkDataSetAttributes* output, int ctype, vtkIdType sz, vtkIdType ext) const;
void CopyData(int inputIndex, vtkDataSetAttributes* input, vtkIdType fromId,
vtkDataSetAttributes* output, vtkIdType toId) const;
void CopyData(int inputIdx, vtkDataSetAttributes* input, vtkIdType inputStart,
vtkIdType numValues, vtkDataSetAttributes* output, vtkIdType outStart) const;
void InterpolatePoint(int inputIdx, vtkDataSetAttributes* input, vtkIdList* inputIds,
double* weights, vtkDataSetAttributes* output, vtkIdType toId) const;
//@}
//@{
/**
* vtkDataSetAttributes::FieldList used a different internal data structure in
* older versions of VTK. This exposes that API for legacy applications.
* It may be deprecated in the future.
*
* Using these methods should be avoided in new code.
*/
int IsAttributePresent(int attrType) const;
int GetNumberOfFields() const;
int GetFieldIndex(int i) const;
const char* GetFieldName(int i) const;
int GetFieldComponents(int i) const;
int GetDSAIndex(int index, int i) const;
//@}
protected:
/**
* Called to create an output array for the given type.
* Default implementation calls `vtkAbstractArray::CreateArray()`.
*/
virtual vtkSmartPointer<vtkAbstractArray> CreateArray(int type) const;
private:
class vtkInternals;
std::unique_ptr<vtkInternals> Internals;
vtkDataSetAttributesFieldList(const vtkDataSetAttributesFieldList&) = delete;
void operator=(vtkDataSetAttributesFieldList&) = delete;
};
#endif
// VTK-HeaderTest-Exclude: vtkDataSetAttributesFieldList.h
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "vtkBoundingBox.h" #include "vtkBoundingBox.h"
#include "vtkCellData.h" #include "vtkCellData.h"
#include "vtkCell.h" #include "vtkCell.h"
#include "vtkDataSetAttributes.h"
#include "vtkDataSetCollection.h" #include "vtkDataSetCollection.h"
#include "vtkExecutive.h" #include "vtkExecutive.h"
#include "vtkIncrementalOctreePointLocator.h" #include "vtkIncrementalOctreePointLocator.h"
...@@ -30,7 +29,6 @@ ...@@ -30,7 +29,6 @@
#include "vtkUnstructuredGrid.h" #include "vtkUnstructuredGrid.h"
#include "vtkStreamingDemandDrivenPipeline.h" #include "vtkStreamingDemandDrivenPipeline.h"
#include <set>
#include <string> #include <string>
vtkStandardNewMacro(vtkAppendFilter); vtkStandardNewMacro(vtkAppendFilter);
...@@ -391,251 +389,42 @@ void vtkAppendFilter::AppendArrays(int attributesType, ...@@ -391,251 +389,42 @@ void vtkAppendFilter::AppendArrays(int attributesType,
return; return;
} }
////////////////////////////////////////////////////////////////// vtkDataSetAttributes::FieldList fieldList;
// Phase 1 - Find arrays to append based on name auto inputs = vtkSmartPointer<vtkDataSetCollection>::Take(this->GetNonEmptyInputs(inputVector));
//////////////////////////////////////////////////////////////////
// Store the set of data arrays common to all inputs. This set is
// initialized with the data arrays from the first input and is
// updated to be the intersection of it with the arrays from
// subsequent inputs.
std::set<std::string> dataArrayNames;
vtkDataSetAttributes* outputData = output->GetAttributes(attributesType);
bool isFirstInputData = true;
vtkDataSetAttributes* firstInputData = nullptr;
vtkSmartPointer<vtkDataSetCollection> inputs;
inputs.TakeReference(this->GetNonEmptyInputs(inputVector));
vtkCollectionSimpleIterator iter; vtkCollectionSimpleIterator iter;
inputs->InitTraversal(iter);
vtkDataSet* dataSet = nullptr; vtkDataSet* dataSet = nullptr;
while ((dataSet = inputs->GetNextDataSet(iter))) for (dataSet = nullptr, inputs->InitTraversal(iter); (dataSet = inputs->GetNextDataSet(iter)); )
{
vtkDataSetAttributes* inputData = dataSet->GetAttributes(attributesType);
if (isFirstInputData)
{
isFirstInputData = false;
firstInputData = inputData;
for (int arrayIndex = 0; arrayIndex < inputData->GetNumberOfArrays(); ++arrayIndex)
{
vtkAbstractArray* array = inputData->GetAbstractArray(arrayIndex);
if (array && array->GetName())
{
// NOTE - it is possible for an array to not have a name,
// but be an active attribute. We'll deal with that case
// later on.
dataArrayNames.insert(std::string(array->GetName()));
}
}
}
else
{
std::set<std::string>::iterator it = dataArrayNames.begin();
while (it != dataArrayNames.end())
{
const char* arrayName = it->c_str();
vtkAbstractArray* array = inputData->GetAbstractArray(arrayName);
vtkAbstractArray* firstArray = firstInputData->GetAbstractArray(arrayName);
if (!array ||
array->GetDataType() != firstArray->GetDataType() ||
array->GetNumberOfComponents() != firstArray->GetNumberOfComponents())
{
// Incompatible array in this input. We can't append it.
dataArrayNames.erase(it++);
}
else
{
++it;
}
}
}
}
// Allocate arrays for the output
for (std::set<std::string>::iterator it = dataArrayNames.begin(); it != dataArrayNames.end(); ++it)
{
vtkAbstractArray* srcArray = firstInputData->GetAbstractArray((*it).c_str());
vtkAbstractArray* dstArray = vtkAbstractArray::CreateArray(srcArray->GetDataType());
dstArray->CopyInformation(srcArray->GetInformation());
dstArray->SetName(srcArray->GetName());
dstArray->SetNumberOfComponents(srcArray->GetNumberOfComponents());
for (int j = 0; j < srcArray->GetNumberOfComponents(); ++j)
{
if (srcArray->GetComponentName(j))
{
dstArray->SetComponentName(j, srcArray->GetComponentName(j));
}
}
dstArray->SetNumberOfTuples(totalNumberOfElements);
outputData->AddArray(dstArray);
dstArray->Delete();
}
//////////////////////////////////////////////////////////////////
// Phase 2 - Set up arrays as attributes
//////////////////////////////////////////////////////////////////
// Set active attributes in the outputs only if all the inputs have
// the same active attributes name (or the name is nullptr).
vtkAbstractArray* attributeArrays[vtkDataSetAttributes::NUM_ATTRIBUTES];
// Initialize with the active attribute from the first input
for (int attribute = 0; attribute < vtkDataSetAttributes::NUM_ATTRIBUTES; ++attribute)
{
attributeArrays[attribute] = firstInputData->GetAbstractAttribute(attribute);
}
inputs->InitTraversal(iter);
while ((dataSet = inputs->GetNextDataSet(iter)))
{
for (int attributeIndex = 0; attributeIndex < vtkDataSetAttributes::NUM_ATTRIBUTES; ++attributeIndex)
{
if (attributeArrays[attributeIndex])
{
vtkDataSetAttributes* inputData = dataSet->GetAttributes(attributesType);
vtkAbstractArray* thisArray = inputData->GetAbstractAttribute(attributeIndex);
bool matches = thisArray &&
((attributeArrays[attributeIndex]->GetName() == nullptr && thisArray->GetName() == nullptr) ||
strcmp(attributeArrays[attributeIndex]->GetName(), thisArray->GetName()) == 0);
if (!matches)
{
// This input doesn't agree on the active attribute, so unset it.
attributeArrays[attributeIndex] = nullptr;
}
}
}
}
// Set the active attributes
for (int attributeIndex = 0; attributeIndex < vtkDataSetAttributes::NUM_ATTRIBUTES; ++attributeIndex)
{ {
if (attributeArrays[attributeIndex]) if (auto inputData = dataSet->GetAttributes(attributesType))
{ {
const char* arrayName = attributeArrays[attributeIndex]->GetName();