Example #1
0
// this is the rect of the top surface of the pie, i.e. excluding the "3D" rim effect.
QRectF PieDiagram::twoDPieRect( const QRectF &contentsRect, const ThreeDPieAttributes& threeDAttrs ) const
{
    QRectF pieRect;
    if ( !threeDAttrs.isEnabled() ) {
        qreal x = ( contentsRect.width() - d->size ) / 2.0;
        qreal y = ( contentsRect.height() - d->size ) / 2.0;
        pieRect = QRectF( contentsRect.left() + x, contentsRect.top() + y, d->size, d->size );
    } else {
        // threeD: width is the maximum possible width; height is 1/2 of that
        qreal sizeFor3DEffect = 0.0;

        qreal x = ( contentsRect.width() - d->size ) / 2.0;
        qreal height = d->size;
        // make sure that the height plus the threeDheight is not more than the
        // available size
        if ( threeDAttrs.depth() >= 0.0 ) {
            // positive pie height: absolute value
            sizeFor3DEffect = threeDAttrs.depth();
            height = d->size - sizeFor3DEffect;
        } else {
            // negative pie height: relative value
            sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height;
            height = d->size - sizeFor3DEffect;
        }
        qreal y = ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0;

        pieRect = QRectF( contentsRect.left() + x, contentsRect.top() + y, d->size, height );
    }
    return pieRect;
}
bool ThreeDPieAttributes::operator==( const ThreeDPieAttributes& r ) const
{
    return ( useShadowColors() == r.useShadowColors() &&
             AbstractThreeDAttributes::operator==(r));
}
/**
  Internal method that draws the shadow creating the 3D effect of a pie

  \param painter the QPainter to draw in
  \param drawPosition the position to draw at
  \param dataset the dataset to draw the pie for
  \param pie the pie to draw the shadow for
  \param threeDHeight the height of the shadow
  */
