VTK/2DAPI: Difference between revisions

From KitwarePublic
< VTK
Jump to navigationJump to search
(Added some notes about the 2D API)
 
mNo edit summary
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
The 2D API is currently composed of two levels, a concreate class called vtk2DPainter that is called by the paint functions of components operating within the 2D API, and a vtk2DPaintDevice which is called by the vtk2DPainter to actually draw to a context. The vtk2DPainter contains a pointer to the derived class of the vtk2DPaintDevice to do low level painting. This is the class that must be implemented for a new backend to be supported. The vtk2DPainter builds up more complex 2D constructs on top of the basic constructs implemented in the device.
The 2D API is currently composed of two levels, a concreate class called vtkContext2D that is called by the paint functions of components operating within the 2D API, and a vtkContextDevice2D which is called by the vtkContext2D to actually draw to a context. The vtkContext2D contains a pointer to the derived class of the vtkContextDevice2D to do low level painting. This is the class that must be implemented for a new backend to be supported. The vtkContext2D builds up more complex 2D constructs on top of the basic constructs implemented in the device.
 
Currently we only have an OpenGL context device, but this could change in the future.


<graphviz renderer='neato' caption='2D API'>
<graphviz renderer='neato' caption='2D API'>
Line 16: Line 18:


   // Classes
   // Classes
   vtk2DPainter
   vtkContext2D


   vtk2DPaintDevice
   vtkContextDevice2D
   vtk2DGLPaintDevice
   vtkOpenGLContextDevice2D
   vtk2DQtPaintDevice
   vtkQtContextDevice2D?
  vtk2DCairoPaintDevice


// Subclass relationships
// Subclass relationships
Line 33: Line 34:
]
]


   vtk2DPaintDevice -> vtk2DGLPaintDevice
   vtkContextDevice2D -> vtkOpenGLContextDevice2D
   vtk2DPaintDevice -> vtk2DQtPaintDevice
   vtkContextDevice2D -> vtkQtContextDevice2D?
  vtk2DPaintDevice -> vtk2DCairoPaintDevice


// "owns the lifetime of"
// "owns the lifetime of"
Line 49: Line 49:
]
]


   vtk2DPainter -> vtk2DPaintDevice
   vtkContext2D -> vtkContextDevice2D
}
}
</graphviz>
</graphviz>


