VTK/CanvasAPI: Difference between revisions

From KitwarePublic
< VTK
Jump to navigationJump to search
(Added some initial notes and a diagram.)
 
(Updated with some of the refactoring work)
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
After recent discussions I have been prototyping a simple canvas style API for VTK. This gives a rough equivalence to the QGraphicsView framework present in Qt, but with a much more basic API at this stage.
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.


<graphviz renderer='neato' caption='Chart Classes'>
<graphviz renderer='neato' caption='Chart Classes'>
Line 37: Line 41:
]
]


   vtkContextActor -> vtkContextScene
   vtkContextView -> vtkContextScene
   vtkContextScene -> vtkContextItem [headlabel="n" taillabel="1"]
   vtkContextScene -> vtkContextItem [headlabel="n" taillabel="1"]


Line 44: Line 48:
}
}
</graphviz>
</graphviz>
== 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.
<graphviz renderer='neato' caption='Chart Classes'>
digraph ContextItem {
  node [
    fontsize = 10
    fontname = Helvetica
    shape = record
    height = 0.1
  ]
  edge [
    fontsize = 10
    fontname = Helvetica
  ]
// Subclass relationships
//
// A -> B means
// A is the superclass of B
// The notation is a bit counterintuitive but required to obtain
// the desired top-to-bottom layout
edge [
  arrowtail = onormal
  arrowhead = none
]
  vtkAbstractContextItem -> vtkContextItem
  vtkAbstractContextItem -> vtkContextTransform
  vtkAbstractContextItem -> vtkContextClip
  vtkContextItem -> vtkBlockItem
  vtkContextItem -> vtkLineItem
// "has a pointer to"
edge [
  arrowtail = odiamond
  arrowhead = none
]
  vtkContextView -> vtkContextScene
  vtkContextScene -> vtkAbstractContextItem [headlabel="n" taillabel="1"]
  vtkAbstractContextItem -> vtkAbstractContextItem [headlabel="n" taillabel="1"]
}
</graphviz>
== 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
|-
| [[Image:MarkExplorer-Qt.png]] || [[Image: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 [http://doc.qt.nokia.com/4.5/qtconcurrentmap.html 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>

Latest revision as of 16:48, 11 June 2010

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.

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