void PieDiagram::draw3DEffect( QPainter* painter,
        const QRectF& drawPosition,
        uint dataset, uint pie,
        qreal granularity,
        const ThreeDPieAttributes& threeDAttrs,
        bool /*explode*/ )
{
    Q_UNUSED( dataset );

    if( ! threeDAttrs.isEnabled() )
        return;

    // NOTE: We cannot optimize away drawing some of the effects (even
    // when not exploding), because some of the pies might be left out
    // in future versions which would make some of the normally hidden
    // pies visible. Complex hidden-line algorithms would be much more
    // expensive than just drawing for nothing.

    // No need to save the brush, will be changed on return from this
    // method anyway.
    const QBrush brush = this->brush( model()->index( 0, pie, rootIndex() ) );
    if( threeDAttrs.useShadowColors() ){        
        painter->setBrush( QBrush( brush.color().darker() ) );
    }
    else{
        painter->setBrush( brush );
    }
    //painter->setBrush( QBrush( threeDAttrs.dataShadow1Color( pie ),
    //            params()->shadowPattern() ) );

    qreal startAngle = d->startAngles[ pie ];
    qreal endAngle = startAngle + d->angleLens[ pie ];
    // Normalize angles
    while ( startAngle >= 360 )
        startAngle -= 360;
    while ( endAngle >= 360 )
        endAngle -= 360;
    Q_ASSERT( startAngle >= 0 && startAngle <= 360 );
    Q_ASSERT( endAngle >= 0 && endAngle <= 360 );

    //int centerY = drawPosition.center().y();

    if ( startAngle == endAngle ||
            startAngle == endAngle - 360 ) { // full circle
        drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, 360, granularity );
    } else if ( startAngle <= 90 ) {
        if ( endAngle <= 90 ) {
            if ( startAngle <= endAngle ) {
                /// starts and ends in first quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), startAngle );
                drawUpperBrinkEffect( painter, drawPosition, endAngle );
            } else {
                /// starts and ends in first quadrant, more than 3/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), startAngle );
                drawUpperBrinkEffect( painter, drawPosition, endAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    180, 360, granularity );
            }
        } else if ( endAngle <= 180 ) {
            /// starts in first quadrant, ends in second quadrant,
            /// less than 1/2
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
        } else if ( endAngle <= 270 ) {
            /// starts in first quadrant, ends in third quadrant
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, endAngle, granularity );
        } else { // 270*16 < endAngle < 360*16
            /// starts in first quadrant, ends in fourth quadrant,
            /// more than 3/4
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, endAngle, granularity );
        }
    } else if ( startAngle <= 180 ) {
        if ( endAngle <= 90 ) {
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, 360, granularity );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
        } else if ( endAngle <= 180 ) {
            if ( startAngle <= endAngle ) {
                /// starts in second quadrant, ends in second
                /// quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), endAngle );
                drawUpperBrinkEffect( painter, drawPosition, startAngle );
            } else {
                /// starts in second quadrant, ends in second
                /// quadrant, more than 1/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), endAngle );
                drawUpperBrinkEffect( painter, drawPosition, startAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    180, 360, granularity );
            }
        } else if ( endAngle <= 270 ) {
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, endAngle, granularity );
        } else { // 270*16 < endAngle < 360*16
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, endAngle, granularity );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
        }
    } else if ( startAngle <= 270 ) {
        if ( endAngle <= 90 ) {
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, 360, granularity );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
        } else if ( endAngle <= 180 ) {
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, 360, granularity );
        } else if ( endAngle <= 270 ) {
            if ( startAngle <= endAngle ) {
                /// starts in third quadrant, ends in third quadrant,
                /// less than 1/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), endAngle );
                drawUpperBrinkEffect( painter, drawPosition, startAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    startAngle, endAngle, granularity );
            } else {
                /// starts in third quadrant, ends in third quadrant,
                /// more than 3/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), endAngle );
                drawUpperBrinkEffect( painter, drawPosition, startAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    180, endAngle, granularity );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    startAngle, 360, granularity );
            }
        } else { // 270*16 < endAngle < 360*16
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, endAngle, granularity );
            drawUpperBrinkEffect( painter, drawPosition, startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
        }
    } else { // 270*16 < startAngle < 360*16
        if ( endAngle <= 90 ) {
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawUpperBrinkEffect( painter, drawPosition, endAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, 360, granularity );
        } else if ( endAngle <= 180 ) {
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, 360, granularity );
        } else if ( endAngle <= 270 ) {
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), startAngle );
            drawStraightEffectSegment( painter, drawPosition,
                threeDAttrs.depth(), endAngle );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                180, endAngle, granularity );
            drawArcEffectSegment( painter, drawPosition,
                threeDAttrs.depth(),
                startAngle, 360, granularity );
        } else { // 270*16 < endAngle < 360*16
            if ( startAngle <= endAngle ) {
                /// starts in fourth quadrant, ends in fourth
                /// quadrant, less than 1/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), startAngle );
                drawUpperBrinkEffect( painter, drawPosition, endAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    startAngle, endAngle, granularity );
            } else {
                /// starts in fourth quadrant, ends in fourth
                /// quadrant, more than 3/4
                drawStraightEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(), startAngle );
                drawUpperBrinkEffect( painter, drawPosition, endAngle );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    startAngle, 360, granularity );
                drawArcEffectSegment( painter, drawPosition,
                    threeDAttrs.depth(),
                    180, endAngle, granularity );
            }
        }
    }
    drawArcUpperBrinkEffectSegment( painter, drawPosition, startAngle, endAngle, granularity );
}
void PieDiagram::paintInternal(PaintContext* ctx, QRectF& textBoundingRect)
{
    // note: Not having any data model assigned is no bug
    //       but we can not draw a diagram then either.
    if ( !checkInvariants(true) )
        return;

    d->reverseMapper.clear();

    const PieAttributes attrs( pieAttributes() );
    const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( model()->index( 0, 0, rootIndex() ) ) );

    const int colCount = columnCount();

    QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) );
    contentsRect = ctx->rectangle();
