Image Component Library (ICL)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Public Member Functions | Protected Member Functions | Private Attributes
icl::qt::LowLevelPlotWidget Class Reference

Default AbstractPlotWidget implementation for plotting different kinds of data. More...

#include <LowLevelPlotWidget.h>

Inheritance diagram for icl::qt::LowLevelPlotWidget:
icl::qt::AbstractPlotWidget icl::qt::ThreadedUpdatableWidget icl::utils::Configurable icl::qt::PlotWidget icl::qt::HistogrammWidget

List of all members.

Public Member Functions

 LowLevelPlotWidget (QWidget *parent=0)
 constructor
 ~LowLevelPlotWidget ()
 destructor
virtual utils::Rect32f getDataViewPort () const
 returns the data viewport
void addSeriesData (const float *data, int len, const AbstractPlotWidget::PenPtr &style=new AbstractPlotWidget::Pen(QColor(255, 0, 0)), const std::string &name="", int stride=1, bool deepCopy=true, bool passOwnerShip=false)
 adds series data
void addBarPlotData (const float *data, int len, const AbstractPlotWidget::PenPtr &style=new AbstractPlotWidget::Pen(QColor(255, 0, 0)), const std::string &name="", int stride=1, bool deepCopy=true, bool passOwnerShip=false)
 adds data for a bar plots
void addScatterData (char symbol, const float *xs, const float *ys, int num, const std::string &name="", int r=255, int g=0, int b=0, int size=5, bool connectingLine=false, int xStride=1, int yStride=1, bool filled=false, bool deepCopyData=true, bool passDataOwnerShip=false)
 adds a list of symbols
void clearScatterData ()
 clears the scatter data draw list
void clearSeriesData ()
 clears series data draw list
void clearBarPlotData ()
 clears the bar plot draw list
void clear ()
 clears all contained data

Protected Member Functions

virtual void drawLegend (QPainter &p, const utils::Rect &where, bool horizontal)
 internally used
virtual bool drawSeriesData (QPainter &p, const DrawState &state)
 draws the series data
virtual bool drawScatterData (QPainter &p, const DrawState &state)
 draws the sctter data
virtual bool drawBarPlotData (QPainter &p, const DrawState &state)
 draws the bar plot data
virtual bool drawData (QPainter &p)
 draws all the data
virtual utils::Range32f estimateDataXRange () const
 estimates the data xrange (for automatic viewport adaption)
virtual utils::Range32f estimateDataYRange () const
 estimates the data yrange (for automatic viewport adaption)

Private Attributes

Data * data
 pimpl

Detailed Description

Default AbstractPlotWidget implementation for plotting different kinds of data.

Why Low-Level ?

ICL's plotting framework does also provide a derived class called icl::PlotWidget. We strongly recommend to use the PlotWidget instead of the LowLevelPlotWidget be cause the PlotWidget provides a simpler interface and it's usage is more similar to the usage of ICL's image annotation framework (see ICLDrawWidget and ICLDrawWidget3D). However, in some special situations, the LowLevelPlotWidget provides a better performance, since it can e.g. visualize data that is just linked into it using a shallow copy. In contrast to the LowLevelPlotWidget, the PlotWidget does also provide template methods, that can be used to draw data of all POD types directly. In short: The LowLevelPlotWidget provides a low level interface for data visualization. It's functions are tuned for speed. Usually, the derived class icl::PlotWidget is simpler to use.

Data types

So far, the LowLevelPlotWidget can be used as scatter plot, bar-plot and as series-plot (graph-plot). All types can be used in parallel for overlayed visualization.

plot_widget_h.png

scatter plots

The scatter plot engine uses a set of data sets, that can be added successively. Each dataset is basically defined by a set of N (x,y) points, where the x- and y- coordinates are both taken from a data pointer that is read using a stride value. To add scatter data, the method LowLevelPlotWidget::addScatterData is provided. Scatter data is usually visualized by scattered colored symbols, and it can be visualized with- and without connecting lines between the points and if the shape that is described by the points is convex, it can also be filled automatically.

