4.4. Extended Filter Implementations
In Chapter 3.3 (Simple Worklets) and Chapter 4.3 (Worklet Types) we discuss how to implement an algorithm in the VTK‑m framework by creating a worklet. For simplicity, worklet algorithms are wrapped in what are called filter objects for general usage. Chapter 2.6 (Running Filters) introduces the concept of filters, and Chapter 2.7 (Provided Filters) documents those that come with the VTK‑m library. Chapter 3.4 (Basic Filter Implementation) gives a brief introduction on implementing filters. This chapter elaborates on building new filter objects by introducing new filter types. These will be used to wrap filters around the extended worklet examples in Chapter 4.3 (Worklet Types).
Unsurprisingly, the base filter objects are contained in the vtkm::filter
package.
In particular, all filter objects inherit from vtkm::filter::Filter
, either directly or indirectly.
The filter implementation must override the protected pure virtual method vtkm::filter::Filter::DoExecute()
.
The base class will call this method to run the operation of the filter.
The vtkm::filter::Filter::DoExecute()
method has a single argument that is a vtkm::cont::DataSet
.
The vtkm::cont::DataSet
contains the data on which the filter will operate.
vtkm::filter::Filter::DoExecute()
must then return a new vtkm::cont::DataSet
containing the derived data.
The vtkm::cont::DataSet
should be created with one of the vtkm::filter::Filter::CreateResult()
methods.
A filter implementation may also optionally override the vtkm::filter::Filter::DoExecutePartitions()
.
This method is similar to vtkm::filter::Filter::DoExecute()
except that it takes and returns a vtkm::cont::PartitionedDataSet
object.
If a filter does not provide a vtkm::filter::Filter::DoExecutePartitions()
method, then if given a vtkm::cont::PartitionedDataSet
, the base class will call vtkm::filter::Filter::DoExecute()
on each of the partitions and build a vtkm::cont::PartitionedDataSet
with the results.
In addition to (or instead of) operating on the geometric structure of a vtkm::cont::DataSet
, a filter will commonly take one or more fields from the input vtkm::cont::DataSet
and write one or more fields to the result.
For this reason, vtkm::filter::Filter
provides convenience methods to select input fields and output field names.
It also provides a method named vtkm::filter::Filter::GetFieldFromDataSet()
that can be used to get the input fields from the vtkm::cont::DataSet
passed to vtkm::filter::Filter::DoExecute()
.
When getting a field with vtkm::filter::Filter::GetFieldFromDataSet()
, you get a vtkm::cont::Field
object.
Before you can operate on the vtkm::cont::Field
, you have to convert it to a vtkm::cont::ArrayHandle
.
vtkm::filter::Filter::CastAndCallScalarField()
can be used to do this conversion.
It takes the field object as the first argument and attempts to convert it to an vtkm::cont::ArrayHandle
of different types.
When it finds the correct type, it calls the provided functor with the appropriate vtkm::cont::ArrayHandle
.
The similar vtkm::filter::Filter::CastAndCallVecField()
does the same thing to find an vtkm::cont::ArrayHandle
with vtkm::Vec
’s of a selected length, and vtkm::filter::Filter::CastAndCallVariableVecField()
does the same thing but will find vtkm::Vec
’s of any length.
The remainder of this chapter will provide some common patterns of filter operation based on the data they use and generate.
4.4.1. Deriving Fields from other Fields
A common type of filter is one that generates a new field that is derived from one or more existing fields or point coordinates on the data set.
For example, mass, volume, and density are interrelated, and any one can be derived from the other two.
Typically, you would use vtkm::filter::Filter::GetFieldFromDataSet()
to retrieve the input fields, one of the vtkm::filter::Filter::CastAndCall()
methods to resolve the array type of the field, and finally use vtkm::filter::Filter::CreateResultField()
to produce the output.
In this section we provide an example implementation of a field filter that wraps the “magnitude” worklet provided in Example 4.42.
By C++ convention, object implementations are split into two files.
The first file is a standard header file with a .h
extension that contains the declaration of the filter class without the implementation.
So we would expect the following code to be in a file named FieldMagnitude.h
.
1namespace vtkm
2{
3namespace filter
4{
5namespace vector_calculus
6{
7
8class VTKM_FILTER_VECTOR_CALCULUS_EXPORT FieldMagnitude : public vtkm::filter::Filter
9{
10public:
11 VTKM_CONT FieldMagnitude();
12
13 VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inDataSet) override;
14};
15
16} // namespace vector_calculus
17} // namespace filter
18} // namespace vtkm
You may notice in Example 4.55, line 8 there is a special macro names VTKM_FILTER_VECTOR_CALCULUS_EXPORT
.
This macro tells the C++ compiler that the class FieldMagnitude
is going to be exported from a library.
More specifically, the CMake for VTK‑m’s build will generate a header file containing this export macro for the associated library.
By VTK‑m’s convention, a filter in the vtkm::filter::vector_calculus
will be defined in the vtkm/filter/vector_calculus
directory.
When defining the targets for this library, CMake will create a header file named vtkm_filter_vector_calculus.h
that contains the macro named VTKM_FILTER_VECTOR_CALCULUS_EXPORT
.
This macro will provide the correct modifiers for the particular C++ compiler being used to export the class from the library.
If this macro is left out, then the library will work on some platforms, but on other platforms will produce a linker error for missing symbols.
Once the filter class is declared in the .h
file, the implementation filter is by convention given in a separate .cxx
file.
So the continuation of our example that follows would be expected in a file named FieldMagnitude.cxx
.
1namespace vtkm
2{
3namespace filter
4{
5namespace vector_calculus
6{
7
8VTKM_CONT
9FieldMagnitude::FieldMagnitude()
10{
11 this->SetOutputFieldName("");
12}
13
14VTKM_CONT vtkm::cont::DataSet FieldMagnitude::DoExecute(
15 const vtkm::cont::DataSet& inDataSet)
16{
17 vtkm::cont::Field inField = this->GetFieldFromDataSet(inDataSet);
18
19 vtkm::cont::UnknownArrayHandle outField;
20
21 // Use a C++ lambda expression to provide a callback for CastAndCall. The lambda
22 // will capture references to local variables like outFieldArray (using `[&]`)
23 // that it can read and write.
24 auto resolveType = [&](const auto& inFieldArray) {
25 using InArrayHandleType = std::decay_t<decltype(inFieldArray)>;
26 using ComponentType =
27 typename vtkm::VecTraits<typename InArrayHandleType::ValueType>::ComponentType;
28
29 vtkm::cont::ArrayHandle<ComponentType> outFieldArray;
30
31 this->Invoke(ComputeMagnitude{}, inFieldArray, outFieldArray);
32 outField = outFieldArray;
33 };
34
35 this->CastAndCallVecField<3>(inField, resolveType);
36
37 std::string outFieldName = this->GetOutputFieldName();
38 if (outFieldName == "")
39 {
40 outFieldName = inField.GetName() + "_magnitude";
41 }
42
43 return this->CreateResultField(
44 inDataSet, outFieldName, inField.GetAssociation(), outField);
45}
46
47} // namespace vector_calculus
48} // namespace filter
49} // namespace vtkm
The implementation of vtkm::filter::Filter::DoExecute()
first pulls the input field from the provided vtkm::cont::DataSet
using vtkm::filter::Filter::GetFieldFromDataSet()
.
It then uses vtkm::filter::Filter::CastAndCallVecField()
to determine what type of vtkm::cont::ArrayHandle
is contained in the input field.
That calls a lambda function that invokes a worklet to create the output field.
-
template<vtkm::IdComponent VecSize, typename Functor, typename ...Args>
inline void vtkm::filter::Filter::CastAndCallVecField(const vtkm::cont::UnknownArrayHandle &fieldArray, Functor &&functor, Args&&... args) const Convenience method to get the array from a filter’s input vector field.
A field filter typically gets its input fields using the internal
GetFieldFromDataSet
. To use this field in a worklet, it eventually needs to be converted to anvtkm::cont::ArrayHandle
. If the input field is limited to be a vector field with vectors of a specific size, then this method provides a convenient way to determine the correct array type. Like otherCastAndCall
methods, it takes as input avtkm::cont::Field
(orvtkm::cont::UnknownArrayHandle
) and a function/functor to call with the appropriatevtkm::cont::ArrayHandle
type. You also have to provide the vector size as the first template argument. For exampleCastAndCallVecField<3>(field, functor);
.
-
template<vtkm::IdComponent VecSize, typename Functor, typename ...Args>
inline void vtkm::filter::Filter::CastAndCallVecField(const vtkm::cont::Field &field, Functor &&functor, Args&&... args) const Convenience method to get the array from a filter’s input vector field.
A field filter typically gets its input fields using the internal
GetFieldFromDataSet
. To use this field in a worklet, it eventually needs to be converted to anvtkm::cont::ArrayHandle
. If the input field is limited to be a vector field with vectors of a specific size, then this method provides a convenient way to determine the correct array type. Like otherCastAndCall
methods, it takes as input avtkm::cont::Field
(orvtkm::cont::UnknownArrayHandle
) and a function/functor to call with the appropriatevtkm::cont::ArrayHandle
type. You also have to provide the vector size as the first template argument. For exampleCastAndCallVecField<3>(field, functor);
.
Did You Know?
The filter implemented in Example 4.56 is limited to only find the magnitude of vtkm::Vec
’s with 3 components.
It may be the case you wish to implement a filter that operates on vtkm::Vec
’s of multiple sizes (or perhaps even any size).
Chapter ref{chap:UnknownArrayHandle} discusses how you can use the vtkm::cont::UnknownArrayHandle
contained in the vtkm::cont::Field
to more expressively decide what types to check for.
-
template<typename Functor, typename ...Args>
inline void vtkm::filter::Filter::CastAndCallVariableVecField(const vtkm::cont::UnknownArrayHandle &fieldArray, Functor &&functor, Args&&... args) const This method is like
CastAndCallVecField
except that it can be used for a field of unknown vector size (or scalars).This method will call the given functor with an
vtkm::cont::ArrayHandleRecombineVec
.Note that there are limitations with using
vtkm::cont::ArrayHandleRecombineVec
within a worklet. Because the size of the vectors are not known at compile time, you cannot just create an intermediatevtkm::Vec
of the correct size. Typically, you must allocate the output array (for example, withvtkm::cont::ArrayHandleRuntimeVec
), and the worklet must iterate over the components and store them in the prealocated output.
-
template<typename Functor, typename ...Args>
inline void vtkm::filter::Filter::CastAndCallVariableVecField(const vtkm::cont::Field &field, Functor &&functor, Args&&... args) const This method is like
CastAndCallVecField
except that it can be used for a field of unknown vector size (or scalars).This method will call the given functor with an
vtkm::cont::ArrayHandleRecombineVec
.Note that there are limitations with using
vtkm::cont::ArrayHandleRecombineVec
within a worklet. Because the size of the vectors are not known at compile time, you cannot just create an intermediatevtkm::Vec
of the correct size. Typically, you must allocate the output array (for example, withvtkm::cont::ArrayHandleRuntimeVec
), and the worklet must iterate over the components and store them in the prealocated output.
Finally, vtkm::filter::Filter::CreateResultField()
generates the output of the filter.
Note that all fields need a unique name, which is the reason for the second argument to vtkm::filter::Filter::CreateResult()
.
The vtkm::filter::Filter
base class contains a pair of methods named vtkm::filter::Filter::SetOutputFieldName()
and vtkm::filter::Filter::GetOutputFieldName()
to allow users to specify the name of output fields.
The vtkm::filter::Filter::DoExecute()
method should respect the given output field name.
However, it is also good practice for the filter to have a default name if none is given.
This might be simply specifying a name in the constructor, but it is worthwhile for many filters to derive a name based on the name of the input field.
4.4.2. Deriving Fields from Topology
The previous example performed a simple operation on each element of a field independently.
However, it is also common for a “field” filter to take into account the topology of a data set.
In this case, the implementation involves pulling a vtkm::cont::CellSet
from the input vtkm::cont::DataSet
and performing operations on fields associated with different topological elements.
The steps involve calling vtkm::cont::DataSet::GetCellSet()
to get access to the vtkm::cont::CellSet
object and then using topology-based worklets, described in Section 4.3.2 (Topology Map), to operate on them.
In this section we provide an example implementation of a field filter on cells that wraps the “cell center” worklet provided in Example 4.44.
1namespace vtkm
2{
3namespace filter
4{
5namespace field_conversion
6{
7
8class VTKM_FILTER_FIELD_CONVERSION_EXPORT CellCenters : public vtkm::filter::Filter
9{
10public:
11 VTKM_CONT CellCenters();
12
13 VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inDataSet) override;
14};
15
16} // namespace field_conversion
17} // namespace filter
18} // namespace vtkm
As with any subclass of vtkm::filter::Filter
, the filter implements vtkm::filter::Filter::DoExecute()
, which in this case invokes a worklet to compute a new field array and then return a newly constructed vtkm::cont::DataSet
object.
1namespace vtkm
2{
3namespace filter
4{
5namespace field_conversion
6{
7
8VTKM_CONT
9CellCenters::CellCenters()
10{
11 this->SetOutputFieldName("");
12}
13
14VTKM_CONT cont::DataSet CellCenters::DoExecute(const vtkm::cont::DataSet& inDataSet)
15{
16 vtkm::cont::Field inField = this->GetFieldFromDataSet(inDataSet);
17
18 if (!inField.IsPointField())
19 {
20 throw vtkm::cont::ErrorBadType("Cell Centers filter operates on point data.");
21 }
22
23 vtkm::cont::UnknownArrayHandle outUnknownArray;
24
25 auto resolveType = [&](const auto& inArray) {
26 using InArrayHandleType = std::decay_t<decltype(inArray)>;
27 using ValueType = typename InArrayHandleType::ValueType;
28 vtkm::cont::ArrayHandle<ValueType> outArray;
29
30 this->Invoke(vtkm::worklet::CellCenter{}, inDataSet.GetCellSet(), inArray, outArray);
31
32 outUnknownArray = outArray;
33 };
34
35 vtkm::cont::UnknownArrayHandle inUnknownArray = inField.GetData();
36 inUnknownArray.CastAndCallForTypesWithFloatFallback<VTKM_DEFAULT_TYPE_LIST,
37 VTKM_DEFAULT_STORAGE_LIST>(
38 resolveType);
39
40 std::string outFieldName = this->GetOutputFieldName();
41 if (outFieldName == "")
42 {
43 outFieldName = inField.GetName() + "_center";
44 }
45
46 return this->CreateResultFieldCell(inDataSet, outFieldName, outUnknownArray);
47}
48
49} // namespace field_conversion
50} // namespace filter
51} // namespace vtkm
4.4.3. Data Set Filters
Sometimes, a filter will generate a data set with a new cell set based off the cells of an input data set. For example, a data set can be significantly altered by adding, removing, or replacing cells.
As with any filter, data set filters can be implemented in classes that derive the vtkm::filter::Filter
base class and implement its vtkm::filter::Filter::DoExecute()
method.
In this section we provide an example implementation of a data set filter that wraps the functionality of extracting the edges from a data set as line elements.
Many variations of implementing this functionality are given in Chapter~ref{chap:GeneratingCellSets}.
Suffice it to say that a pair of worklets will be used to create a new vtkm::cont::CellSet
, and this vtkm::cont::CellSet
will be used to create the result vtkm::cont::DataSet
.
Details on how the worklets work are given in Section ref{sec:GeneratingCellSets:SingleType}.
Because the operation of this edge extraction depends only on vtkm::cont::CellSet
in a provided vtkm::cont::DataSet
, the filter class is a simple subclass of vtkm::filter::Filter
.
1namespace vtkm
2{
3namespace filter
4{
5namespace entity_extraction
6{
7
8class VTKM_FILTER_ENTITY_EXTRACTION_EXPORT ExtractEdges : public vtkm::filter::Filter
9{
10public:
11 VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inData) override;
12};
13
14} // namespace entity_extraction
15} // namespace filter
16} // namespace vtkm
The implementation of vtkm::filter::Filter::DoExecute()
first gets the vtkm::cont::CellSet
and calls the worklet methods to generate a new vtkm::cont::CellSet
class.
It then uses a form of vtkm::filter::Filter::CreateResult()
to generate the resulting vtkm::cont::DataSet
.
1inline VTKM_CONT vtkm::cont::DataSet ExtractEdges::DoExecute(
2 const vtkm::cont::DataSet& inData)
3{
4 auto inCellSet = inData.GetCellSet();
5
6 // Count number of edges in each cell.
7 vtkm::cont::ArrayHandle<vtkm::IdComponent> edgeCounts;
8 this->Invoke(vtkm::worklet::CountEdgesWorklet{}, inCellSet, edgeCounts);
9
10 // Build the scatter object (for non 1-to-1 mapping of input to output)
11 vtkm::worklet::ScatterCounting scatter(edgeCounts);
12 auto outputToInputCellMap = scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
13
14 vtkm::cont::ArrayHandle<vtkm::Id> connectivityArray;
15 this->Invoke(vtkm::worklet::EdgeIndicesWorklet{},
16 scatter,
17 inCellSet,
18 vtkm::cont::make_ArrayHandleGroupVec<2>(connectivityArray));
19
20 vtkm::cont::CellSetSingleType<> outCellSet;
21 outCellSet.Fill(
22 inCellSet.GetNumberOfPoints(), vtkm::CELL_SHAPE_LINE, 2, connectivityArray);
23
24 // This lambda function maps an input field to the output data set. It is
25 // used with the CreateResult method.
26 auto fieldMapper = [&](vtkm::cont::DataSet& outData,
27 const vtkm::cont::Field& inputField) {
28 if (inputField.IsCellField())
29 {
30 vtkm::filter::MapFieldPermutation(inputField, outputToInputCellMap, outData);
31 }
32 else
33 {
34 outData.AddField(inputField); // pass through
35 }
36 };
37
38 return this->CreateResult(inData, outCellSet, fieldMapper);
39}
The form of vtkm::filter::Filter::CreateResult()
used (Example 4.60, line 38) takes as input a vtkm::cont::CellSet
to use in the generated data.
In forms of vtkm::filter::Filter::CreateResult()
used in previous examples of this chapter, the cell structure of the output was created from the cell structure of the input.
Because these cell structures were the same, coordinate systems and fields needed to be changed.
However, because we are providing a new vtkm::cont::CellSet
, we need to also specify how the coordinate systems and fields change.
The last two arguments to vtkm::filter::Filter::CreateResult()
are providing this information.
The second-to-last argument is a std::vector
of the vtkm::cont::CoordinateSystem
’s to use.
Because this filter does not actually change the points in the data set, the vtkm::cont::CoordinateSystem
’s can just be copied over.
The last argument provides a functor that maps a field from the input to the output.
The functor takes two arguments: the output vtkm::cont::DataSet
to modify and the input vtkm::cont::Field
to map.
In this example, the functor is defined as a lambda function (Example 4.60, line 26).
Did You Know?
The field mapper in Example 4.59 uses a helper function named vtkm::filter::MapFieldPermutation()
.
In the case of this example, every cell in the output comes from one cell in the input.
For this common case, the values in the field arrays just need to be permuted so that each input value gets to the right output value.
vtkm::filter::MapFieldPermutation()
will do this shuffling for you.
VTK‑m also comes with a similar helper function vtkm::filter::MapFieldMergeAverage()
that can be used when each output cell (or point) was constructed from multiple inputs.
In this case, vtkm::filter::MapFieldMergeAverage()
can do a simple average for each output value of all input values that contributed.
-
bool vtkm::filter::MapFieldPermutation(const vtkm::cont::Field &inputField, const vtkm::cont::ArrayHandle<vtkm::Id> &permutation, vtkm::cont::Field &outputField, vtkm::Float64 invalidValue = vtkm::Nan<vtkm::Float64>())
Maps a field by permuting it by a given index array.
This method will create a new field containing the data from the provided
inputField
but reorded by the givenpermutation
index array. The value in the resulting field for index i will be be a value frominputField
, but comes from the index that comes frompermutation
at position i. The result is placed inoutputField
.The intention of this method is to implement the mapping of fields from the input to the output in filters (many of which require this permutation of a field), but can be used in other places as well.
outputField
is set to have the same metadata as the input. If the metadata needs to change (such as the name or the association) that should be done after the function returns.This function returns whether the field was successfully permuted. If the returned result is
true
, then the results inoutputField
are valid. If it isfalse
, thenoutputField
should not be used.If an invalid index is given in the permutation array (i.e. less than 0 or greater than the size of the array), then the resulting outputField will be given
invalidValue
(converted as best as possible to the correct data type).
-
bool vtkm::filter::MapFieldPermutation(const vtkm::cont::Field &inputField, const vtkm::cont::ArrayHandle<vtkm::Id> &permutation, vtkm::cont::DataSet &outputData, vtkm::Float64 invalidValue = vtkm::Nan<vtkm::Float64>())
Maps a field by permuting it by a given index array.
This method will create a new field containing the data from the provided
inputField
but reorded by the givenpermutation
index array. The value in the resulting field for index i will be be a value frominputField
, but comes from the index that comes frompermutation
at position i.The intention of this method is to implement the
MapFieldOntoOutput
methods in filters (many of which require this permutation of a field), but can be used in other places as well. The resulting field is put in the givenDataSet
.The returned
Field
has the same metadata as the input. If the metadata needs to change (such as the name or the association), then a different form ofMapFieldPermutation
should be used.This function returns whether the field was successfully permuted. If the returned result is
true
, thenoutputData
has the permuted field. If it isfalse
, then the field is not placed inoutputData
.If an invalid index is given in the permutation array (i.e. less than 0 or greater than the size of the array), then the resulting outputField will be given
invalidValue
(converted as best as possible to the correct data type).
-
bool vtkm::filter::MapFieldMergeAverage(const vtkm::cont::Field &inputField, const vtkm::worklet::internal::KeysBase &keys, vtkm::cont::Field &outputField)
Maps a field by merging entries based on a keys object.
This method will create a new field containing the data from the provided
inputField
but but with groups of entities merged together. The inputkeys
object encapsulates which elements should be merged together. A group of elements merged together will be averaged. The result is placed inoutputField
.The intention of this method is to implement the
MapFieldOntoOutput
methods in filters (many of which require this merge of a field), but can be used in other places as well.outputField
is set to have the same metadata as the input. If the metadata needs to change (such as the name or the association) that should be done after the function returns.This function returns whether the field was successfully merged. If the returned result is
true
, then the results inoutputField
are valid. If it isfalse
, thenoutputField
should not be used.
-
bool vtkm::filter::MapFieldMergeAverage(const vtkm::cont::Field &inputField, const vtkm::worklet::internal::KeysBase &keys, vtkm::cont::DataSet &outputData)
Maps a field by merging entries based on a keys object.
This method will create a new field containing the data from the provided
inputField
but but with groups of entities merged together. The inputkeys
object encapsulates which elements should be merged together. A group of elements merged together will be averaged.The intention of this method is to implement the
MapFieldOntoOutput
methods in filters (many of which require this merge of a field), but can be used in other places as well. The resulting field is put in the givenDataSet
.The returned
Field
has the same metadata as the input. If the metadata needs to change (such as the name or the association), then a different form ofMapFieldMergeAverage
should be used.This function returns whether the field was successfully merged. If the returned result is
true
, thenoutputData
has the merged field. If it isfalse
, then the field is not placed inoutputData
.
Did You Know?
Although not the case in this example, sometimes a filter creating a new cell set changes the points of the cells.
As long as the field mapper you provide to vtkm::filter::Filter::CreateResult()
properly converts points from the input to the output, all fields and coordinate systems will be automatically filled in the output.
Sometimes when creating this new cell set you also create new point coordinates for it.
This might be because the point coordinates are necessary for the computation or might be due to a faster way of computing the point coordinates.
In either case, if the filter already has point coordinates computed, it can use vtkm::filter::Filter::CreateResultCoordinateSystem()
to use the precomputed point coordinates.
4.4.4. Data Set with Field Filters
Sometimes, a filter will generate a data set with a new cell set based off the cells of an input data set along with the data in at least one field. For example, a field might determine how each cell is culled, clipped, or sliced.
In this section we provide an example implementation of a data set with field filter that blanks the cells in a data set based on a field that acts as a mask (or stencil).
Any cell associated with a mask value of zero will be removed.
For simplicity of this example, we will use the vtkm::filter::entity_extraction::Threshold
filter internally for the implementation.
1namespace vtkm
2{
3namespace filter
4{
5namespace entity_extraction
6{
7
8class VTKM_FILTER_ENTITY_EXTRACTION_EXPORT BlankCells : public vtkm::filter::Filter
9{
10public:
11 VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inDataSet) override;
12};
13
14
15} // namespace entity_extraction
16} // namespace filter
17} // namespace vtkm
The implementation of vtkm::filter::Filter::DoExecute()
first derives an array that contains a flag whether the input array value is zero or non-zero.
This is simply to guarantee the range for the threshold filter.
After that a threshold filter is set up and run to generate the result.
1VTKM_CONT vtkm::cont::DataSet BlankCells::DoExecute(const vtkm::cont::DataSet& inData)
2{
3 vtkm::cont::Field inField = this->GetFieldFromDataSet(inData);
4 if (!inField.IsCellField())
5 {
6 throw vtkm::cont::ErrorBadValue("Blanking field must be a cell field.");
7 }
8
9 // Set up this array to have a 0 for any cell to be removed and
10 // a 1 for any cell to keep.
11 vtkm::cont::ArrayHandle<vtkm::FloatDefault> blankingArray;
12
13 auto resolveType = [&](const auto& inFieldArray) {
14 auto transformArray =
15 vtkm::cont::make_ArrayHandleTransform(inFieldArray, vtkm::NotZeroInitialized{});
16 vtkm::cont::ArrayCopyDevice(transformArray, blankingArray);
17 };
18
19 this->CastAndCallScalarField(inField, resolveType);
20
21 // Make a temporary DataSet (shallow copy of the input) to pass blankingArray
22 // to threshold.
23 vtkm::cont::DataSet tempData = inData;
24 tempData.AddCellField("vtkm-blanking-array", blankingArray);
25
26 // Just use the Threshold filter to implement the actual cell removal.
27 vtkm::filter::entity_extraction::Threshold thresholdFilter;
28 thresholdFilter.SetLowerThreshold(0.5);
29 thresholdFilter.SetUpperThreshold(2.0);
30 thresholdFilter.SetActiveField("vtkm-blanking-array",
31 vtkm::cont::Field::Association::Cells);
32
33 // Make sure threshold filter passes all the fields requested, but not the
34 // blanking array.
35 thresholdFilter.SetFieldsToPass(this->GetFieldsToPass());
36 thresholdFilter.SetFieldsToPass("vtkm-blanking-array",
37 vtkm::cont::Field::Association::Cells,
38 vtkm::filter::FieldSelection::Mode::Exclude);
39
40 // Use the threshold filter to generate the actual output.
41 return thresholdFilter.Execute(tempData);
42}