Skip to content
qwt_plot_curve.cpp 29.9 KiB
Newer Older
pixhawk's avatar
pixhawk committed
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

Bryant's avatar
Bryant committed
#include "qwt_plot_curve.h"
#include "qwt_point_data.h"
pixhawk's avatar
pixhawk committed
#include "qwt_math.h"
#include "qwt_clipper.h"
#include "qwt_painter.h"
Bryant's avatar
Bryant committed
#include "qwt_scale_map.h"
pixhawk's avatar
pixhawk committed
#include "qwt_plot.h"
#include "qwt_curve_fitter.h"
#include "qwt_symbol.h"
Bryant's avatar
Bryant committed
#include "qwt_point_mapper.h"
#include <qpainter.h>
#include <qpixmap.h>
#include <qalgorithms.h>
#include <qmath.h>
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
static void qwtUpdateLegendIconSize( QwtPlotCurve *curve )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( curve->symbol() && 
        curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) )
    {
        QSize sz = curve->symbol()->boundingRect().size();
        sz += QSize( 2, 2 ); // margin
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) )
        {
            // Avoid, that the line is completely covered by the symbol
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            int w = qCeil( 1.5 * sz.width() );
            if ( w % 2 )
                w++;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            sz.setWidth( qMax( 8, w ) );
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        curve->setLegendIconSize( sz );
pixhawk's avatar
pixhawk committed
    }
}

Bryant's avatar
Bryant committed
static int qwtVerifyRange( int size, int &i1, int &i2 )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( size < 1 )
pixhawk's avatar
pixhawk committed
        return 0;

Bryant's avatar
Bryant committed
    i1 = qBound( 0, i1, size - 1 );
    i2 = qBound( 0, i2, size - 1 );
pixhawk's avatar
pixhawk committed

    if ( i1 > i2 )
Bryant's avatar
Bryant committed
        qSwap( i1, i2 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    return ( i2 - i1 + 1 );
pixhawk's avatar
pixhawk committed
}

class QwtPlotCurve::PrivateData
{
public:
    PrivateData():
Bryant's avatar
Bryant committed
        style( QwtPlotCurve::Lines ),
        baseline( 0.0 ),
        symbol( NULL ),
        attributes( 0 ),
        paintAttributes( 
            QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ),
        legendAttributes( 0 )
    {
        pen = QPen( Qt::black );
pixhawk's avatar
pixhawk committed
        curveFitter = new QwtSplineCurveFitter;
    }

Bryant's avatar
Bryant committed
    ~PrivateData()
    {
pixhawk's avatar
pixhawk committed
        delete symbol;
        delete curveFitter;
    }

    QwtPlotCurve::CurveStyle style;
Bryant's avatar
Bryant committed
    double baseline;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const QwtSymbol *symbol;
pixhawk's avatar
pixhawk committed
    QwtCurveFitter *curveFitter;

    QPen pen;
    QBrush brush;

Bryant's avatar
Bryant committed
    QwtPlotCurve::CurveAttributes attributes;
    QwtPlotCurve::PaintAttributes paintAttributes;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtPlotCurve::LegendAttributes legendAttributes;
pixhawk's avatar
pixhawk committed
};