function data (or series data)

In order to draw function graphs, the X-viewport is used in slightly adapted way . Basically, a single function data-set is defined by a 'strided' float array of N elements. If a data-viewport is given to the inherited AbstractPlotWidget, the data X-values (which are not given) are assumed to be equally distributed withing the data-viewport's x-range. If no data viewport is provided, the X-range is automatically estimated to [0, N-1], which is the data index range.

_BAR_PLOTS_

Basically, bar plots are quite similar to function data/series plots. The viewport adaptions is managed indentically here. If several bar plots are used in one graph, the bars of each bin become smaller and the different bins are shown in an interleaved manner. As for function data, the longest bar plot row is used to determine the data x-range if no x-range is provided explicitly.

Please Note: If both, scatter- and function data is given, but not viewport information is available, it is not clear, whether to use the scatter-data X-range or the function data index-range. Therefore, visualizing scatter and function data at once is only supported if the data viewport is given.

Performance and bechmarks

The data interfaces are hold very low-level. The provide the option copy given data shallowly and deeply. However drawing the data needs some viewport calculations. Note, in the widget's right-button context menu, you can directly see the rendering time!

Here are some examples, to get an idea of the performance (using a 4-Core Intel(R) Xeon(R) CPU E5530 running at 2.40GHz and an optimized build)

  1. scatter data point rendering point rendering is internally accellerated since it turned out that the QPainter's drawPoint method works very slow for single points. Instead, the points are pre-buffered internally in order to be able to use the QPainter's drawPoints method, which is almost 10 times faster!
    • rendering 100.000 points (almost all visible) -> 12ms
    • zooming into the center of the data -> 7ms
    • zooming to somewhere where no datapoints are -> 5ms (please note, that the clipping mechanism, that avoid to draw invisible point works quite well)
    • using a dynamic viewport for 100.000 points (here, all are visible) -> 17ms
  1. scatter plot symbol rendering (here, size 5 crosses)
    • rendering 100.000 crosses (all visible) 30ms
    • zooming into the center of the data -> 12ms
    • zooming to somewhere where no datapoints are -> 5ms
    • additionally connecting all successive points with lines the lines -> 50ms
  1. drawing function/series data Here, x-clipping is implemented much more efficient: Due to the linear character of the x-coordinates, the visible X-interval of the function can be estimated directly. Therefore, only the visible part of the function data is processed and drawn at all.
    • rendering a sine-function with 100.000 entries (usually this does not make sense since about 100 Entries become a single point on the screen)
      • lines only -> 17ms
      • symbols only (dots) -> 17ms
      • lines and dots (redundant) -> 26ms
      • fill only with alpha value -> 65ms (rather slow, but actually much less samples are needed. Using less samples can easily be implemented by increasing the data stride)
      • using '+'-symbols instead of dots, lines off -> 26ms
    • using the same function, but a stride value of 100 (optically, there is no difference). In this case: with and without symbols, the rendering time is 1msec
      • lines only or in combination with any symbol: 1ms
      • fill only (with alpha value) -> 9ms
      • fill and lines -> 15ms

Examples

Please note, that the example application can be found at ICLQt/examples/plot-component-demo.cpp the binary name is icl-plot-component-demo.

Example 1

        LowLevelPlotWidget pw;
        static std::vector<float> sinData(100); // fill it
        pw.setPropertyValue("tics.y-distance",0.25);
        pw.setPropertyValue("enable fill",true);
        pw.addSeriesData(sinData.data(), sinData.size(), 
                         new AbstractPlotWidget::Pen(QColor(255,0,0),Qt::NoPen,' ',5, QColor(255,0,0,100)),
                         "sin(x)");
plot_widget_a.png

Second Example

        static std::vector<float> tanData(100); // fill it!
        pw.setPropertyValue("tics.y-distance",0.25);
        pw.addSeriesData(tanData.data(), tanData.size(), 
                         new AbstractPlotWidget::Pen(QColor(0,100,255),Qt::NoPen,' ',2, QColor(0,100,255,100)),
                         "tan(x)");
