Kitware Source Feature Article: April 2009

Selections in VTK

In the June 2008 issue of the Kitware Source, the article “Selecting Data in ParaView” discussed ParaView’s powerful new selection facility and how it can be used to explore data in new ways. In this article, we are taking things down a level to explore the VTK selection architecture that ParaView uses. We will see how to use selections to manipulate data in any VTK-based application. All code in this article is based on VTK 5.4.

What is a selection?
In its most general sense, a selection is a data structure that specifies a subset of data. This subset may be highlighted to show a feature in the data, or may be extracted to analyze a portion of the data in more detail. VTK provides a framework for generating, processing, and sharing selections in applications using the vtkSelection class, along with related selection sources, filters and views.

vtkSelection is a container class holding one or more vtk-SelectionNode objects. Each node contains information indicating what part of the data is selected. A compound selection is interpreted as the union of the individual node selections. We allow selections to consist of multiple nodes so that in one place we can represent selections on multiple parts of the data. For example, a selection on geometry may contain a vtkSelectionNode for both points and cells. Another use case is to collect selections from multiple datasets in the same renderer into one place.

Types of selections
Each vtkSelectionNode has a selection type which indicates how the selection is to be interpreted. The selection types are constants defined in vtkSelectionNode.h. Values associated with a selection are stored in an array retrieved by GetSelectionList(). For convenience, we will use the term “element” to refer to the basic building-blocks of datasets to which attributes may be assigned. For vtkDataSet subclasses, the elements are points and cells. The elements of vtkGraph subclasses are vertices and edges. For vtkTable, the elements are the rows of the table.

Index selections
This is the most basic type of selection. An index selection’s selection list is a vtkIdTypeArray containing the raw zerobased indices of the selected elements in a dataset, using the dataset’s internal ordering. Since these indices may change as a dataset is processed or filtered, an index selection is generally only applicable to a single dataset. You should not share an index selection between datasets with different topologies.

Pedigree ID selections
A pedigree ID is an identifier assigned to each element in a dataset at its source, and is propagated down the pipeline. You specify pedigree IDs on a dataset in the same way that other special attributes like scalars and vectors are specified, by calling SetPedigreeIds() on a dataset’s attributes. Pedigree ID arrays may be of any type, including vtkStringArray and vtkVariantArray. A pedigree ID selection contains a list of values from a dataset’s pedigree ID array. Both pedigree ID and global ID selections refer to elements by name, instead of by a dataset-specific offset used in index selections, which makes them more robust across different datasets originating from the same source.

Global ID selections
Global ID selections are much like pedigree ID selections, except that they refer to the global ID attribute (set with SetGlobalIds()). Global IDs are used like pedigree IDs, except that they must be numeric, and some filters re-assign global IDs to ensure that IDs are never repeated.

Frustum selections
Frustum selections simply store the geometry of a frustum in the selection. All elements within the frustum are considered selected. This is most useful in cases where the user drag-selects a region in a 3D scene. The rectangular region on the screen translates into a frustum in the 3D scene. The selection list for this type of selection must be a vtkDouble-Array with eight four-component tuples. The points, in the form (x,y,z,1), should be in the following order:

View Frustrum Selection
The expected order of the points in a view frustum selection.

Value selections
A value selection is a selection that refers to elements in an arbitrary array. You must set the name of the selection list in a value selection to the name of the array that you want to select on. For example, suppose you have a dataset where the points contain an integer attribute “type” which varies from 0 to 10. To select only points with values 1, 3, and 7, create a vtkIntArray, add the desired values to it, then set the name of the array to “type”. Finally, with the array you’ve created call node->SetSelectionList(arr).

Threshold selections
Threshold selections work just like value selections, except you indicate value ranges with each pair of elements in the array. While value selections may be of any type, threshold selections only work on numeric arrays, and the selection list must be a vtkDoubleArray. To select points with type in the range 0-5, create a vtkDoubleArray and add the elements 0 and 5. You may add additional ranges to the threshold selection by adding more pairs of numbers.

Location selections
As the name suggests, you provide this type of selection with the 3D locations that you want selected. A location selection must be a vtkDoubleArray with 3 components per tuple. A location selection is often used to select cells that contain a point. For location selections referring to the points of a dataset, there is a way to specify the maximum distance a selected dataset point can be to a point in the selection list:

n->GetProperties()->Set(vtkSelectionNode::EPSILON distance);

Block selections
VTK has a data object called vtkMultiBlockDataset, which can store a collection of datasets. A block selection allows you to specify which blocks to select. The selection list must be a vtkUnsignedIntArray.

Creating a selection
The following C++ code creates a simple selection which selects points where the pedigree ID attribute is “John”, “Joe”, or “Sue”.

vtkSelection* s = vtkSelection::New();
vtkSelectionNode* n = vtkSelectionNode::New();
n->SetFieldType(vtkSelectionNode::POINTS);
n->SetContentType(vtkSelectionNode::PEDIGREEIDS);
vtkStringArray* a = vtkStringArray::New();
a->InsertNextValue(“John”);
a->InsertNextValue(“Joe”);
a->InsertNextValue(“Sue”);
n->SetSelectionList(a);
s->AddNode(n);

