const QPair<QPointF, QPointF> RingDiagram::calculateDataBoundaries () const { if ( !checkInvariants( true ) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) ); const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) ); QPointF bottomLeft ( QPointF( 0, 0 ) ); QPointF topRight; // If we explode, we need extra space for the pie slice that has // the largest explosion distance. if ( attrs.explode() ) { const int rCount = rowCount(); const int colCount = columnCount(); qreal maxExplode = 0.0; for( int i = 0; i < rCount; ++i ){ qreal maxExplodeInThisRow = 0.0; for( int j = 0; j < colCount; ++j ){ const PieAttributes columnAttrs( pieAttributes( model()->index( i, j, rootIndex() ) ) ); //qDebug() << columnAttrs.explodeFactor(); maxExplodeInThisRow = qMax( maxExplodeInThisRow, columnAttrs.explodeFactor() ); } maxExplode += maxExplodeInThisRow; // FIXME: What if explode factor of inner ring is > 1.0 ? if ( !d->expandWhenExploded ) break; } // explode factor is relative to width (outer r - inner r) of one ring maxExplode /= ( rCount + 1); topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode ); }else{ topRight = QPointF( 1.0, 1.0 ); } return QPair<QPointF, QPointF> ( bottomLeft, topRight ); }
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; } }
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 ); }