/*!
  Constructor
Bryant's avatar
Bryant committed
  \param title Title of the curve
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtPlotCurve::QwtPlotCurve( const QwtText &title ):
    QwtPlotSeriesItem( title )
pixhawk's avatar
pixhawk committed
{
    init();
}

/*!
  Constructor
Bryant's avatar
Bryant committed
  \param title Title of the curve
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtPlotCurve::QwtPlotCurve( const QString &title ):
    QwtPlotSeriesItem( QwtText( title ) )
pixhawk's avatar
pixhawk committed
{
    init();
}

//! Destructor
QwtPlotCurve::~QwtPlotCurve()
{
    delete d_data;
}

Bryant's avatar
Bryant committed
//! Initialize internal members
pixhawk's avatar
pixhawk committed
void QwtPlotCurve::init()
{
Bryant's avatar
Bryant committed
    setItemAttribute( QwtPlotItem::Legend );
    setItemAttribute( QwtPlotItem::AutoScale );
pixhawk's avatar
pixhawk committed

    d_data = new PrivateData;
Bryant's avatar
Bryant committed
    setData( new QwtPointSeriesData() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    setZ( 20.0 );
pixhawk's avatar
pixhawk committed
}

//! \return QwtPlotItem::Rtti_PlotCurve
int QwtPlotCurve::rtti() const
{
    return QwtPlotItem::Rtti_PlotCurve;
}

/*!
Bryant's avatar
Bryant committed
  Specify an attribute how to draw the curve
pixhawk's avatar
pixhawk committed

  \param attribute Paint attribute
  \param on On/Off
Bryant's avatar
Bryant committed
  \sa testPaintAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on )
pixhawk's avatar
pixhawk committed
{
    if ( on )
        d_data->paintAttributes |= attribute;
    else
        d_data->paintAttributes &= ~attribute;
}

/*!
Bryant's avatar
Bryant committed
    \return True, when attribute is enabled
    \sa setPaintAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return ( d_data->paintAttributes & attribute );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Specify an attribute how to draw the legend icon

  \param attribute Attribute
  \param on On/Off
  /sa testLegendAttribute(). legendIcon()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( on != testLegendAttribute( attribute ) )
    {
        if ( on )
            d_data->legendAttributes |= attribute;
        else
            d_data->legendAttributes &= ~attribute;

        qwtUpdateLegendIconSize( this );
        legendChanged();
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  \return True, when attribute is enabled
  \sa setLegendAttribute()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const
Bryant's avatar
Bryant committed
    return ( d_data->legendAttributes & attribute );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Set the curve's drawing style
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param style Curve style
  \sa style()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setStyle( CurveStyle style )
Bryant's avatar
Bryant committed
    if ( style != d_data->style )
    {
        d_data->style = style;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        legendChanged();
pixhawk's avatar
pixhawk committed
        itemChanged();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Style of the curve
  \sa setStyle()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
QwtPlotCurve::CurveStyle QwtPlotCurve::style() const
Bryant's avatar
Bryant committed
    return d_data->style;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Assign a symbol

  The curve will take the ownership of the symbol, hence the previously
  set symbol will be delete by setting a new one. If \p symbol is 
  \c NULL no symbol will be drawn.

  \param symbol Symbol
  \sa symbol()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setSymbol( QwtSymbol *symbol )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( symbol != d_data->symbol )
    {
        delete d_data->symbol;
        d_data->symbol = symbol;

        qwtUpdateLegendIconSize( this );

        legendChanged();
pixhawk's avatar
pixhawk committed
        itemChanged();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Current symbol or NULL, when no symbol has been assigned
  \sa setSymbol()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
const QwtSymbol *QwtPlotCurve::symbol() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->symbol;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Build and assign a pen
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
  non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
  to hide this incompatibility.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param color Pen color
  \param width Pen width
  \param style Pen style
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa pen(), brush()
 */