The class relationship diagram is shown above. The headers for the two classes look as follows.
The class relationship diagram is shown above. This a [http://en.wikipedia.org/wiki/Bridge_pattern bridge design pattern] where <em>Abstraction</em> is <tt>vtkContext2D</tt>, <em>Implementor</em> is <tt>vtkContextDevice2D</tt> and <em>ConcreteImplementors</em> are <tt>vtkOpenGLContextDevice2D</tt> and <tt>vtkQtContextDevice2D</tt>. There is no <em>RefinedAbstraction</em>.
 
The headers for the two classes look as follows.


== vtk2DPainter ==
== vtkContext2D ==


<source lang="cpp">
<source lang="cpp">
class VTK_CHARTS_EXPORT vtk2DPainter : public vtkObject
class VTK_CHARTS_EXPORT vtkContext2D : public vtkObject
{
{
public:
public:
   vtkTypeRevisionMacro(vtk2DPainter, vtkObject);
   vtkTypeRevisionMacro(vtkContext2D, vtkObject);
   virtual void PrintSelf(ostream &os, vtkIndent indent);
   virtual void PrintSelf(ostream &os, vtkIndent indent);
   static vtk2DPainter *New();
   static vtkContext2D *New();
    
    
   bool Begin(vtk2DPaintDevice *device);
  // Begin painting, a valid context device is required
   bool Begin(vtkContextDevice2D *device);
 
  // Perform any cleanup that might be necessary
   bool End();
   bool End();


Line 88: Line 93:


protected:
protected:
   vtk2DPainter();
   vtkContext2D();
   ~vtk2DPainter();
   ~vtkContext2D();
};
};
</source>
</source>


== vtk2DPaintDevice (Abstract) ==
== vtkContextDevice2D (Abstract) ==


While the vtk2DPaintDevice class is an abstract class. The header for the equivalent functionality is,
While the vtkContextDevice2D class is an abstract class. The header for the equivalent functionality is,


<source lang="cpp">
<source lang="cpp">
class VTK_CHARTS_EXPORT vtk2DPaintDevice : public vtkObject
class VTK_CHARTS_EXPORT vtkContextDevice2D : public vtkObject
{
{
public:
public:
   vtkTypeRevisionMacro(vtk2DPaintDevice, vtkObject);
   vtkTypeRevisionMacro(vtkContextDevice2D, vtkObject);
   virtual void PrintSelf(ostream &os, vtkIndent indent);
   virtual void PrintSelf(ostream &os, vtkIndent indent);
   static vtk2DPaintDevice *New();
   static vtkContextDevice2D *New();
    
    
   // Set up the paint device context
   // Set up the paint device context
Line 117: Line 122:
   virtual void DrawPoints(vtkPoints2D *points) = 0;
   virtual void DrawPoints(vtkPoints2D *points) = 0;


   // Manage the state of the painter
   // Manage the state of the paint device
   void SetColor(int r, int g, int b, int a);
   virtual void SetColor(int r, int g, int b, int a) = 0;
   void SetPointSize(float size);
   virtual void SetPointSize(float size) = 0;
   void SetLineWidth(float width);
   virtual void SetLineWidth(float width) = 0;
 
protected:
  vtkContextDevice2D();
  ~vtkContextDevice2D();
};
</source>
 
== Drawing Marks ==
 
The [http://kitware.com/InfovisWiki/index.php/Views_and_Charts Views and Charts] page contains details of a higher level API for drawing marks. Modifying this slightly to sit above the vtkContext2D API would allow a backend agnostic programmable glyph renderer.
 
<source lang="cpp">
class vtkPointMark
{
public:
  virtual int GetNumberOfParameters();
 
  virtual const char* GetParameterName( int param );
  virtual int GetParameterHandle( const char* paramName );
  virtual vtkInformation* GetParameterInformation( int param );
  virtual vtkInformation* GetParameterInformation( const char* name );
 
  virtual void ResetParameters();
 
  virtual void BindParameter( int param, vtkVariant& value );
  virtual void BindParameter( const char* paramName, vtkVariant& value );
 
  virtual void BindParameter( int param, vtkAbstractArray* values, int component );
  virtual void BindParameter( const char* paramName, vtkAbstractArray* values, int component );
 
  virtual int GetParameterBinding(
    int param, vtkVariant& constVal, vtkAbstractArray*& arrayVal, int& component );
  virtual int GetParameterBinding(
    const char* paramName, vtkVariant& constVal, vtkAbstractArray*& arrayVal, int& component );
 
  virtual void DrawMarks( vtkContext2D* context,
    vtkDataArray* xCoords, int xComponent,
    vtkDataArray* yCoords, int yComponent,
    vtkIdType start, vtkIdType end, vtkIdType stride );


protected:
protected:
   vtk2DPaintDevice();
   void GetParameterValuesForTuple( vtkIdType pt, vtkVariantArray* pvals );
   ~vtk2DPaintDevice();
   virtual void SetupParameters() = 0;
};
};
</source>
</source>
== Performance Considerations ==
One of the goals of this project is to create charts that scale well to large data sets. I have been examining several techniques to accomplish this goal, mainly centered around passing arrays to drawing functions to draw multiple 2D primitives using only one function call. If we use a virtual base class for the context device then it is important to be able to draw large numbers of points without multiple function calls. Even without virtual function calls, the function call overhead can become the bottleneck, i.e. OpenGL glVertex3fv versus glVertexPointer.
Most rendering backends expect coordinates to be packed in memory in certain ways. One of the most common is as a 1D array with of length tuple * number of points. The vtkPoints2D and vtkPoints (3D) classes achieve this packing in their underlying data structure. Using this data structure will lead to the best performance, otherwise the context must repack the arrays (such as data coming from multiple columns in a table).
This is the motivation for using functions with signatures that take multiple points. In my initial testing this has worked well with millions of points and maps well to OpenGL's API. I will work on adding some benchmarking to properly quantify how well this scales. It would be an interesting way to summarize the performance of various architectures.
<source lang="cpp">void DrawPoints(vtkPoints2D *points);</source>

Latest revision as of 19:58, 17 May 2010

The 2D API is currently composed of two levels, a concreate class called vtkContext2D that is called by the paint functions of components operating within the 2D API, and a vtkContextDevice2D which is called by the vtkContext2D to actually draw to a context. The vtkContext2D contains a pointer to the derived class of the vtkContextDevice2D to do low level painting. This is the class that must be implemented for a new backend to be supported. The vtkContext2D builds up more complex 2D constructs on top of the basic constructs implemented in the device.

Currently we only have an OpenGL context device, but this could change in the future.

2D API

The class relationship diagram is shown above. This a bridge design pattern where Abstraction is vtkContext2D, Implementor is vtkContextDevice2D and ConcreteImplementors are vtkOpenGLContextDevice2D and vtkQtContextDevice2D. There is no RefinedAbstraction.

The headers for the two classes look as follows.

vtkContext2D

<source lang="cpp"> class VTK_CHARTS_EXPORT vtkContext2D : public vtkObject { public:

 vtkTypeRevisionMacro(vtkContext2D, vtkObject);
 virtual void PrintSelf(ostream &os, vtkIndent indent);
 static vtkContext2D *New();
 
 // Begin painting, a valid context device is required
 bool Begin(vtkContextDevice2D *device);
 // Perform any cleanup that might be necessary
 bool End();
 // Line drawing functions
 void DrawLine(float x1, float y1, float x2, float y2);
 void DrawLine(vtkPoints2D *points);
 void DrawPoly(float *x, float *y, int n);
 void DrawPoly(vtkPoints2D *points);
 void DrawRectangle(float x, float y, float width, float height);
 void DrawRectangle(float *p);
 void DrawRectangle(vtkPoints2D *points);
 // Point drawing functions
 void DrawPoint(float x, float y);
 void DrawPoints(float *x, float *y, int n);
 void DrawPoints(vtkPoints2D *points);
 // Manage the state of the painter
 void SetColor(int r, int g, int b, int a);
 void SetPointSize(float size);
 void SetLineWidth(float width);

protected:

 vtkContext2D();
 ~vtkContext2D();

}; </source>

vtkContextDevice2D (Abstract)

While the vtkContextDevice2D class is an abstract class. The header for the equivalent functionality is,

<source lang="cpp"> class VTK_CHARTS_EXPORT vtkContextDevice2D : public vtkObject { public:

 vtkTypeRevisionMacro(vtkContextDevice2D, vtkObject);
 virtual void PrintSelf(ostream &os, vtkIndent indent);
 static vtkContextDevice2D *New();
 
 // Set up the paint device context
 virtual void Begin(vtkRenderer* renderer) { }
 
 // Clean anything up once rendering has been completed
 virtual void End() { }
 // Line drawing functions
 virtual void DrawPoly(vtkPoints2D *points) = 0;
 // Point drawing functions
 virtual void DrawPoints(vtkPoints2D *points) = 0;
 // Manage the state of the paint device
 virtual void SetColor(int r, int g, int b, int a) = 0;
 virtual void SetPointSize(float size) = 0;
 virtual void SetLineWidth(float width) = 0;

protected:

 vtkContextDevice2D();
 ~vtkContextDevice2D();

}; </source>

Drawing Marks

The Views and Charts page contains details of a higher level API for drawing marks. Modifying this slightly to sit above the vtkContext2D API would allow a backend agnostic programmable glyph renderer.

<source lang="cpp"> class vtkPointMark { public:

 virtual int GetNumberOfParameters();
 virtual const char* GetParameterName( int param );
 virtual int GetParameterHandle( const char* paramName );
 virtual vtkInformation* GetParameterInformation( int param );
 virtual vtkInformation* GetParameterInformation( const char* name );
 virtual void ResetParameters();
 virtual void BindParameter( int param, vtkVariant& value );
 virtual void BindParameter( const char* paramName, vtkVariant& value );
 virtual void BindParameter( int param, vtkAbstractArray* values, int component );
 virtual void BindParameter( const char* paramName, vtkAbstractArray* values, int component );
 virtual int GetParameterBinding(
   int param, vtkVariant& constVal, vtkAbstractArray*& arrayVal, int& component );
 virtual int GetParameterBinding(
   const char* paramName, vtkVariant& constVal, vtkAbstractArray*& arrayVal, int& component );
 virtual void DrawMarks( vtkContext2D* context,
   vtkDataArray* xCoords, int xComponent,
   vtkDataArray* yCoords, int yComponent,
   vtkIdType start, vtkIdType end, vtkIdType stride );

protected:

 void GetParameterValuesForTuple( vtkIdType pt, vtkVariantArray* pvals );
 virtual void SetupParameters() = 0;

}; </source>

Performance Considerations

One of the goals of this project is to create charts that scale well to large data sets. I have been examining several techniques to accomplish this goal, mainly centered around passing arrays to drawing functions to draw multiple 2D primitives using only one function call. If we use a virtual base class for the context device then it is important to be able to draw large numbers of points without multiple function calls. Even without virtual function calls, the function call overhead can become the bottleneck, i.e. OpenGL glVertex3fv versus glVertexPointer.

Most rendering backends expect coordinates to be packed in memory in certain ways. One of the most common is as a 1D array with of length tuple * number of points. The vtkPoints2D and vtkPoints (3D) classes achieve this packing in their underlying data structure. Using this data structure will lead to the best performance, otherwise the context must repack the arrays (such as data coming from multiple columns in a table).

This is the motivation for using functions with signatures that take multiple points. In my initial testing this has worked well with millions of points and maps well to OpenGL's API. I will work on adding some benchmarking to properly quantify how well this scales. It would be an interesting way to summarize the performance of various architectures.

<source lang="cpp">void DrawPoints(vtkPoints2D *points);</source>