/*!
  Draw symbols

  \param painter Painter
  \param symbol Curve symbol
  \param xMap x map
  \param yMap y map
  \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

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

    const QRectF clipRect = qwtIntersectedClipRect( canvasRect, painter );
    mapper.setBoundingRect( clipRect );

    const int chunkSize = 500;

    for ( int i = from; i <= to; i += chunkSize )
    {
        const int n = qMin( chunkSize, to - i + 1 );

        const QPolygonF points = mapper.toPointsF( xMap, yMap,
            data(), i, i + n - 1 );

        if ( points.size() > 0 )
            symbol.drawSymbols( painter, points );
    }
}
/*!
  Draw dots

  \param painter Painter
  \param xMap x map
  \param yMap y map
  \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

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

    if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 )
    {
        return;
    }

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

    QwtPointMapper mapper;
    mapper.setBoundingRect( canvasRect );
    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );

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

    if ( doFill )
    {
        mapper.setFlag( QwtPointMapper::WeedOutPoints, false );

        QPolygonF points = mapper.toPointsF( 
            xMap, yMap, data(), from, to );

        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() );

        painter->drawImage( canvasRect.toAlignedRect(), image );
    }
    else if ( d_data->paintAttributes & MinimizeMemory )
    {
        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 );
            }

            QwtPainter::drawPoint( painter, QPointF( xi, yi ) );
        }
    }
    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 );

            QwtPainter::drawPoints( painter, points );
        }
    }
}
/*!
  \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
  \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

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

    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 );

    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.

    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

        if ( !doFit && !doFill )
            doIntegers = true; 
    }
#endif

    const bool noDuplicates = d_data->paintAttributes & FilterPoints;

    QwtPointMapper mapper;
    mapper.setFlag( QwtPointMapper::RoundPoints, doAlign );
    mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates );
    mapper.setBoundingRect( canvasRect );

    if ( doIntegers )
    {
        QPolygon polyline = mapper.toPolygon( 
            xMap, yMap, data(), from, to );

        if ( d_data->paintAttributes & ClipPolygons )
        {
            polyline = QwtClipper::clipPolygon( 
                clipRect.toAlignedRect(), polyline, false );
        }

        QwtPainter::drawPolyline( painter, polyline );
    }
    else
    {
        QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to );

        if ( doFit )
            polyline = d_data->curveFitter->fitCurve( polyline );

        if ( doFill )
        {
            if ( painter->pen().style() != Qt::NoPen )
            {
                // here we are wasting memory for the filled copy,
                // do polygon clipping twice etc .. TODO

                QPolygonF filled = polyline;
                fillCurve( painter, xMap, yMap, canvasRect, filled );
                filled.clear();

                if ( d_data->paintAttributes & ClipPolygons )
                {
                    polyline = QwtClipper::clipPolygonF( 
                        clipRect, polyline, false );
                }

                QwtPainter::drawPolyline( painter, polyline );
            }
            else
            {
                fillCurve( painter, xMap, yMap, canvasRect, polyline );
            }
        }
        else
        {
            if ( d_data->paintAttributes & ClipPolygons )
            {
                polyline = QwtClipper::clipPolygonF(
                    clipRect, polyline, false );
            }

            QwtPainter::drawPolyline( painter, polyline );
        }
    }
}
/*!
  \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
  \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

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

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

    QRectF clipRect;
    if ( d_data->paintAttributes & ClipPolygons )
    {
        clipRect = qwtIntersectedClipRect( canvasRect, painter );

        const qreal pw = QwtPainter::effectivePenWidth( painter->pen() );
        clipRect = clipRect.adjusted(-pw, -pw, pw, pw);
    }

    bool doIntegers = false;

#if QT_VERSION < 0x040800
    if ( painter->paintEngine()->type() == QPaintEngine::Raster )
    {

        // 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.

        // In case of filling or fitting performance doesn't count
        // because both operations are much more expensive
        // then drawing the polyline itself

        if ( !doFit && !doFill )
            doIntegers = true;
    }
#endif

    QwtPointMapper mapper;

    if ( doAlign )
    {
        mapper.setFlag( QwtPointMapper::RoundPoints, true );
        mapper.setFlag( QwtPointMapper::WeedOutIntermediatePoints,
            testPaintAttribute( FilterPointsAggressive ) );
    }

    mapper.setFlag( QwtPointMapper::WeedOutPoints,
        testPaintAttribute( FilterPoints ) ||
        testPaintAttribute( FilterPointsAggressive ) );

    mapper.setBoundingRect( canvasRect );

    if ( doIntegers )
    {
        QPolygon polyline = mapper.toPolygon(
            xMap, yMap, data(), from, to );

        if ( testPaintAttribute( ClipPolygons ) )
        {
            QwtClipper::clipPolygon( clipRect, polyline, false );
        }

        QwtPainter::drawPolyline( painter, polyline );
    }
    else
    {
        QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to );

        if ( doFill )
        {
            if ( doFit )
            {
                // it might be better to extend and draw the curvePath, but for
                // the moment we keep an implementation, where we translate the
                // path back to a polyline.

                polyline = d_data->curveFitter->fitCurve( polyline );
            }

            if ( painter->pen().style() != Qt::NoPen )
            {
                // here we are wasting memory for the filled copy,
                // do polygon clipping twice etc .. TODO

                QPolygonF filled = polyline;
                fillCurve( painter, xMap, yMap, canvasRect, filled );
                filled.clear();

                if ( d_data->paintAttributes & ClipPolygons )
                    QwtClipper::clipPolygonF( clipRect, polyline, false );

                QwtPainter::drawPolyline( painter, polyline );
            }
            else
            {
                fillCurve( painter, xMap, yMap, canvasRect, polyline );
            }
        }
        else
        {
            if ( testPaintAttribute( ClipPolygons ) )
            {
                QwtClipper::clipPolygonF( clipRect, polyline, false );
            }

            if ( doFit )
            {
                if ( d_data->curveFitter->mode() == QwtCurveFitter::Path )
                {
                    const QPainterPath curvePath =
                        d_data->curveFitter->fitCurvePath( polyline );

                    painter->drawPath( curvePath );
                }
                else
                {
                    polyline = d_data->curveFitter->fitCurve( polyline );
                    QwtPainter::drawPolyline( painter, polyline );
                }
            }
            else
            {
                QwtPainter::drawPolyline( painter, polyline );
            }
        }
    }
}