void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    setPen( QPen( color, width, style ) );
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Assign a pen
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param pen New pen
  \sa pen(), brush()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setPen( const QPen &pen )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( pen != d_data->pen )
    {
        d_data->pen = pen;

        legendChanged();
        itemChanged();
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Pen used to draw the lines
  \sa setPen(), brush()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
const QPen& QwtPlotCurve::pen() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->pen;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \brief Assign a brush.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
   In case of brush.style() != QBrush::NoBrush
   and style() != QwtPlotCurve::Sticks
   the area between the curve and the baseline will be filled.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
   In case !brush.color().isValid() the area will be filled by
   pen.color(). The fill algorithm simply connects the first and the
   last curve point to the baseline. So the curve data has to be sorted
   (ascending or descending).
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param brush New brush
  \sa brush(), setBaseline(), baseline()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setBrush( const QBrush &brush )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( brush != d_data->brush )
    {
        d_data->brush = brush;

        legendChanged();
        itemChanged();
    }
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  \return Brush used to fill the area between lines and the baseline
  \sa setBrush(), setBaseline(), baseline()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
const QBrush& QwtPlotCurve::brush() const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    return d_data->brush;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Draw an interval of the curve
pixhawk's avatar
pixhawk committed

  \param painter Painter
  \param xMap Maps x-values into pixel coordinates.
  \param yMap Maps y-values into pixel coordinates.
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from Index of the first point to be painted
  \param to Index of the last point to be painted. If to < 0 the
         curve will be painted to its last point.

Bryant's avatar
Bryant committed
  \sa drawCurve(), drawSymbols(),
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawSeries( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const size_t numSamples = dataSize();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( !painter || numSamples <= 0 )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    if ( to < 0 )
        to = numSamples - 1;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( qwtVerifyRange( numSamples, from, to ) > 0 )
pixhawk's avatar
pixhawk committed
    {
        painter->save();
Bryant's avatar
Bryant committed
        painter->setPen( d_data->pen );
pixhawk's avatar
pixhawk committed

        /*
          Qt 4.0.0 is slow when drawing lines, but it's even
pixhawk's avatar
pixhawk committed
          slower when the painter has a brush. So we don't
          set the brush before we really need it.
         */

Bryant's avatar
Bryant committed
        drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to );
pixhawk's avatar
pixhawk committed
        painter->restore();

Bryant's avatar
Bryant committed
        if ( d_data->symbol &&
            ( d_data->symbol->style() != QwtSymbol::NoSymbol ) )
        {
pixhawk's avatar
pixhawk committed
            painter->save();
Bryant's avatar
Bryant committed
            drawSymbols( painter, *d_data->symbol,
                xMap, yMap, canvasRect, from, to );
pixhawk's avatar
pixhawk committed
            painter->restore();
        }
    }
}

