Skip to content
Snippets Groups Projects
qwt_plot_curve.cpp 29.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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
    {
    
        const int numSamples = static_cast<int>(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 = static_cast<int>(dataSize()) - 1;
    
    Bryant's avatar
    Bryant committed
                }
                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();