There are a few more selection properties that you should be aware of. You can invert a selection by setting the INVERSE property to 1 with the code:

n->GetProperties()
    ->Set(vtkSelectionNode::INVERSE(), 1);

Also, when performing a point selection on a vtkDataSet the CONTAINING_CELLS property indicates whether to also select cells which use at least one selected point. The test code located in VTK/Rendering/Testing/Cxx/TestExtraction. cxx creates and displays many different selection types and exercises various selection options.

Using the hardware selector
VTK also provides the class vtkHardwareSelector to assist you in generating selections from a rectangular region of the screen.

vtkHardwareSelector* hs =
   vtkHardwareSelector::New();
hs->SetRenderer(ren);
hs->SetArea(xmin, ymin, xmax, ymax);
vtkSelection* s = hs->Select();

Hardware Cell
The result of a hardware cell selection on two actors.

The hardware selector performs special rendering passes in order to determine which datasets in the renderer are selected, and which cells within those datasests are selected. Any cell that is rendered to at least one pixel within the selection area is inserted into the output selection. The output selection contains one vtkSelectionNode for each selected actor. These nodes are cell index selections by default, although the hardware selector can also be configured to select points. You can retrieve the pointer to the associated actor in the scene by accessing the PROP property of the selection node.

Extracting selections
Now that we know how to define a selection, we must use it in some way. One of the most common tasks is to extract the selection from a dataset. To do this, use the vtkExtractSelection filter for vtkDataSets or vtkMultiBlockDataSets, or use vtkExtractSelectedGraph for vtkGraphs. Both filters work in similar ways and accept a selection of any type. To extract a selection from a vtkPolyData, we can do the following:

vtkPolyData* pd = vtkPolyData::New();
// Populate the poly data here
vtkSelection* s = vtkSelection::New();
// Populate the selection here
vtkExtractSelection* ex =
   vtkExtractSelection::New();
ex->SetInput(0, pd);
ex->SetInput(1, s);
ex->Update();
vtkUnstructuredGrid* extracted = ex->GetOutput();

Note that because vtkExtractSelection accepts any vtk-DataSet subclass, the output is the most general dataset, vtkUnstructuredGrid. The output type cannot match the input type because a selection, which denotes an arbitrary subset on a structured type like vtkImageData, might no longer be structured. If you desire to simply mark selected elements of a dataset, call ex->PreserveTopologyOn() before updating the filter.

This will pass the data structurally unchanged to the output, with the same data type. Instead of culling away rejected elements, the filter adds a boolean flag to each element indicating whether it is selected or not.

Extracted vtkImageData Image
A portion of a vtkImageData extracted by a frustum selection.

Using selections in views
Selection is an important component of the new view architecture recently added to VTK in version 5.2. The views in VTK combine rendering logic, interaction, visualization parameters and selection into one place. Datasets are displayed in views by adding what are called “representations” to the view. A representation object prepares an input dataset to be displayed in a view. The view automatically generates selections of the desired type when the user performs a selection interaction (i.e. clicking or dragging a selection box). Selections may be shared across views by simply setting a common vtkSelectionLink object on multiple representations.

We will finish with the following example which generates a graph view and table view with linked selection. The table view displays the vertex data of the graph. When rows are selected in the table view, the graph view updates to reflect this, and vice versa.

QApplication app(argc, argv);

// Create the graph source and table conversion
vtkRandomGraphSource* src =
   vtkRandomGraphSource::New();
vtkDataObjectToTable* o2t =
   vtkDataObjectToTable::New();
o2t->SetInputConnection(src->GetOutputPort());
o2t->SetFieldType
   (vtkDataObjectToTable::VERTEX_DATA);

// Create Qt table view and add a representation
vtkQtTableView* tv = vtkQtTableView::New();
vtkDataRepresentation* tr;
tr = tv->AddRepresentationFromInputConnection
   (o2t->GetOutputPort());

// Create graph layout view
vtkGraphLayoutView* gv = vtkGraphLayoutView::New();
vtkRenderWindow* win = vtkRenderWindow::New();
gv->SetupRenderWindow(win);
gv->SetVertexLabelArrayName(“vertex id”);
gv->VertexLabelVisibilityOn();
gv->SetLayoutStrategyToSimple2D();

// Add representation to graph view
vtkDataRepresentation* gr;
gr = gv->AddRepresentationFromInputConnection
   (src->GetOutputPort());
gr->SetSelectionLink(tr->GetSelectionLink());

// Ensure both views update when selection changes
vtkViewUpdater* vu = vtkViewUpdater::New();
vu->AddView(gv);
vu->AddView(tv);

// Start application
tv->GetItemView()->show();
app.exec();

Linked Views
A simple application showing linked selection between a graph layout view and a Qt table view.

Acknowledgements
This work was supported in part by Sandia National Labs through contract number #664044.

Jeff Baumes  Dr. Jeff Baumes is an R&D Engineer in Kitware’s Clifton Park, NY office. Dr. Baumes is the main developer for Titan, whose goal is to bring information visualization and informatics capabilities to VTK.