/*!
  \brief Draw the line part (without symbols) of a curve interval.
pixhawk's avatar
pixhawk committed
  \param painter Painter
  \param style curve style, see QwtPlotCurve::CurveStyle
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from index of the first point to be painted
  \param to index of the last point to be painted
  \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawCurve( QPainter *painter, int style,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    switch ( style )
    {
        case Lines:
            if ( testCurveAttribute( Fitted ) )
            {
                // we always need the complete
                // curve for fitting
                from = 0;
                to = dataSize() - 1;
            }
            drawLines( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Sticks:
            drawSticks( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Steps:
            drawSteps( painter, xMap, yMap, canvasRect, from, to );
            break;
        case Dots:
            drawDots( painter, xMap, yMap, canvasRect, from, to );
            break;
        case NoCurve:
        default:
            break;
pixhawk's avatar
pixhawk committed
    }
}

/*!
  \brief Draw lines

  If the CurveAttribute Fitted is enabled a QwtCurveFitter tries
  to interpolate/smooth the curve, before it is painted.

  \param painter Painter
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from index of the first point to be painted
  \param to index of the last point to be painted

  \sa setCurveAttribute(), setCurveFitter(), draw(),
pixhawk's avatar
pixhawk committed
      drawLines(), drawDots(), drawSteps(), drawSticks()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawLines( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( from > to )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    const bool doAlign = QwtPainter::roundingAlignment( painter );
    const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter;
    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
            && ( d_data->brush.color().alpha() > 0 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QRectF clipRect;
    if ( d_data->paintAttributes & ClipPolygons )
    {
        qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
        clipRect = canvasRect.adjusted(-pw, -pw, pw, pw);
    }

    bool doIntegers = false;

#if QT_VERSION < 0x040800

    // For Qt <= 4.7 the raster paint engine is significantly faster
    // for rendering QPolygon than for QPolygonF. So let's
    // see if we can use it.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( painter->paintEngine()->type() == QPaintEngine::Raster )
    {
        // In case of filling or fitting performance doesn't count
        // because both operations are much more expensive
        // then drawing the polyline itself
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( !doFit && !doFill )
            doIntegers = true; 
    }
#endif
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const bool noDuplicates = d_data->paintAttributes & FilterPoints;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtPointMapper mapper;
    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
    mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
    mapper.setBoundingRect( canvasRect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( doIntegers )
    {
        const QPolygon polyline = mapper.toPolygon( 
            xMap, yMap, data(), from, to );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( d_data->paintAttributes & ClipPolygons )
        {
            const QPolygon clipped = QwtClipper::clipPolygon( 
                clipRect.toAlignedRect(), polyline, false );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            QwtPainter::drawPolyline( painter, clipped );
pixhawk's avatar
pixhawk committed
        }
Bryant's avatar
Bryant committed
        else
        {
            QwtPainter::drawPolyline( painter, polyline );
pixhawk's avatar
pixhawk committed
        }
    }
Bryant's avatar
Bryant committed
    else
    {
        QPolygonF polyline = mapper.toPolygonF( xMap, yMap,
            data(), from, to );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( doFit )
            polyline = d_data->curveFitter->fitCurve( polyline );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( d_data->paintAttributes & ClipPolygons )
        {
            const QPolygonF clipped = QwtClipper::clipPolygonF( 
                clipRect, polyline, false );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            QwtPainter::drawPolyline( painter, clipped );
        }
        else
        {
            QwtPainter::drawPolyline( painter, polyline );
        }

        if ( doFill )
        {
            fillCurve( painter, xMap, yMap, canvasRect, polyline );
        }
    }
pixhawk's avatar
pixhawk committed
}

/*!
  Draw sticks

  \param painter Painter
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from index of the first point to be painted
  \param to index of the last point to be painted

  \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawSticks( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    painter->save();
    painter->setRenderHint( QPainter::Antialiasing, false );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const bool doAlign = QwtPainter::roundingAlignment( painter );

    double x0 = xMap.transform( d_data->baseline );
    double y0 = yMap.transform( d_data->baseline );
    if ( doAlign )
    {
        x0 = qRound( x0 );
        y0 = qRound( y0 );
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const Qt::Orientation o = orientation();

    const QwtSeriesData<QPointF> *series = data();

    for ( int i = from; i <= to; i++ )
    {
        const QPointF sample = series->sample( i );
        double xi = xMap.transform( sample.x() );
        double yi = yMap.transform( sample.y() );
        if ( doAlign )
        {
            xi = qRound( xi );
            yi = qRound( yi );
        }

        if ( o == Qt::Horizontal )
            QwtPainter::drawLine( painter, x0, yi, xi, yi );
pixhawk's avatar
pixhawk committed
        else
Bryant's avatar
Bryant committed
            QwtPainter::drawLine( painter, xi, y0, xi, yi );
pixhawk's avatar
pixhawk committed
    }
Bryant's avatar
Bryant committed

    painter->restore();
pixhawk's avatar
pixhawk committed
}

/*!
  Draw dots

  \param painter Painter
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from index of the first point to be painted
  \param to index of the last point to be painted

  \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawDots( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const QColor color = painter->pen().color();

    if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
    {
pixhawk's avatar
pixhawk committed
        return;
Bryant's avatar
Bryant committed
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const bool doFill = ( d_data->brush.style() != Qt::NoBrush )
            && ( d_data->brush.color().alpha() > 0 );
    const bool doAlign = QwtPainter::roundingAlignment( painter );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtPointMapper mapper;
    mapper.setBoundingRect( canvasRect );
    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( d_data->paintAttributes & FilterPoints )
    {
        if ( ( color.alpha() == 255 )
            && !( painter->renderHints() & QPainter::Antialiasing ) )
        {
            mapper.setFlag( QwtPointMapper::WeedOutPoints, true );
        }
    }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    if ( doFill )
    {
        mapper.setFlag( QwtPointMapper::WeedOutPoints, false );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        QPolygonF points = mapper.toPointsF( 
            xMap, yMap, data(), from, to );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        QwtPainter::drawPoints( painter, points );
        fillCurve( painter, xMap, yMap, canvasRect, points );
    }
    else if ( d_data->paintAttributes & ImageBuffer )
    {
        const QImage image = mapper.toImage( xMap, yMap,
            data(), from, to, d_data->pen, 
            painter->testRenderHint( QPainter::Antialiasing ),
            renderThreadCount() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        painter->drawImage( canvasRect.toAlignedRect(), image );
    }
    else if ( d_data->paintAttributes & MinimizeMemory )
    {
        const QwtSeriesData<QPointF> *series = data();
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        for ( int i = from; i <= to; i++ )
        {
            const QPointF sample = series->sample( i );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            double xi = xMap.transform( sample.x() );
            double yi = yMap.transform( sample.y() );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            if ( doAlign )
            {
                xi = qRound( xi );
                yi = qRound( yi );
pixhawk's avatar
pixhawk committed
            }
Bryant's avatar
Bryant committed

            QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
pixhawk's avatar
pixhawk committed
        }
    }
Bryant's avatar
Bryant committed
    else
    {
        if ( doAlign )
        {
            const QPolygon points = mapper.toPoints(
                xMap, yMap, data(), from, to ); 

            QwtPainter::drawPoints( painter, points );
        }
        else
        {
            const QPolygonF points = mapper.toPointsF( 
                xMap, yMap, data(), from, to );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
            QwtPainter::drawPoints( painter, points );
        }
pixhawk's avatar
pixhawk committed
    }
}

/*!
  Draw step function

  The direction of the steps depends on Inverted attribute.
pixhawk's avatar
pixhawk committed

  \param painter Painter
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
pixhawk's avatar
pixhawk committed
  \param from index of the first point to be painted
  \param to index of the last point to be painted

  \sa CurveAttribute, setCurveAttribute(),
      draw(), drawCurve(), drawDots(), drawLines(), drawSticks()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawSteps( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const bool doAlign = QwtPainter::roundingAlignment( painter );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QPolygonF polygon( 2 * ( to - from ) + 1 );
    QPointF *points = polygon.data();

    bool inverted = orientation() == Qt::Vertical;
pixhawk's avatar
pixhawk committed
    if ( d_data->attributes & Inverted )
        inverted = !inverted;

Bryant's avatar
Bryant committed
    const QwtSeriesData<QPointF> *series = data();

    int i, ip;
    for ( i = from, ip = 0; i <= to; i++, ip += 2 )
    {
        const QPointF sample = series->sample( i );
        double xi = xMap.transform( sample.x() );
        double yi = yMap.transform( sample.y() );
        if ( doAlign )
        {
            xi = qRound( xi );
            yi = qRound( yi );
        }
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( ip > 0 )
        {
            const QPointF &p0 = points[ip - 2];
            QPointF &p = points[ip - 1];

            if ( inverted )
            {
                p.rx() = p0.x();
                p.ry() = yi;
            }
pixhawk's avatar
pixhawk committed
            else
Bryant's avatar
Bryant committed
            {
                p.rx() = xi;
                p.ry() = p0.y();
            }
pixhawk's avatar
pixhawk committed
        }

Bryant's avatar
Bryant committed
        points[ip].rx() = xi;
        points[ip].ry() = yi;
pixhawk's avatar
pixhawk committed
    }

Bryant's avatar
Bryant committed
    if ( d_data->paintAttributes & ClipPolygons )
    {
        const QPolygonF clipped = QwtClipper::clipPolygonF( 
            canvasRect, polygon, false );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        QwtPainter::drawPolyline( painter, clipped );
    }
    else
    {
        QwtPainter::drawPolyline( painter, polygon );
    }
pixhawk's avatar
pixhawk committed

    if ( d_data->brush.style() != Qt::NoBrush )
Bryant's avatar
Bryant committed
        fillCurve( painter, xMap, yMap, canvasRect, polygon );
pixhawk's avatar
pixhawk committed
}


/*!
Bryant's avatar
Bryant committed
  Specify an attribute for drawing the curve
pixhawk's avatar
pixhawk committed

  \param attribute Curve attribute
  \param on On/Off

  /sa testCurveAttribute(), setCurveFitter()
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( bool( d_data->attributes & attribute ) == on )
pixhawk's avatar
pixhawk committed
        return;

    if ( on )
        d_data->attributes |= attribute;
    else
        d_data->attributes &= ~attribute;

    itemChanged();
}

/*!
    \return true, if attribute is enabled
    \sa setCurveAttribute()
*/
Bryant's avatar
Bryant committed
bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const
pixhawk's avatar
pixhawk committed
    return d_data->attributes & attribute;
}

