VTK/CanvasAPI
After recent discussions I have been prototyping a simple canvas style API for VTK. The diagram below shows a rough equivalence to the QGraphicsView framework present in Qt, but with a much more basic API at this stage.
The vtkContextView essentially holds the rendering context, window etc and hooks into the RenderOverlay phase of VTK rendering. The vtkContextScene can hold multiple vtkContextItems (items derived from vtkContextItem). It handles coordination of rendering, propogation of events, hit detection and persistence. I have prepared a short example program with two movable vtkBlockItem objects. The performance in our initial tests is still good.
As these objects are drawn in the render overlay, integration with 3D VTK scenes should be much simpler. Due to the relatively thin layer of abstraction rendering performance is also very good.
Refactoring of the Scene
Making the scene into a real tree, introducing transform and clip nodes in the graph. This makes many things simpler, and allows things such as the charts to be composed entirely in the scene rather than encapsulating this logic internally.
vtkMark Back Ends
The following shows basically the same mark configuration built with a QGraphicsView-based canvas elements and the proposed VTK-based canvas.
Qt back-end | VTK/OpenGL back-end |
---|---|
Using Function Pointers
It seems that there is a wish to be able to declare functions that override certain aspects of rendering. In a traditional C++ API this would normally be accomplished by inheritance and overriding of a virtual function. The same thing can be accomplished by supplying function pointers without needing to derive a class.
The QtConcurrent API uses function pointers, and boost_bind optionally. Check out the relevant sections of their API documentation here for more details. So a specific signature is required for the function pointer, but this could optionally be created by boost::bind (or possibly boost lambda functions).
As an example the equivalent code would be.
<source lang="cpp">#include <mymark.h>
double scalarFunction(int index);
int main() {
MyMark *mark = new MyMark; mark->SetScalarFunctor(scalarFunction);
}
double scalarFunction(int index) {
return index * 3.0 / (index - 1.0);
}</source>
Compared with a more traditional inherited class API.
<source lang="cpp">#include <mymark.h>
class InheritedMark : public MyMark { protected:
virtual double scalarFunction(int index);
};
int main() {
InheritedMark *mark = new InheritedMark;
}
double InheritedMark::scalarFunction(int index) {
return index * 3.0 / (index - 1.0);
}</source>
Example Scene
Below is the boilerplate code to set up and instantiate a new scene.
<source lang="cpp">int main( int argc, char * argv [] ) {
// Qt initialization QApplication app(argc, argv); QMainWindow *mainWindow = new QMainWindow; mainWindow->setGeometry(0, 0, 800, 600);
// QVTK set up and initialization QVTKWidget *qvtkWidget = new QVTKWidget(mainWindow); mainWindow->setCentralWidget(qvtkWidget);
// Set up a 2D chart actor, APIDiagram object andn add them to the renderer VTK_CREATE(vtkContextActor, actor); VTK_CREATE(vtkRenderer, renderer); renderer->SetBackground(1.0, 1.0, 1.0); vtkRenderWindow *renderWindow = vtkRenderWindow::New(); renderWindow->AddRenderer(renderer); qvtkWidget->SetRenderWindow(renderWindow); renderer->AddActor(actor); // Try to set an interactor style on the chart vtkInteractorStyleRubberBand2D *interactor = vtkInteractorStyleRubberBand2D::New(); renderWindow->GetInteractor()->SetInteractorStyle(interactor); actor->GetScene()->AddInteractorStyle(interactor); // The scene currently needs the render window to trigger updates actor->GetScene()->SetWindow(renderWindow);</source>
Then the code to add a few new elements to the scene.
<source lang="cpp"> // Create two new block items and add them to the scene
VTK_CREATE(vtkBlockItem, block); actor->GetScene()->AddItem(block); block->SetLabel("Test"); block->SetDimensions(100, 50, 200, 100); block->SetScalarFunctor(myFunction); VTK_CREATE(vtkBlockItem, block2); actor->GetScene()->AddItem(block2); block2->SetLabel("Test 2"); block2->SetDimensions(100, 250, 200, 100);</source>
Finally, the event loop is started.
<source lang="cpp"> // Now show the application and start the event loop
mainWindow->show(); return app.exec();
}</source>