Example #1
0
void PieDiagram::calcPieSize( const QRectF &contentsRect )
{
    d->size = qMin( contentsRect.width(), contentsRect.height() );

    // if any slice explodes, the whole pie needs additional space so we make the basic size smaller
    qreal maxExplode = 0.0;
    const int colCount = columnCount();
    for ( int j = 0; j < colCount; ++j ) {
        const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); // checked
        maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
    }
    d->size /= ( 1.0 + 1.0 * maxExplode );

    if ( d->size < 0.0 ) {
        d->size = 0;
    }
}
Example #2
0
const QPair<QPointF, QPointF> PieDiagram::calculateDataBoundaries () const
{
    if ( !checkInvariants( true ) || model()->rowCount() < 1 ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );

    const PieAttributes attrs( pieAttributes() );

    QPointF bottomLeft( QPointF( 0, 0 ) );
    QPointF topRight;
    // If we explode, we need extra space for the slice that has the largest explosion distance.
    if ( attrs.explode() ) {
        const int colCount = columnCount();
        qreal maxExplode = 0.0;
        for ( int j = 0; j < colCount; ++j ) {
            const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) ); // checked
            maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
        }
        topRight = QPointF( 1.0 + maxExplode, 1.0 + maxExplode );
    } else {
        topRight = QPointF( 1.0, 1.0 );
    }
    return QPair<QPointF, QPointF> ( bottomLeft,  topRight );
}
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 );
}