/*!
Bryant's avatar
Bryant committed
  Assign a curve fitter
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The curve fitter "smooths" the curve points, when the Fitted
  CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  The curve fitter operates on the translated points ( = widget coordinates)
  to be functional for logarithmic scales. Obviously this is less performant
  for fitting algorithms, that reduce the number of points.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  For situations, where curve fitting is used to improve the performance
  of painting huge series of points it might be better to execute the fitter
  on the curve points once and to cache the result in the QwtSeriesData object.
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \param curveFitter() Curve fitter
  \sa Fitted
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter )
pixhawk's avatar
pixhawk committed
{
    delete d_data->curveFitter;
    d_data->curveFitter = curveFitter;

    itemChanged();
}

Bryant's avatar
Bryant committed
/*!
  Get the curve fitter. If curve fitting is disabled NULL is returned.

  \return Curve fitter
  \sa setCurveFitter(), Fitted
*/
pixhawk's avatar
pixhawk committed
QwtCurveFitter *QwtPlotCurve::curveFitter() const
{
    return d_data->curveFitter;
}

/*!
  Fill the area between the curve and the baseline with
pixhawk's avatar
pixhawk committed
  the curve brush

  \param painter Painter
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
  \param polygon Polygon - will be modified !
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa setBrush(), setBaseline(), setStyle()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::fillCurve( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, QPolygonF &polygon ) const
pixhawk's avatar
pixhawk committed
{
    if ( d_data->brush.style() == Qt::NoBrush )
        return;

Bryant's avatar
Bryant committed
    closePolyline( painter, xMap, yMap, polygon );
    if ( polygon.count() <= 2 ) // a line can't be filled
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    QBrush brush = d_data->brush;
    if ( !brush.color().isValid() )
        brush.setColor( d_data->pen.color() );

    if ( d_data->paintAttributes & ClipPolygons )
        polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true );
pixhawk's avatar
pixhawk committed

    painter->save();

Bryant's avatar
Bryant committed
    painter->setPen( Qt::NoPen );
    painter->setBrush( brush );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    QwtPainter::drawPolygon( painter, polygon );
pixhawk's avatar
pixhawk committed

    painter->restore();
}

/*!
Bryant's avatar
Bryant committed
  \brief Complete a polygon to be a closed polygon including the 
         area between the original polygon and the baseline.

  \param painter Painter
pixhawk's avatar
pixhawk committed
  \param xMap X map
  \param yMap Y map
Bryant's avatar
Bryant committed
  \param polygon Polygon to be completed
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::closePolyline( QPainter *painter,
pixhawk's avatar
pixhawk committed
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
Bryant's avatar
Bryant committed
    QPolygonF &polygon ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( polygon.size() < 2 )
pixhawk's avatar
pixhawk committed
        return;

Bryant's avatar
Bryant committed
    const bool doAlign = QwtPainter::roundingAlignment( painter );

    double baseline = d_data->baseline;
    
    if ( orientation() == Qt::Vertical )
    {
        if ( yMap.transformation() )
            baseline = yMap.transformation()->bounded( baseline );

        double refY = yMap.transform( baseline );
        if ( doAlign )
            refY = qRound( refY );

        polygon += QPointF( polygon.last().x(), refY );
        polygon += QPointF( polygon.first().x(), refY );
    }
    else
    {
        if ( xMap.transformation() )
            baseline = xMap.transformation()->bounded( baseline );

        double refX = xMap.transform( baseline );
        if ( doAlign )
            refX = qRound( refX );

        polygon += QPointF( refX, polygon.last().y() );
        polygon += QPointF( refX, polygon.first().y() );
pixhawk's avatar
pixhawk committed
    }
}

/*!
Bryant's avatar
Bryant committed
  Draw symbols

pixhawk's avatar
pixhawk committed
  \param painter Painter
  \param symbol Curve symbol
  \param xMap x map
  \param yMap y map
Bryant's avatar
Bryant committed
  \param canvasRect Contents rectangle of the canvas
  \param from Index of the first point to be painted
  \param to Index of the last point to be painted
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
  \sa setSymbol(), drawSeries(), drawCurve()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect, int from, int to ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    QwtPointMapper mapper;
    mapper.setFlag( QwtPointMapper::RoundPoints, 
        QwtPainter::roundingAlignment( painter ) );
    mapper.setFlag( QwtPointMapper::WeedOutPoints, 
        testPaintAttribute( QwtPlotCurve::FilterPoints ) );
    mapper.setBoundingRect( canvasRect );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    const int chunkSize = 500;
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
    for ( int i = from; i <= to; i += chunkSize )
    {
        const int n = qMin( chunkSize, to - i + 1 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        const QPolygonF points = mapper.toPointsF( xMap, yMap,
            data(), i, i + n - 1 );
pixhawk's avatar
pixhawk committed

Bryant's avatar
Bryant committed
        if ( points.size() > 0 )
            symbol.drawSymbols( painter, points );
pixhawk's avatar
pixhawk committed
    }
}

/*!
  \brief Set the value of the baseline

  The baseline is needed for filling the curve with a brush or
  the Sticks drawing style.
Bryant's avatar
Bryant committed

  The interpretation of the baseline depends on the orientation().
  With Qt::Horizontal, the baseline is interpreted as a horizontal line
  at y = baseline(), with Qt::Vertical, it is interpreted as a vertical
  line at x = baseline().

  The default value is 0.0.

  \param value Value of the baseline
  \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation()
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
void QwtPlotCurve::setBaseline( double value )
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    if ( d_data->baseline != value )
    {
        d_data->baseline = value;
pixhawk's avatar
pixhawk committed
        itemChanged();
    }
}

/*!
Bryant's avatar
Bryant committed
  \return Value of the baseline
  \sa setBaseline()
pixhawk's avatar
pixhawk committed
*/
double QwtPlotCurve::baseline() const
{
Bryant's avatar
Bryant committed
    return d_data->baseline;
pixhawk's avatar
pixhawk committed
}

/*!
Bryant's avatar
Bryant committed
  Find the closest curve point for a specific position

  \param pos Position, where to look for the closest curve point
  \param dist If dist != NULL, closestPoint() returns the distance between
              the position and the closest curve point
  \return Index of the closest curve point, or -1 if none can be found
          ( f.e when the curve has no points )
  \note closestPoint() implements a dumb algorithm, that iterates
        over all points
pixhawk's avatar
pixhawk committed
*/
Bryant's avatar
Bryant committed
int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const
pixhawk's avatar
pixhawk committed
{
Bryant's avatar
Bryant committed
    const size_t numSamples = dataSize();