//    contentsRect = geometry();
//qDebug() << contentsRect;
    if( contentsRect.isEmpty() )
        return;

    DataValueTextInfoList list;
    const qreal sum = valueTotals();

    if( sum == 0.0 ) //nothing to draw
        return;

    d->startAngles.resize( colCount );
    d->angleLens.resize( colCount );

    // compute position
    d->size = qMin( contentsRect.width(), contentsRect.height() ); // initial size

    // if the pies explode, we need to give them additional space =>
    // make the basic size smaller
    qreal maxExplode = 0.0;
    for( int j = 0; j < colCount; ++j ){
        const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
        maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
    }
    d->size /= ( 1.0 + 1.0 * maxExplode );

    if(d->size < 0.0)
        d->size = 0;

    if(!textBoundingRect.isEmpty() && d->size > 0.0)
    {
        // Find out the distances from every corner of the rectangle with
        // the center.
        QPointF center = ctx->rectangle().center();
        qreal left = qMax(qreal(0.0), center.x() - textBoundingRect.left());
        qreal right = qMax(qreal(0.0), textBoundingRect.right() - center.x());
        qreal top = qMax(qreal(0.0), center.y() - textBoundingRect.top());
        qreal bottom = qMax(qreal(0.0), textBoundingRect.bottom() - center.y());
        // Compute the minimal and maximal distances for horizontal vs vertical
        // the text has.
        qreal xDistanceMax, xDistanceMin, yDistanceMax, yDistanceMin;
        if ( left > right ) {
            xDistanceMax = left;
            xDistanceMin = right;
        } else {
            xDistanceMax = right;
            xDistanceMin = left;
        }
        if ( top > bottom ) {
            yDistanceMax = top;
            yDistanceMin = bottom;
        } else {
            yDistanceMax = bottom;
            yDistanceMin = top;
        }
        // Above we are dealing with the distance from the center what means
        // below we need to make sure to use the half d->size while working
        // with those values.
        qreal availableDistance = d->size/2.0;
        // Now first check what size (if any) the text needs more in any
        // of the corners then what is available for the pie-chart.
        // The resulting diff value is not any longer only related to the
        // distance (d->size/2 cause of calculation from the center) but
        // is now in relation to the whole d->size.
        qreal diff;
        if ( xDistanceMax + xDistanceMin > yDistanceMax + yDistanceMin ) {
            diff = qMax(qreal(0.0), xDistanceMax - availableDistance) + qMax(qreal(0.0), xDistanceMin - availableDistance);
        } else {
            diff = qMax(qreal(0.0), yDistanceMax - availableDistance) + qMax(qreal(0.0), yDistanceMin - availableDistance);
        }
        if(diff > 0.0) {
            // If that is the case then we need to shrink the size available for the
            // pie-chart by the additional space needed for the text. Those space
            // removed from the pie-chart will then be used by the text and makes
            // sure that the text fits into the contentsRect and is not drawn outside.
            d->size -= qMin(d->size, diff);
        }
    }

    qreal sizeFor3DEffect = 0.0;
    if ( ! threeDAttrs.isEnabled() ) {

        qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
        qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 );
        d->position = QRectF( x, y, d->size, d->size );
        d->position.translate( contentsRect.left(), contentsRect.top() );
    } else {
        // threeD: width is the maximum possible width; height is 1/2 of that
        qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
        qreal height = d->size;
        // make sure that the height plus the threeDheight is not more than the
        // available size
        if ( threeDAttrs.depth() >= 0.0 ) {
            // positive pie height: absolute value
            sizeFor3DEffect = threeDAttrs.depth();
            height = d->size - sizeFor3DEffect;
        } else {
            // negative pie height: relative value
            sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height;
            height = d->size - sizeFor3DEffect;
        }
        qreal y = ( contentsRect.height() == height ) ? 0.0 : ( ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0 );

        d->position = QRectF( contentsRect.left() + x, contentsRect.top() + y,
                d->size, height );
        //  d->position.moveBy( contentsRect.left(), contentsRect.top() );
    }

    const PolarCoordinatePlane * plane = polarCoordinatePlane();
    const qreal sectorsPerValue = 360.0 / sum;
    qreal currentValue = plane ? plane->startPosition() : 0.0;

    bool atLeastOneValue = false; // guard against completely empty tables
    QVariant vValY;
    for ( int iColumn = 0; iColumn < colCount; ++iColumn ) {
        // is there anything at all at this column?
        bool bOK;
        const double cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) )
            .toDouble( &bOK ) );

        if( bOK ){
            d->startAngles[ iColumn ] = currentValue;
            d->angleLens[ iColumn ] = cellValue * sectorsPerValue;
            atLeastOneValue = true;
        } else { // mark as non-existent
            d->angleLens[ iColumn ] = 0.0;
            if ( iColumn > 0.0 )
                d->startAngles[ iColumn ] = d->startAngles[ iColumn - 1 ];
            else
                d->startAngles[ iColumn ] = currentValue;
        }
        //qDebug() << "d->startAngles["<<iColumn<<"] == " << d->startAngles[ iColumn ]
        //         << " +  d->angleLens["<<iColumn<<"]" << d->angleLens[ iColumn ]
        //         << " = " << d->startAngles[ iColumn ]+d->angleLens[ iColumn ];

        currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ];
    }

    // If there was no value at all, bail out, to avoid endless loops
    // later on (e.g. in findPieAt()).
    if( ! atLeastOneValue )
        return;


    // Find the backmost pie which is at +90° and needs to be drawn
    // first
    int backmostpie = findPieAt( 90, colCount );
    // Find the frontmost pie (at -90°/+270°) that should be drawn last
    int frontmostpie = findPieAt( 270, colCount );
    // the right- and the leftmost (only needed in some special cases...)
    int rightmostpie = findPieAt( 0, colCount );
    int leftmostpie = findPieAt( 180, colCount );


    int currentLeftPie = backmostpie;
    int currentRightPie = backmostpie;

    d->clearListOfAlreadyDrawnDataValueTexts();

    drawOnePie( ctx->painter(), &list, 0, backmostpie, granularity(), sizeFor3DEffect );

    if( backmostpie == frontmostpie )
    {
        if( backmostpie == leftmostpie )
            currentLeftPie = findLeftPie( currentLeftPie, colCount );
        if( backmostpie == rightmostpie )
            currentRightPie = findRightPie( currentRightPie, colCount );
    }
    while( currentLeftPie != frontmostpie )
    {
        if( currentLeftPie != backmostpie )
            drawOnePie( ctx->painter(), &list, 0, currentLeftPie, granularity(), sizeFor3DEffect );
        currentLeftPie = findLeftPie( currentLeftPie, colCount );
    }
    while( currentRightPie != frontmostpie )
    {
        if( currentRightPie != backmostpie )
            drawOnePie( ctx->painter(), &list, 0, currentRightPie, granularity(), sizeFor3DEffect );
        currentRightPie = findRightPie( currentRightPie, colCount );
    }

    // if the backmost pie is not the frontmost pie, we draw the frontmost at last
    if( backmostpie != frontmostpie || ! threeDPieAttributes().isEnabled() )
    {
        drawOnePie( ctx->painter(), &list, 0, frontmostpie, granularity(), sizeFor3DEffect );
    // otherwise, this gets a bit more complicated...
/*    } else if( threeDPieAttributes().isEnabled() ) {
        //drawPieSurface( ctx->painter(), 0, frontmostpie, granularity() );
        const QModelIndex index = model()->index( 0, frontmostpie, rootIndex() );
        QPen pen = this->pen( index );
        ctx->painter()->setBrush( brush( index ) );
        if ( threeDAttrs.isEnabled() )
            pen.setColor( QColor( 0, 0, 0 ) );
        ctx->painter()->setPen( pen );

        qreal startAngle = d->startAngles[ frontmostpie ];
        if( startAngle > 360 )
            startAngle -= 360;

        qreal endAngle = startAngle + d->angleLens[ frontmostpie ];
        startAngle = qMax( startAngle, 180.0 );

        drawArcEffectSegment( ctx->painter(), piePosition( 0, frontmostpie),
                sizeFor3DEffect, startAngle, endAngle, granularity() );*/
    }

    d->paintDataValueTextsAndMarkers(  this,  ctx,  list,  false, false, &textBoundingRect );
}