From KitwarePublic
Revision as of 16:48, 11 June 2010 by Marcus.hanwell (talk | contribs) (Updated with some of the refactoring work)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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.

Chart Classes

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.

Chart Classes

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
MarkExplorer-Qt.png MarkExplorer-VTK.png

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.

#include <mymark.h>

double scalarFunction(int index);

int main()
  MyMark *mark = new MyMark;

double scalarFunction(int index)
  return index * 3.0 / (index - 1.0);

Compared with a more traditional inherited class API.

#include <mymark.h>

class InheritedMark : public MyMark
  virtual double scalarFunction(int index);

int main()
  InheritedMark *mark = new InheritedMark;

double InheritedMark::scalarFunction(int index)
  return index * 3.0 / (index - 1.0);

Example Scene

Below is the boilerplate code to set up and instantiate a new scene.

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);

  // 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();
  // Try to set an interactor style on the chart
  vtkInteractorStyleRubberBand2D *interactor = vtkInteractorStyleRubberBand2D::New();
  // The scene currently needs the render window to trigger updates

Then the code to add a few new elements to the scene.

  // Create two new block items and add them to the scene
  VTK_CREATE(vtkBlockItem, block);
  block->SetDimensions(100, 50, 200, 100);
  VTK_CREATE(vtkBlockItem, block2);
  block2->SetLabel("Test 2");
  block2->SetDimensions(100, 250, 200, 100);

Finally, the event loop is started.

  // Now show the application and start the event loop
  return app.exec();