00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <QDebug>
00027 #include <QPainter>
00028 #include <QStack>
00029
00030 #include "KDChartPieDiagram.h"
00031 #include "KDChartPieDiagram_p.h"
00032
00033 #include "KDChartAttributesModel.h"
00034 #include "KDChartPaintContext.h"
00035 #include "KDChartPieAttributes.h"
00036 #include "KDChartThreeDPieAttributes.h"
00037 #include "KDChartPainterSaver_p.h"
00038 #include "KDChartDataValueAttributes.h"
00039 #include "KDChartNullPaintDevice.h"
00040
00041 #include <KDABLibFakes>
00042
00043
00044 using namespace KDChart;
00045
00046 PieDiagram::Private::Private()
00047 {
00048 }
00049
00050 PieDiagram::Private::~Private() {}
00051
00052 #define d d_func()
00053
00054 PieDiagram::PieDiagram( QWidget* parent, PolarCoordinatePlane* plane ) :
00055 AbstractPieDiagram( new Private(), parent, plane )
00056 {
00057 init();
00058 }
00059
00060 PieDiagram::~PieDiagram()
00061 {
00062 }
00063
00064 void PieDiagram::init()
00065 {
00066 }
00067
00071 PieDiagram * PieDiagram::clone() const
00072 {
00073 return new PieDiagram( new Private( *d ) );
00074 }
00075
00076 const QPair<QPointF, QPointF> PieDiagram::calculateDataBoundaries () const
00077 {
00078 if ( !checkInvariants( true ) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
00079
00080 const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) );
00081
00082 QPointF bottomLeft ( QPointF( 0, 0 ) );
00083 QPointF topRight;
00084
00085
00086 if ( attrs.explode() ) {
00087 const int colCount = columnCount();
00088 qreal maxExplode = 0.0;
00089 for( int j = 0; j < colCount; ++j ){
00090 const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
00091 maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
00092 }
00093 topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode );
00094 }else{
00095 topRight = QPointF( 1.0, 1.0 );
00096 }
00097 return QPair<QPointF, QPointF> ( bottomLeft, topRight );
00098 }
00099
00100
00101 void PieDiagram::paintEvent( QPaintEvent* )
00102 {
00103 QPainter painter ( viewport() );
00104 PaintContext ctx;
00105 ctx.setPainter ( &painter );
00106 ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
00107 paint ( &ctx );
00108 }
00109
00110 void PieDiagram::resizeEvent ( QResizeEvent*)
00111 {
00112 }
00113
00114 void PieDiagram::resize ( const QSizeF& )
00115 {
00116 }
00117
00118 static QRectF buildReferenceRect( const PolarCoordinatePlane* plane )
00119 {
00120 QRectF contentsRect;
00121
00122 QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
00123 QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
00124 const double offset = temp.y();
00125 referencePointAtTop.setX( referencePointAtTop.x() - offset );
00126 contentsRect.setTopLeft( referencePointAtTop );
00127 contentsRect.setBottomRight( referencePointAtTop + QPointF( 2*offset, 2*offset) );
00128
00129 return contentsRect;
00130 }
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174 void PieDiagram::paint(PaintContext* ctx)
00175 {
00176
00177
00178
00179
00180
00181 QPainter* actualPainter = ctx->painter();
00182 QRectF textBoundingRect;
00183
00184
00185 KDChart::NullPaintDevice nullPd(ctx->rectangle().size().toSize());
00186 QPainter nullPainter(&nullPd);
00187 ctx->setPainter(&nullPainter);
00188 paintInternal(ctx, textBoundingRect);
00189
00190
00191 ctx->setPainter(actualPainter);
00192 paintInternal(ctx, textBoundingRect);
00193 }
00194
00195 void PieDiagram::paintInternal(PaintContext* ctx, QRectF& textBoundingRect)
00196 {
00197
00198
00199 if ( !checkInvariants(true) )
00200 return;
00201
00202 d->reverseMapper.clear();
00203
00204 const PieAttributes attrs( pieAttributes() );
00205 const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( model()->index( 0, 0, rootIndex() ) ) );
00206
00207 const int colCount = columnCount();
00208
00209 QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) );
00210 contentsRect = ctx->rectangle();
00211
00212
00213 if( contentsRect.isEmpty() )
00214 return;
00215
00216 DataValueTextInfoList list;
00217 const qreal sum = valueTotals();
00218
00219 if( sum == 0.0 )
00220 return;
00221
00222 d->startAngles.resize( colCount );
00223 d->angleLens.resize( colCount );
00224
00225
00226 d->size = qMin( contentsRect.width(), contentsRect.height() );
00227
00228
00229
00230 qreal maxExplode = 0.0;
00231 for( int j = 0; j < colCount; ++j ){
00232 const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
00233 maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
00234 }
00235 d->size /= ( 1.0 + 2.0 * maxExplode );
00236
00237 if(!textBoundingRect.isEmpty())
00238 {
00239
00240
00241 double maxDistance = 0, dist = 0;
00242
00243 QPointF center = ctx->rectangle().center();
00244
00245 dist = qAbs(textBoundingRect.right() - center.x());
00246 if(dist > maxDistance)
00247 maxDistance = dist;
00248
00249 dist = qAbs(textBoundingRect.left() - center.x());
00250 if(dist > maxDistance)
00251 maxDistance = dist;
00252
00253 dist = qAbs(textBoundingRect.top() - center.y());
00254 if(dist > maxDistance)
00255 maxDistance = dist;
00256
00257 dist = qAbs(textBoundingRect.bottom() - center.y());
00258 if(dist > maxDistance)
00259 maxDistance = dist;
00260
00261 double size = d->size;
00262 double diff = (2*maxDistance - d->size);
00263 if(diff > 0)
00264 d->size *= 1.0-(diff/size);
00265 }
00266
00267 qreal sizeFor3DEffect = 0.0;
00268 if ( ! threeDAttrs.isEnabled() ) {
00269
00270 qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
00271 qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 );
00272 d->position = QRectF( x, y, d->size, d->size );
00273 d->position.translate( contentsRect.left(), contentsRect.top() );
00274 } else {
00275
00276 qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
00277 qreal height = d->size;
00278
00279
00280 if ( threeDAttrs.depth() >= 0.0 ) {
00281
00282 sizeFor3DEffect = threeDAttrs.depth();
00283 height = d->size - sizeFor3DEffect;
00284 } else {
00285
00286 sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height;
00287 height = d->size - sizeFor3DEffect;
00288 }
00289 qreal y = ( contentsRect.height() == height ) ? 0.0 : ( ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0 );
00290
00291 d->position = QRectF( contentsRect.left() + x, contentsRect.top() + y,
00292 d->size, height );
00293
00294 }
00295
00296 const PolarCoordinatePlane * plane = polarCoordinatePlane();
00297 const qreal sectorsPerValue = 360.0 / sum;
00298 qreal currentValue = plane ? plane->startPosition() : 0.0;
00299
00300 bool atLeastOneValue = false;
00301 QVariant vValY;
00302 for ( int iColumn = 0; iColumn < colCount; ++iColumn ) {
00303
00304 bool bOK;
00305 const double cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) )
00306 .toDouble( &bOK ) );
00307
00308 if( bOK ){
00309 d->startAngles[ iColumn ] = currentValue;
00310 d->angleLens[ iColumn ] = cellValue * sectorsPerValue;
00311 atLeastOneValue = true;
00312 } else {
00313 d->angleLens[ iColumn ] = 0.0;
00314 if ( iColumn > 0.0 )
00315 d->startAngles[ iColumn ] = d->startAngles[ iColumn - 1 ];
00316 else
00317 d->startAngles[ iColumn ] = currentValue;
00318 }
00319
00320
00321
00322
00323 currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ];
00324 }
00325
00326
00327
00328 if( ! atLeastOneValue )
00329 return;
00330
00331
00332
00333
00334 int backmostpie = findPieAt( 90, colCount );
00335
00336 int frontmostpie = findPieAt( 270, colCount );
00337
00338 int rightmostpie = findPieAt( 0, colCount );
00339 int leftmostpie = findPieAt( 180, colCount );
00340
00341
00342 int currentLeftPie = backmostpie;
00343 int currentRightPie = backmostpie;
00344
00345 d->clearListOfAlreadyDrawnDataValueTexts();
00346
00347 drawOnePie( ctx->painter(), &list, 0, backmostpie, granularity(), sizeFor3DEffect );
00348
00349 if( backmostpie == frontmostpie )
00350 {
00351 if( backmostpie == leftmostpie )
00352 currentLeftPie = findLeftPie( currentLeftPie, colCount );
00353 if( backmostpie == rightmostpie )
00354 currentRightPie = findRightPie( currentRightPie, colCount );
00355 }
00356 while( currentLeftPie != frontmostpie )
00357 {
00358 if( currentLeftPie != backmostpie )
00359 drawOnePie( ctx->painter(), &list, 0, currentLeftPie, granularity(), sizeFor3DEffect );
00360 currentLeftPie = findLeftPie( currentLeftPie, colCount );
00361 }
00362 while( currentRightPie != frontmostpie )
00363 {
00364 if( currentRightPie != backmostpie )
00365 drawOnePie( ctx->painter(), &list, 0, currentRightPie, granularity(), sizeFor3DEffect );
00366 currentRightPie = findRightPie( currentRightPie, colCount );
00367 }
00368
00369
00370 if( backmostpie != frontmostpie || ! threeDPieAttributes().isEnabled() )
00371 {
00372 drawOnePie( ctx->painter(), &list, 0, frontmostpie, granularity(), sizeFor3DEffect );
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392 }
00393
00394 d->paintDataValueTextsAndMarkers( this, ctx, list, false, false, &textBoundingRect );
00395 }
00396
00397 #if defined ( Q_WS_WIN)
00398 #define trunc(x) ((int)(x))
00399 #endif
00400
00401 QRectF PieDiagram::piePosition( uint dataset, uint pie ) const
00402 {
00403 Q_UNUSED( dataset );
00404 qreal angleLen = d->angleLens[ pie ];
00405 qreal startAngle = d->startAngles[ pie ];
00406 QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00407 const PieAttributes attrs( pieAttributes( index ) );
00408 const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00409
00410 QRectF drawPosition( d->position );
00411
00412 if ( attrs.explode() ) {
00413 qreal explodeAngle = ( startAngle + angleLen / 2.0 );
00414 qreal explodeAngleRad = DEGTORAD( explodeAngle );
00415 qreal cosAngle = cos( explodeAngleRad );
00416 qreal sinAngle = -sin( explodeAngleRad );
00417 qreal explodeX = attrs.explodeFactor() * d->size * cosAngle;
00418 qreal explodeY = attrs.explodeFactor() * d->size * sinAngle;
00419 drawPosition.translate( explodeX, explodeY );
00420 }
00421 return drawPosition;
00422 }
00423
00432 void PieDiagram::drawOnePie( QPainter* painter,
00433 DataValueTextInfoList* list,
00434 uint dataset, uint pie,
00435 qreal granularity,
00436 qreal threeDPieHeight )
00437 {
00438 Q_UNUSED( threeDPieHeight );
00439
00440 const qreal angleLen = d->angleLens[ pie ];
00441 if ( angleLen ) {
00442 const QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00443 const PieAttributes attrs( pieAttributes( index ) );
00444 const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00445
00446 const QRectF drawPosition = piePosition( dataset, pie );
00447
00448 draw3DEffect( painter,
00449 drawPosition, dataset, pie,
00450 granularity,
00451 threeDAttrs,
00452 attrs.explode() );
00453
00454 drawPieSurface( painter, list, dataset, pie, granularity );
00455 }
00456 }
00457
00465 void PieDiagram::drawPieSurface( QPainter* painter,
00466 DataValueTextInfoList* list,
00467 uint dataset, uint pie,
00468 qreal granularity )
00469 {
00470
00471 qreal angleLen = d->angleLens[ pie ];
00472 if ( angleLen ) {
00473 qreal startAngle = d->startAngles[ pie ];
00474
00475 QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00476 const PieAttributes attrs( pieAttributes( index ) );
00477 const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00478
00479 QRectF drawPosition = piePosition( dataset, pie );
00480
00481 QPen pen = this->pen( index );
00482 painter->setRenderHint ( QPainter::Antialiasing );
00483 painter->setBrush( brush( index ) );
00484
00485
00486
00487
00488 if ( angleLen == 360 ) {
00489
00490 painter->drawEllipse( drawPosition );
00491 } else {
00492
00493
00494 const int arcPoints = static_cast<int>(trunc( angleLen / granularity ));
00495 QPolygonF poly( arcPoints+2 );
00496 qreal degree=0.0;
00497 int iPoint = 0;
00498 bool perfectMatch = false;
00499
00500 while ( degree <= angleLen ){
00501 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + degree );
00502
00503 perfectMatch = (degree == angleLen);
00504 degree += granularity;
00505 ++iPoint;
00506 }
00507 int last = poly.size();
00508
00509 if( ! perfectMatch ){
00510 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + angleLen );
00511
00512
00513 poly.append( drawPosition.center() );
00514 }else{
00515 poly[ iPoint ] = drawPosition.center();
00516 }
00517
00518
00519 const qreal sum = valueTotals();
00520 d->reverseMapper.addPolygon( index.row(), index.column(), poly );
00521 painter->drawPolygon( poly );
00522
00523
00524
00525
00526
00527 const QPointF south = drawPosition.center();
00528 const QPointF southEast = south;
00529 const QPointF southWest = south;
00530 const QPointF north = pointOnCircle( drawPosition, startAngle + angleLen/2.0 );
00531 const QPointF northEast = pointOnCircle( drawPosition, startAngle );
00532 const QPointF northWest = pointOnCircle( drawPosition, startAngle + angleLen );
00533 const QPointF center = (south + north) / 2.0;
00534 const QPointF east = (south + northEast) / 2.0;
00535 const QPointF west = (south + northWest) / 2.0;
00536 PositionPoints points( center, northWest, north, northEast, east, southEast, south, southWest, west);
00537 qreal topAngle = startAngle - 90;
00538 if( topAngle < 0.0 )
00539 topAngle += 360;
00540 points.setDegrees(KDChartEnums::PositionEast, topAngle);
00541 points.setDegrees(KDChartEnums::PositionNorthEast, topAngle);
00542 points.setDegrees(KDChartEnums::PositionWest, topAngle + angleLen);
00543 points.setDegrees(KDChartEnums::PositionNorthWest, topAngle + angleLen);
00544 points.setDegrees(KDChartEnums::PositionCenter, topAngle + angleLen/2.0);
00545 points.setDegrees(KDChartEnums::PositionNorth, topAngle + angleLen/2.0);
00546 d->appendDataValueTextInfoToList(
00547 this, *list, index, 0,
00548 points, Position::Center, Position::Center,
00549 angleLen*sum / 360 );
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562 }
00563 }
00564 }
00565
00566
00576 void PieDiagram::draw3DEffect( QPainter* painter,
00577 const QRectF& drawPosition,
00578 uint dataset, uint pie,
00579 qreal granularity,
00580 const ThreeDPieAttributes& threeDAttrs,
00581 bool )
00582 {
00583 Q_UNUSED( dataset );
00584
00585 if( ! threeDAttrs.isEnabled() )
00586 return;
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596 if( threeDAttrs.useShadowColors() ){
00597 const QPen pen = this->pen( model()->index( 0, pie, rootIndex() ) );
00598 painter->setBrush( QBrush( pen.color() ) );
00599 }
00600
00601
00602
00603 qreal startAngle = d->startAngles[ pie ];
00604 qreal endAngle = startAngle + d->angleLens[ pie ];
00605
00606 while ( startAngle >= 360 )
00607 startAngle -= 360;
00608 while ( endAngle >= 360 )
00609 endAngle -= 360;
00610 Q_ASSERT( startAngle >= 0 && startAngle <= 360 );
00611 Q_ASSERT( endAngle >= 0 && endAngle <= 360 );
00612
00613
00614
00615 if ( startAngle == endAngle ||
00616 startAngle == endAngle - 360 ) {
00617 drawArcEffectSegment( painter, drawPosition,
00618 threeDAttrs.depth(),
00619 180, 360, granularity );
00620 } else if ( startAngle <= 90 ) {
00621 if ( endAngle <= 90 ) {
00622 if ( startAngle <= endAngle ) {
00624 drawStraightEffectSegment( painter, drawPosition,
00625 threeDAttrs.depth(), startAngle );
00626 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00627 } else {
00629 drawStraightEffectSegment( painter, drawPosition,
00630 threeDAttrs.depth(), startAngle );
00631 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00632 drawArcEffectSegment( painter, drawPosition,
00633 threeDAttrs.depth(),
00634 180, 360, granularity );
00635 }
00636 } else if ( endAngle <= 180 ) {
00639 drawStraightEffectSegment( painter, drawPosition,
00640 threeDAttrs.depth(), startAngle );
00641 drawStraightEffectSegment( painter, drawPosition,
00642 threeDAttrs.depth(), endAngle );
00643 } else if ( endAngle <= 270 ) {
00645 drawStraightEffectSegment( painter, drawPosition,
00646 threeDAttrs.depth(), startAngle );
00647 drawStraightEffectSegment( painter, drawPosition,
00648 threeDAttrs.depth(), endAngle );
00649 drawArcEffectSegment( painter, drawPosition,
00650 threeDAttrs.depth(),
00651 180, endAngle, granularity );
00652 } else {
00655 drawStraightEffectSegment( painter, drawPosition,
00656 threeDAttrs.depth(), startAngle );
00657 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00658 drawArcEffectSegment( painter, drawPosition,
00659 threeDAttrs.depth(),
00660 180, endAngle, granularity );
00661 }
00662 } else if ( startAngle <= 180 ) {
00663 if ( endAngle <= 90 ) {
00664 drawArcEffectSegment( painter, drawPosition,
00665 threeDAttrs.depth(),
00666 180, 360, granularity );
00667 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00668 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00669 } else if ( endAngle <= 180 ) {
00670 if ( startAngle <= endAngle ) {
00673 drawStraightEffectSegment( painter, drawPosition,
00674 threeDAttrs.depth(), endAngle );
00675 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00676 } else {
00679 drawStraightEffectSegment( painter, drawPosition,
00680 threeDAttrs.depth(), endAngle );
00681 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00682 drawArcEffectSegment( painter, drawPosition,
00683 threeDAttrs.depth(),
00684 180, 360, granularity );
00685 }
00686 } else if ( endAngle <= 270 ) {
00687 drawStraightEffectSegment( painter, drawPosition,
00688 threeDAttrs.depth(), endAngle );
00689 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00690 drawArcEffectSegment( painter, drawPosition,
00691 threeDAttrs.depth(),
00692 180, endAngle, granularity );
00693 } else {
00694 drawArcEffectSegment( painter, drawPosition,
00695 threeDAttrs.depth(),
00696 180, endAngle, granularity );
00697 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00698 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00699 }
00700 } else if ( startAngle <= 270 ) {
00701 if ( endAngle <= 90 ) {
00702 drawArcEffectSegment( painter, drawPosition,
00703 threeDAttrs.depth(),
00704 startAngle, 360, granularity );
00705 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00706 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00707 } else if ( endAngle <= 180 ) {
00708 drawStraightEffectSegment( painter, drawPosition,
00709 threeDAttrs.depth(), endAngle );
00710 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00711 drawArcEffectSegment( painter, drawPosition,
00712 threeDAttrs.depth(),
00713 startAngle, 360, granularity );
00714 } else if ( endAngle <= 270 ) {
00715 if ( startAngle <= endAngle ) {
00718 drawStraightEffectSegment( painter, drawPosition,
00719 threeDAttrs.depth(), endAngle );
00720 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00721 drawArcEffectSegment( painter, drawPosition,
00722 threeDAttrs.depth(),
00723 startAngle, endAngle, granularity );
00724 } else {
00727 drawStraightEffectSegment( painter, drawPosition,
00728 threeDAttrs.depth(), endAngle );
00729 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00730 drawArcEffectSegment( painter, drawPosition,
00731 threeDAttrs.depth(),
00732 180, endAngle, granularity );
00733 drawArcEffectSegment( painter, drawPosition,
00734 threeDAttrs.depth(),
00735 startAngle, 360, granularity );
00736 }
00737 } else {
00738 drawArcEffectSegment( painter, drawPosition,
00739 threeDAttrs.depth(),
00740 startAngle, endAngle, granularity );
00741 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00742 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00743 }
00744 } else {
00745 if ( endAngle <= 90 ) {
00746 drawStraightEffectSegment( painter, drawPosition,
00747 threeDAttrs.depth(), startAngle );
00748 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00749 drawArcEffectSegment( painter, drawPosition,
00750 threeDAttrs.depth(),
00751 startAngle, 360, granularity );
00752 } else if ( endAngle <= 180 ) {
00753 drawStraightEffectSegment( painter, drawPosition,
00754 threeDAttrs.depth(), startAngle );
00755 drawStraightEffectSegment( painter, drawPosition,
00756 threeDAttrs.depth(), endAngle );
00757 drawArcEffectSegment( painter, drawPosition,
00758 threeDAttrs.depth(),
00759 startAngle, 360, granularity );
00760 } else if ( endAngle <= 270 ) {
00761 drawStraightEffectSegment( painter, drawPosition,
00762 threeDAttrs.depth(), startAngle );
00763 drawStraightEffectSegment( painter, drawPosition,
00764 threeDAttrs.depth(), endAngle );
00765 drawArcEffectSegment( painter, drawPosition,
00766 threeDAttrs.depth(),
00767 180, endAngle, granularity );
00768 drawArcEffectSegment( painter, drawPosition,
00769 threeDAttrs.depth(),
00770 startAngle, 360, granularity );
00771 } else {
00772 if ( startAngle <= endAngle ) {
00775 drawStraightEffectSegment( painter, drawPosition,
00776 threeDAttrs.depth(), startAngle );
00777 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00778 drawArcEffectSegment( painter, drawPosition,
00779 threeDAttrs.depth(),
00780 startAngle, endAngle, granularity );
00781 } else {
00784 drawStraightEffectSegment( painter, drawPosition,
00785 threeDAttrs.depth(), startAngle );
00786 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00787 drawArcEffectSegment( painter, drawPosition,
00788 threeDAttrs.depth(),
00789 startAngle, 360, granularity );
00790 drawArcEffectSegment( painter, drawPosition,
00791 threeDAttrs.depth(),
00792 180, endAngle, granularity );
00793 }
00794 }
00795 }
00796 drawArcUpperBrinkEffectSegment( painter, drawPosition, startAngle, endAngle, granularity );
00797 }
00798
00799
00808 void PieDiagram::drawStraightEffectSegment( QPainter* painter,
00809 const QRectF& rect,
00810 qreal threeDHeight,
00811 qreal angle )
00812 {
00813 QPolygonF poly( 4 );
00814 const QPointF center = rect.center();
00815 const QPointF circlePoint = pointOnCircle( rect, angle );
00816 poly[0] = center;
00817 poly[1] = circlePoint;
00818 poly[2] = QPointF( circlePoint.x(), circlePoint.y() + threeDHeight );
00819 poly[3] = QPointF( center.x(), center.y() + threeDHeight );
00820
00821 painter->drawPolygon( poly );
00822
00823
00824 }
00825
00833 void PieDiagram::drawUpperBrinkEffect( QPainter* painter,
00834 const QRectF& rect,
00835 qreal angle )
00836 {
00837 const QPointF center = rect.center();
00838 const QPointF circlePoint = pointOnCircle( rect, angle );
00839 painter->drawLine( center, circlePoint );
00840 }
00841
00851 void PieDiagram::drawArcEffectSegment( QPainter* painter,
00852 const QRectF& rect,
00853 qreal threeDHeight,
00854 qreal startAngle,
00855 qreal endAngle,
00856 qreal granularity )
00857 {
00858
00859 qreal startA = qMin( startAngle, endAngle );
00860 qreal endA = qMax( startAngle, endAngle );
00861
00862
00863 if( endA > 540 )
00864 drawArcEffectSegment( painter, rect, threeDHeight, 180, endA - 360, granularity );
00865 if( endA > 360 )
00866 endA = qMin( endA, qreal( 360.0 ) );
00867
00868 int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
00869
00870 QPolygonF poly( numHalfPoints );
00871
00872 qreal degree = endA;
00873 int iPoint = 0;
00874 bool perfectMatch = false;
00875 while ( degree >= startA ){
00876 poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
00877
00878 perfectMatch = (degree == startA);
00879 degree -= granularity;
00880 ++iPoint;
00881 }
00882
00883 if( ! perfectMatch ){
00884 poly.prepend( pointOnCircle( rect, startA ) );
00885 ++numHalfPoints;
00886 }
00887
00888 poly.resize( numHalfPoints * 2 );
00889
00890
00891
00892 for ( int i = numHalfPoints - 1; i >= 0; --i ) {
00893 QPointF pointOnFirstArc( poly[ i ] );
00894 pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
00895 poly[ numHalfPoints * 2 - i - 1 ] = pointOnFirstArc;
00896 }
00897
00898
00899 painter->drawPolygon( poly );
00900
00901
00902 }
00903
00912 void PieDiagram::drawArcUpperBrinkEffectSegment( QPainter* painter,
00913 const QRectF& rect,
00914 qreal startAngle,
00915 qreal endAngle,
00916 qreal granularity )
00917 {
00918 if ( endAngle < startAngle )
00919 endAngle += 360;
00920
00921 const qreal startA = qMin( startAngle, endAngle );
00922 const qreal endA = qMax( startAngle, endAngle );
00923
00924 int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
00925
00926 QPolygonF poly( numHalfPoints );
00927
00928 qreal degree = endA;
00929 int iPoint = 0;
00930 bool perfectMatch = false;
00931 while ( degree >= startA ){
00932 poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
00933
00934 perfectMatch = (degree == startA);
00935 degree -= granularity;
00936 ++iPoint;
00937 }
00938
00939 if( ! perfectMatch ){
00940 poly.prepend( pointOnCircle( rect, startA ) );
00941 ++numHalfPoints;
00942 }
00943
00944 painter->drawPolyline( poly );
00945
00946
00947 }
00948
00956 uint PieDiagram::findPieAt( qreal angle, int colCount )
00957 {
00958 for ( int i = 0; i < colCount; ++i ) {
00959 qreal endseg = d->startAngles[ i ] + d->angleLens[ i ];
00960 if ( ( d->startAngles[ i ] <= angle ) &&
00961 ( endseg >= angle ) )
00962
00963 return i;
00964 }
00965
00966
00967
00968 if ( angle < 360 )
00969 return findPieAt( angle + 360, colCount );
00970
00971 return 0;
00972 }
00973
00974
00982 uint PieDiagram::findLeftPie( uint pie, int colCount )
00983 {
00984 if ( pie == 0 )
00985 if ( colCount > 1 )
00986 return colCount - 1;
00987 else
00988 return 0;
00989 else {
00990 return pie - 1;
00991 }
00992 }
00993
00994
01002 uint PieDiagram::findRightPie( uint pie, int colCount )
01003 {
01004 int rightpie = pie + 1;
01005 if ( rightpie == colCount )
01006 rightpie = 0;
01007 return rightpie;
01008 }
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043
01048 QPointF PieDiagram::pointOnCircle( const QRectF& rect, qreal angle )
01049 {
01050 qreal angleRad = DEGTORAD( angle );
01051 qreal cosAngle = cos( angleRad );
01052 qreal sinAngle = -sin( angleRad );
01053 qreal posX = cosAngle * rect.width() / 2.0;
01054 qreal posY = sinAngle * rect.height() / 2.0;
01055 return QPointF( posX + rect.center().x(),
01056 posY + rect.center().y() );
01057
01058 }
01059
01060
01061 double PieDiagram::valueTotals() const
01062 {
01063 const int colCount = columnCount();
01064 double total = 0.0;
01065 for ( int j = 0; j < colCount; ++j ) {
01066 total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toDouble());
01067
01068 }
01069 return total;
01070 }
01071
01072
01073 double PieDiagram::numberOfValuesPerDataset() const
01074 {
01075 return model() ? model()->columnCount( rootIndex() ) : 0.0;
01076 }
01077
01078
01079 double PieDiagram::numberOfGridRings() const
01080 {
01081 return 1;
01082 }