plot_widget_b.png

Multidimensional Output

        static std::vector<float> cosData(100); // fill it!
        pw.setPropertyValue("tics.y-distance",0.25);
        // we use symbols of radius 2, the 'o' selects circles
        pw.addSeriesData(cosData.data(), cosData.size(), 
                         new AbstractPlotWidget::Pen(QColor(0,255,0),QColor(0,255,0),'o',2, QColor(0,255,0,100)),
                         "cos(x)");
plot_widget_e.png

Series Data 4 (multiple functions)

        // use sin, cos and tan data from above
        pw.setPropertyValue("tics.y-distance",0.25);
        pw.addSeriesData(sinData.data(), sinData.size(), 
                         new AbstractPlotWidget::Pen(QColor(255,0,0)),
                         "sin(x)");
        pw.addSeriesData(cosData.data(), cosData.size(), 
                         new AbstractPlotWidget::Pen(QColor(0,255,0)),
                         "cos(x)");
        pw.addSeriesData(tanData.data(), tanData.size(), 
                         new AbstractPlotWidget::Pen(QColor(0,100,255)),
                         "tan(x)");
plot_widget_f.png

Scatter Data (one or two data sets)

        static std::vector<utils::Point32f> scatterData1(10000); // fill it!
        
        static std::vector<float> scatterData2(20000); // fill it!
        
        pw.setPropertyValue("tics.x-distance",3);
        pw.setPropertyValue("tics.y-distance",3);
        pw.addScatterData('.',&scatterData1[0].x,&scatterData1[0].y,scatterData1.size(), 
                             "some noise", 255, 0, 0, 2, false, 2, 2, false, false, false);
        
        pw.addScatterData('.',&scatterData2[0],&scatterData2[0]+10000,scatterData2.size()/2, 
                             "some other noise", 0, 100, 255, 2, false, 1, 1, false, false, false);
plot_widget_c.png

Connected Scatter Data and annotations

        static std::vector<utils::Point32f> scatterData3(1000);
        static utils::Time t = utils::Time::now();
        float dtSec = (utils::Time::now()-t).toSecondsDouble();
        for(unsigned int i=0;i<scatterData3.size();++i){
          float rel = float(i)/(scatterData3.size()-1) * 2 * M_PI;
          float r = 2 + 3 * sin(4*rel+dtSec);
          scatterData3[i].x  = r * cos(rel);
          scatterData3[i].y  = r * sin(rel);
        }
        // again, we use stride values of two for the interleaved data
        // this time, connectLines is true
        pw.addScatterData('.',&scatterData3[0].x,&scatterData3[0].y,scatterData3.size(), 
                             "some shape", 0, 100, 255, 2, true, 2, 2, true, false, false);
        
        pw.addAnnotations('r',FixedMatrix<float,1,4>(-.2,-.2,.4,.4).data() ,1,QColor(255,0,0), QColor(255,0,0,100));
        pw.addAnnotations('l',FixedMatrix<float,1,4>(0,0,3,3).data(),1,QColor(255,0,0));
        pw.addAnnotations('t',FixedMatrix<float,1,2>(3.f,3.f).data(),1,QColor(255,0,0),Qt::NoBrush,"the center");
plot_widget_g.png

Constructor & Destructor Documentation

constructor

destructor


Member Function Documentation

void icl::qt::LowLevelPlotWidget::addBarPlotData ( const float *  data,
int  len,
const AbstractPlotWidget::PenPtr style = new AbstractPlotWidget::Pen(QColor(255, 0, 0)),
const std::string &  name = "",
int  stride = 1,
bool  deepCopy = true,
bool  passOwnerShip = false 
)

adds data for a bar plots

Bar plots basically work like function/series data except for the fact, that bars are drawn instead of a (filled) function graph line

