Skip to content
Snippets Groups Projects

Split up filter documentation into basic and advanced development

Merged Kenneth Moreland requested to merge kmorel/vtk-m-user-guide:basic-filter-impl into master
Files
3
+ 77
0
% -*- latex -*-
\chapter{Basic Filter Implementation}
\label{chap:BasicFilterImpl}
\index{filter!implementation|(}
Chapter \ref{chap:SimpleWorklets} introduced the concept of a worklet and demonstrated how to create and one run to execute an algorithm on a device.
Although worklets provide a powerful mechanism for designing heavily threaded visualization algorithms, invoking them requires quite a bit of knowledge of the workings of \VTKm.
Instead, most users execute algorithms in \VTKm using filters.
Thus, to expose algorithms implemented with worklets to general users, we need to implement a filter to encapsulate the worklets.
In this chapter we will create a filter that encapsulate the worklet algorithm presented in Chapter \ref{chap:SimpleWorklets}, which converted the units of a pressure field from pounds per square inch (psi) to Newtons per square meter ($\mathrm{N}/\mathrm{m}^2$).
Filters in \VTKm are implemented by deriving one of the filter base classes provided in \vtkmfilter{}.
There are multiple base filter classes that we can choose from.
These different classes are documented later in Chapter \ref{chap:FilterTypeReference}.
For this example we will use the \vtkmfilter{FilterField} base class, which is used to derive one field from another field.
The following example shows the declaration of our pressure unit conversion filter.
By convention, this declaration would be placed in a header file with a \textfilename{.h} extension.
\vtkmlisting[ex:SimpleField]{Header declaration for a simple filter.}{SimpleField.cxx}
It should be noted that the \textidentifier{FilterField} superclass is actually a templated class that expects to be templated by the concrete subclass (in this case the \textcode{PoundsPerSquareInchToNewtonsPerSquareMeterFilter} class we are creating).
This allows templated methods in the superclass call templated methods in the concrete subclass.
This technique of templating a superclass based on the subclass is known as the Curiously Recurring Template Pattern (CRTP).
It is typical for a filter to have a constructor to set up its initial state.
A filter will also implement a \classmember*{FilterField}{DoExecute} method.
Different filter types require different arguments for \classmember*{Filter}{DoExecute}, but the \textidentifier{FilterField} version has 4 arguments: the input \textidentifier{DataSet}, an \textidentifier{ArrayHandle} holding the data for the input field, metadata about the input field, and a policy used to pull other information from the input \textidentifier{DataSet}.
(We will not need to use the policy in this example as the superclass will give us all the data we need.
Policies are demonstrated in Chapter \ref{chap:FilterTypeReference}.)
Once the filter class is declared in the \textfilename{.h} file, the implementation filter is by convention given in a separate \textfilename{.hxx} file.
Given the definition of our filter in Example \ref{ex:SimpleField}, we will need to provide the implementation for the constructor and the \classmember*{FilterField}{DoExecute} method.
The constructor is quite simple.
It initializes the name of the output field name, which is managed by the superclass.
\vtkmlisting[ex:SimpleFieldConstructor]{Constructor for a simple filter.}{SimpleFieldConstructor.cxx}
In this case, we are setting the output field name to the empty string.
This is not to mean that the default name of the output field should be the empty string, which is not a good idea.
Rather, as we will see later, we will use the empty string to flag an output name that should be derived from the input name.
The meat of the filter implementation is located in the \classmember*{FilterField}{DoExecute} method.
\vtkmlisting[ex:SimpleFieldDoExecute]{Implementation of \textcode{DoExecute} for a simple filter.}{SimpleFieldDoExecute.cxx}
The second argument to \classmember*{FilterField}{DoExecute} is a \vtkmcont{ArrayHandle} that contains the data for the input field.
This argument is templated because, as described in Chapter \ref{chap:BasicArrayHandles}, \textidentifier{ArrayHandle}s are templated classes that can change based on the types of values and how they are stored.
The superclass has determined what these types are and has passed a concrete version of the \textidentifier{ArrayHandle} to \classmember*{FilterField}{DoExecute}.
It is good practice to check that the type we get is in fact an \textidentifier{ArrayHandle} using the \vtkmmacro{VTKM\_IS\_ARRAY\_HANDLE} macro (demonstrated in line \ref{ex:SimpleFieldDoExecute.cxx:CheckArray} of Example \ref{ex:SimpleFieldDoExecute}).
The first operation our filter does is to create a new \textidentifier{ArrayHandle} to store the new computed field (line \ref{ex:SimpleFieldDoExecute.cxx:CreateOutputArray}).
The type of the computed output field is typically derived from the type of the input array.
Once the filter has both the input and output \textidentifier{ArrayHandle}s, it is ready to invoke the worklet to compute the output.
For convenience, all filter superclasses provide an \classmember*{Filter}{Invoke} member that is a \vtkmcont{Invoker} class.
Thus, worklets can be invoked within a filter by calling \textcode{this->Invoke} as if it were a method.
This is shown starting on line \ref{ex:SimpleFieldDoExecute.cxx:Invoke}.
Recall that the worklet created in Chapter \ref{chap:SimpleWorklets} takes two parameters: an input array and an output array, which are shown in this invocation.
With the output data created, the filter has to build the output structure to return.
All implementations of \classmember*{Filter}{DoExecute} must return a \vtkmcont{DataSet}, and for a simple field filter like this we want to return the same \textidentifier{DataSet} as the input with the output field added.
The output field needs a name, and we get the appropriate name from the superclass (line \ref{ex:SimpleFieldDoExecute.cxx:OutputName}).
However, we would like a special case where if the user does not specify an output field name we construct one based on the input field name.
Recall from Example \ref{ex:SimpleFieldConstructor} that by default we set the output field name to the empty string.
Thus, our filter checks for this empty string, and if it is encountered, it builds a field name by appending ``\_N/M\^2'' to it.
Finally, our filter constructs the output \textidentifier{DataSet} using the \vtkmfilter{CreateResult} helper function.
There are multiple versions of \textidentifier{CreateResult}, but the version we are using is adding our new array as a field as the same type as the input.
This chapter has just provided a brief introduction to creating filters.
There are several more filter superclasses to help express algorithms of different types.
After some more worklet concepts to implement more complex algorithms are introduced in Part \ref{part:Advanced}, we will see a more complete documentation of the types of filters in Chapter \ref{chap:FilterTypeReference}.
\index{filter!implementation|)}
Loading