VTK/2DAPI
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 class relationship diagram is shown above. The headers for the two classes look as follows.
vtk2DPainter
<source lang="cpp"> class VTK_CHARTS_EXPORT vtk2DPainter : public vtkObject { public:
vtkTypeRevisionMacro(vtk2DPainter, vtkObject); virtual void PrintSelf(ostream &os, vtkIndent indent); static vtk2DPainter *New(); // Begin painting, a valid paint device is required bool Begin(vtk2DPaintDevice *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:
vtk2DPainter(); ~vtk2DPainter();
}; </source>
vtk2DPaintDevice (Abstract)
While the vtk2DPaintDevice class is an abstract class. The header for the equivalent functionality is,
<source lang="cpp"> class VTK_CHARTS_EXPORT vtk2DPaintDevice : public vtkObject { public:
vtkTypeRevisionMacro(vtk2DPaintDevice, vtkObject); virtual void PrintSelf(ostream &os, vtkIndent indent); static vtk2DPaintDevice *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:
vtk2DPaintDevice(); ~vtk2DPaintDevice();
}; </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 vtk2DPainter 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( vtk2DPainter* painter, 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 paint 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 painter 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>