Cost of creating vtkMapper / vtkActor instances

I am continuing the implementation of my 3D scatterplot, consisting of spheres, each of which can have a different size and color.

My current implementation uses a vtkMultiBlockDataSet to hold all of the spheres as blocks. This is then mapped through a vtkCompositePolyDataMapper2 instance into a single vtkActor, as shown in the code below. (A “point” is just a struct with three doubles for x, y, and z coordinate). I haven’t posted the code that sets block colors, but it simply uses a vtkLookupTable to map from a scalar property to a color.

	vtkSmartPointer<vtkMultiBlockDataSet>	pointItems = vtkMultiBlockDataSet::New();
	auto nRow = 0;
	for ( auto point : points )
	{
		float	radius = 2.0 + 8.0 * ( point.z - minZ ) / ( maxZ - minZ );

	 	vtkSmartPointer<vtkPolyDataAlgorithm> pdItem = createSphere( point.x, point.y, point.z, radius );
		vtkSmartPointer<vtkDataSet>	pointItem = pdItem->GetOutput();
		pointItems->SetBlock( nRow, pointItem );

		nRow++;
	}

	vtkSmartPointer<vtkCompositePolyDataMapper2> itemsMapper = vtkCompositePolyDataMapper2::New();
	itemsMapper->SetInputDataObject( pointItems.GetPointer() );

	vtkSmartPointer<vtkActor> itemsActor = vtkActor::New();
	itemsActor->SetMapper( itemsMapper );

In trying to add zoom without scaling (as I asked in a previous post), I find that the position returned for this composite actor is always at the origin. I think that in order to get an accurate position for each sphere, I will have to reimplement this as separate spheres each with their own mapper / actor.pair.

A typical scatterplot could have hundreds to the low thousands of spheres. How expensive is it to create individual mapper / actor pairs for each of these? Is that an appropriate approach or is there a third way I should consider?

It is hard to tell from the books and examples how costly some things are, because the examples are usually very simple in nature and involve the creation of only a small number of objects of any type.

Thanks.

Indeed hundreds / thousands of mappers/actors would be expensive. vtkGlyph3DMapper would probably be a better option.

If you add hundreds of actors then the rendering speed will decrease significantly. Thousands of actors will cause serious performance issues.

Fortunately, there are glyph mappers that are intended to solve these kind of rendering tasks. You can easily render tens of thousands of points using small markers that you can individually color, scale, orient, etc. using point scalars.

There is a CPU-based (vtkGlyph3D, has a bit more features) and GPU-based (vtkGlyph3DMapper, faster) pipeline. If you find that any particular feature is missing (e.g., you want to mask or scale your glyphs in some special way) then you can easily extend/customize these classes, especially the CPU-based filter.

1 Like

Thanks for the replies. Glyphs look like they could work. The books give the impression that glyphs are mostly used to put zits on faces, but I see they are much more general than that.

I see from the ScaleGlyphs and ColorGlyphs examples how to scale or color, but how to do both simultaneously? Is there some other feature of vtkPolyData other than the vtkPoints that is used to hold a second set of information that could be the source of scale or color?

Sorry to be asking so many basic questions. The vtk API is overwhelmingly large and it is tough to absorb.

As far as I remember they are enabked/disabled independently. When in doubt, read the documentation and if that is still does not clear up everything then you can have a look at the source code.

Yes, I see that they are enabled independently. My question is how to set the (different) scalar arrays that specify scale and color?

ColorGlyphs does this for colors:

  // Setup scales. This can also be an Int array
  // char is used since it takes the least memory
  vtkNew<vtkUnsignedCharArray> colors;
  colors->SetName("colors");
  colors->SetNumberOfComponents(3);
  unsigned char r[3] = {255, 0, 0};
  unsigned char g[3] = {0, 255, 0};
  unsigned char b[3] = {0, 0, 255};
  colors->InsertNextTupleValue(r);
  colors->InsertNextTupleValue(g);
  colors->InsertNextTupleValue(b);

  // Combine into a polydata
  vtkNew<vtkPolyData> polydata;
  polydata->SetPoints(points);
  polydata->GetPointData()->SetScalars(colors);

// ...
  vtkNew<vtkGlyph3D> glyph3D;
  glyph3D->SetColorModeToColorByScalar();
  glyph3D->SetSourceConnection(cubeSource->GetOutputPort());
  glyph3D->SetInputData(polydata);

while ScaleGlyphs does something nearly identical for scale factors:

  // Setup scales
  vtkNew<vtkFloatArray> scales;
  scales->SetName("scales");

  scales->InsertNextValue(1.0);
  scales->InsertNextValue(2.0);
  scales->InsertNextValue(3.0);

  // Combine into a polydata
  vtkNew<vtkPolyData> polydata;
  polydata->SetPoints(points);
  polydata->GetPointData()->SetScalars(scales);

// ...
  vtkNew<vtkGlyph3D> glyph3D;
  glyph3D->SetScaleModeToScaleByScalar();
  glyph3D->SetSourceConnection(cubeSource->GetOutputPort());
  glyph3D->SetInputData(polydata);

In looking at the vtkGlyph3D::Execute() source code, there are references to a table of input connections, with the array at index 3 used to specify colors. If I create another vtkPolyData instance with the color array added as scalars to the point data, then try to set this on the vtkGlyph3D instance using SetInputData( 3, colordata ), this generates runtime warning that the algorithm has only 2 input ports:

ERROR: In vtkAlgorithm.cxx, line 878
vtkGlyph3D (00000297FC890170): Attempt to connect input port index 3 for an algorithm with 2 input ports.

So I obviously am missing something. Any help would be appreciated.

The filter only uses two input data objects: polydata containing points where you want to draw the glyphs; polydata of the glyph. You cannot specify a third input data object, that’s why you got the error when you called SetInputData(3, ...).

Within each data set (polydata) you can have any number of point and cell arrays. What you want is to specify which point data arrays of the first data set will be used for coloring, scaling, etc. Since the filter calls GetInputArrayToProcess to figure out which array to use, most likely you need to use SetInputArrayToProcess to set them.

If you are not sure about the syntax then:

  • you can find tests for all VTK features in the VTK repository (for example this)
  • you can expect to find dozens of examples for all the commonly used features in the VTK Examples website (for example, see list of tests that use vtkGlyph3D here and a relevant example here)
  • VTK is used in thousands of open-source projects, so you can find usage examples by searching for a VTK class name (for example, you get about 32000 hits if you search for vtkGlyph3D on GitHub - most likely some of those do exactly what you need, although it could be a while to sort through them).

Great, thanks. That’s the detail I was likely missing - that a vtkPolyData instance can have more than one set of points. I will figure out how to associate point sets with the appropriate scalar arrays for scale and color.

I have built all of the C++ examples from the VTK Examples site, and regularly use them to try to find answers before asking questions here. In this case, I found them too simplistic - toy examples that demonstrate only one feature, typically, with really sparse comments as to -why- a certain thing is being done.

Good tip about searching open source projects. I will use that in the future as well.

Thanks for your patience.

A data set, such as vtkPolyData has only one set of points (vtkPoints), but you can add any number of point data arrays. You can learn these fundamental VTK concepts from the VTK textbook.

I have purchased (and have read) both the textbook and the user guide, thanks.

After several hours of Googling, I did find this link, which seems very close to what I want.

1 Like