See also:
addSeriesData
void icl::qt::LowLevelPlotWidget::addScatterData ( char  symbol,
const float *  xs,
const float *  ys,
int  num,
const std::string &  name = "",
int  r = 255,
int  g = 0,
int  b = 0,
int  size = 5,
bool  connectingLine = false,
int  xStride = 1,
int  yStride = 1,
bool  filled = false,
bool  deepCopyData = true,
bool  passDataOwnerShip = false 
)

adds a list of symbols

Parameters:
symbolsymbol type
xsx-coordinate pointer (using xStride as stride)
ysy-coordinate pointer (using yStride as stride)
numnumber of symbols (i.e. xs and ys have xStride * num elements
name
rsymbol red component
gsymbol green component
bsymbol blue component
sizesymbol size (in screen pixels)
connectingLinedraw a line that connects successive symbols
xStridestride for xs-data (in units of sizeof(float), i.e. if the underlying data is interleaved (x,y)-data, the strides are 2 )
yStridestride for ys-data (in units of sizeof(float))
filleddefines whether the symbol must be filled (for triangle, circle, rect and diamond only)
deepCopyDataif set to true, xs, and ys are copied deeply otherwise, they are just linked (via pointer)
passDataOwnerShipif set to true, the plot widget will take the ownership of the given data
See also:
AbstractPlotWidget::Pen::Pen for allowed symbols
void icl::qt::LowLevelPlotWidget::addSeriesData ( const float *  data,
int  len,
const AbstractPlotWidget::PenPtr style = new AbstractPlotWidget::Pen(QColor(255, 0, 0)),
const std::string &  name = "",
int  stride = 1,
bool  deepCopy = true,
bool  passOwnerShip = false 
)

adds series data

Parameters:
datadata pointer
lennumber of elements in the data pointer
styledraw style
name
stridedata stride (in units of sizeof(float))
deepCopyif true, the data is copied into the drawer, otherwise, it is just linked. If the data is linked, it must remain valid until the seriesData is removed using LowLevelPlotWidget::clearSeriesData or LowLevelPlotWidget::clear.
passOwnerShipThis flag is only used if deepCopy is false. If passOwnerShip is true, the LowLevelPlotWidget will delete the data at deletion or when LowLevelPlotWidget::clearSeriesData or LowLevelPlotWidget::clear is called.

clears all contained data

Reimplemented from icl::qt::AbstractPlotWidget.

Reimplemented in icl::qt::PlotWidget.

clears the bar plot draw list

clears the scatter data draw list

clears series data draw list

virtual bool icl::qt::LowLevelPlotWidget::drawBarPlotData ( QPainter &  p,
const DrawState &  state 
) [protected, virtual]

draws the bar plot data

virtual bool icl::qt::LowLevelPlotWidget::drawData ( QPainter &  p) [protected, virtual]

draws all the data

Implements icl::qt::AbstractPlotWidget.

virtual void icl::qt::LowLevelPlotWidget::drawLegend ( QPainter &  p,
const utils::Rect where,
bool  horizontal 
) [protected, virtual]

internally used

draws the ledgend

Reimplemented from icl::qt::AbstractPlotWidget.

virtual bool icl::qt::LowLevelPlotWidget::drawScatterData ( QPainter &  p,
const DrawState &  state 
) [protected, virtual]

draws the sctter data

virtual bool icl::qt::LowLevelPlotWidget::drawSeriesData ( QPainter &  p,
const DrawState &  state 
) [protected, virtual]

draws the series data

virtual utils::Range32f icl::qt::LowLevelPlotWidget::estimateDataXRange ( ) const [protected, virtual]

estimates the data xrange (for automatic viewport adaption)

virtual utils::Range32f icl::qt::LowLevelPlotWidget::estimateDataYRange ( ) const [protected, virtual]

estimates the data yrange (for automatic viewport adaption)

returns the data viewport

If the data viewport is not given using AbstractPlotWidget::setDataViewPort

Reimplemented from icl::qt::AbstractPlotWidget.


Member Data Documentation

pimpl

pimpl pointer

Reimplemented from icl::qt::AbstractPlotWidget.


The documentation for this class was generated from the following file:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines