VTK::ViewsRendering: new View and Representation classes

The VTK::ViewsRendering module merged to master last week (!13418). It is the beginning of a view and representation layer for scientific visualization in VTK, and since there is a good deal more to come, we wanted to explain what we are building and why, and get feedback from the community.

What we are building

VTK has had vtkView and vtkDataRepresentation in Views/Core for many years. They came out of the information visualization work, and a scientific visualization layer was always intended to follow but never got built. What exists on top of that foundation today is Views/Infovis, Views/Context2D and Views/Qt.

ParaView built views and representations of its own instead, outside VTK, because its design is organized around the client/server split. That has kept them out of reach for everyone who is not ParaView, and it also means they do not behave the way ordinary VTK does — a render does not update the pipeline, because representations are split across processes.

With trame offering a different answer to client/server rendering, filling in the scientific visualization side inside VTK is now worth doing. That is what this module starts.

The design

A representation owns everything needed to draw one dataset a particular way: the extraction pipeline, the mapper, the actor and their properties, behind one flat API. vtkSurfaceRepresentation covers surface geometry, including composite datasets, AMR and hyper tree grids through vtkGeometryFilterDispatcher. vtkVolumeRepresentation covers volume rendering on vtkSmartVolumeMapper.

A view owns the renderer, render window and interactor, and starts with defaults chosen so a scene looks reasonable before you configure anything — a trackball camera, a gradient background, orientation axes, a light kit.

vtkNew<vtkStandardRenderView> view;
vtkNew<vtkSurfaceRepresentation> rep;
rep->SetInputConnection(source->GetOutputPort());
rep->SetColor(0.8, 0.2, 0.2);
rep->SetRepresentationToSurfaceWithEdges();
view->AddRepresentation(rep);
view->ResetCamera();
view->Start();

The same scene in Python, where show() creates the representation, connects it, applies properties and adds it in one call:

view = vtkStandardRenderView(window_title="Demo")
view.show(source, color="tomato", representation="surfacewithedges")
view.Start()

The convenience is useful, but it is not the reason for the design. The reason is that a great deal of what makes an application feel finished is policy that spans representations, and there has never been a place in VTK to put it.

Selection is one example. The view drives it: a rubber band picks across every representation, each converts the result back to its own input, and the view holds the combined selection and fires an event. Linked selection between views follows naturally from there.

Scalar bars are a clearer one, and are next. The view — not the representation — keeps one scalar bar per array actually being rendered. Two representations coloring by “Pressure” are put on the same lookup table, so they agree on colors; their data ranges are merged, so they agree on range; a bar appears when something colors by that array and is retired when nothing does. A color map of your own is registered once, by array name, and every representation drawing that array picks it up:

view->GetLookupTableManager()->SetLookupTable("Pressure", myTable);

None of that falls out of wrapping a mapper and an actor. It has to live above the representations, and today every application either reinvents it or does without.

Why it is in C++, and in VTK

The layer is written in C++ so that it is not Python-only. C++ applications, WASM and all the wrapped languages get the same views and representations. It also means the fluent Python API we want to build on top of this — something along the lines of read().contour().show() — is a thin layer over shared behavior rather than a parallel implementation of it.

How it relates to ParaView

ParaView’s views and representations solve a harder problem, distributed client/server rendering, and are shaped by it. We are not proposing that ParaView move onto these, and I do not think VTK should wait on that question to have a usable view layer of its own.

What is coming

Scalar bars and the lookup table manager described above are written and go up shortly. After that: annotation representations for text overlays and grid axes, camera presets and view directions, and screenshot and scene export.

Where we would like input

Nothing here has shipped in a release yet and none of it is driven by a deadline. So there is real room to change the design, and these are the places we would like to hear from people.

The representation contract. Right now the view finds out what a representation is drawing by downcasting to the two classes it knows about. There is no contract that says “this representation colors by an array,” which means a representation written outside this module cannot participate in scalar bars at all. I think the answer is to make the taxonomy explicit — data representations that color by an array, and annotation representations that do not, since text overlays and grid axes are both zero-input — with an intermediate base declaring the array-and-range methods the view needs. I would like opinions on the shape of that before we write it.

Names. This is where we would most welcome help. vtkStandardRenderView in particular: “Standard” says very little, and I chose it mainly because Views/Infovis already has a vtkRenderView that does something else and I did not want to change its behavior. The module name sits a little oddly next to the other Views/ modules as well. Concrete suggestions very welcome.

Scope. What belongs in a representation, what belongs on the view, and what should not be in this module at all.

Frist, I wanted to say that I have been waiting for a first-party high-level API for VTK for a long time, so I’m very happy to see it materialize! I hope it will strengthen adoption of the framework on all the languages and platforms that we support.

I like the general approach of hiding the complexity of mapper/actor/renderwindow/intreractor behind a single class for a 1-liner scene setup.

However, there may be too much mixing of concerns in a single class. vtkStandardRenderView and derivatives expose a very large public API on a single level; They touch very different areas and concepts, that could be grouped under multiple namespaces, such as selection/picking, light, mesh, etc.

From Python, I’d really like to do something like:

view.select.Mode = "Surface"
view.select.UseCells()
view.select.Clear()

view.light.Intensity = 0.8
...

I would also have loved an API that we could share with ParaView, but I understand that this would be a much larger task.

How is this?

https://gitlab.kitware.com/vtk/vtk/-/merge_requests/13589

Regarding the naming, I would suggest to avoid any existing concept in VTK like “view” or “renderer”. Choosing a new word for this class would avoid ambiguity.

I understand that the class class purpose is to quickly look at a data without the usual VTK boilerplate. Here some suggestions: Glimpse, Canvas, Insight, QuickView, SimpleView, SneakPeek.

… Inspector, Visage, Reveal, Show, Glance, Vista, Look, Phiz (yes it’s a word :-))…

The modules is currently in Views → Rendering. The Views part is set in stone because it is inherited from the existing Views hierarchy. Rendering is what we have control over. The Views kit has the following modules : Context2D; Core; Infovis; Qt; Rendering. After thinking about this some and going back and forth with @Louis_Gombert on the design, the best fit for this modules is something like Scivis or Vis3D. Infovis renders things like graphs whereas “Rendering” renders things like polydata, unstructured grids, volumes…