# KDChartPieDiagram.cpp

Go to the documentation of this file.

    00001 /****************************************************************************
    00002  ** Copyright (C) 2006 Klarälvdalens Datakonsult AB.  All rights reserved.
    00003  **
    00004  ** This file is part of the KD Chart library.
    00005  **
    00006  ** This file may be distributed and/or modified under the terms of the
    00007  ** GNU General Public License version 2 as published by the Free Software
    00008  ** Foundation and appearing in the file LICENSE.GPL included in the
    00009  ** packaging of this file.
    00010  **
    00011  ** Licensees holding valid commercial KD Chart licenses may use this file in
    00012  ** accordance with the KD Chart Commercial License Agreement provided with
    00013  ** the Software.
    00014  **
    00015  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    00016  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    00017  **
    00018  ** See http://www.kdab.net/kdchart for
    00019  **   information about KDChart Commercial License Agreements.
    00020  **
    00021  ** Contact info@kdab.net if any conditions of this
    00022  ** licensing are not clear to you.
    00023  **
    00024  **********************************************************************/
    00025
    00026 #include <QDebug>
    00027 #include <QPainter>
    00028 #include <QStack>
    00029
    00030 #include "KDChartAttributesModel.h"
    00031 #include "KDChartPaintContext.h"
    00032 #include "KDChartPieDiagram.h"
    00033 #include "KDChartPieDiagram_p.h"
    00034 #include "KDChartPieAttributes.h"
    00035 #include "KDChartThreeDPieAttributes.h"
    00036 #include "KDChartPainterSaver_p.h"
    00037 #include "KDChartDataValueAttributes.h"
    00038
    00039 #include <KDABLibFakes>
    00040
    00041
    00042 using namespace KDChart;
    00043
    00044 PieDiagram::Private::Private()
    00045 {
    00046 }
    00047
    00048 PieDiagram::Private::~Private() {}
    00049
    00050 #define d d_func()
    00051
    00052 PieDiagram::PieDiagram( QWidget* parent, PolarCoordinatePlane* plane ) :
    00053     AbstractPieDiagram( new Private(), parent, plane )
    00054 {
    00055     init();
    00056 }
    00057
    00058 PieDiagram::~PieDiagram()
    00059 {
    00060 }
    00061
    00062 void PieDiagram::init()
    00063 {
    00064 }
    00065
    00066 PieDiagram * PieDiagram::clone() const
    00067 {
    00068     return new PieDiagram( new Private( *d ) );
    00069 }
    00070
    00071 const QPair<QPointF, QPointF> PieDiagram::calculateDataBoundaries () const
    00072 {
    00073     if ( !checkInvariants( true ) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
    00074
    00075     const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) );
    00076
    00077     QPointF bottomLeft ( QPointF( 0, 0 ) );
    00078     QPointF topRight;
    00079     // If we explode, we need extra space for the pie slice that has
    00080     // the largest explosion distance.
    00081     if ( attrs.explode() ) {
    00082         const int colCount = columnCount();
    00083         qreal maxExplode = 0.0;
    00084         for( int j = 0; j < colCount; ++j ){
    00085             const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
    00086             maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
    00087         }
    00088         topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode );
    00089     }else{
    00090         topRight = QPointF( 1.0, 1.0 );
    00091     }
    00092     return QPair<QPointF, QPointF> ( bottomLeft,  topRight );
    00093 }
    00094
    00095
    00096 void PieDiagram::paintEvent( QPaintEvent* )
    00097 {
    00098     QPainter painter ( viewport() );
    00099     PaintContext ctx;
    00100     ctx.setPainter ( &painter );
    00101     ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
    00102     paint ( &ctx );
    00103 }
    00104
    00105 void PieDiagram::resizeEvent ( QResizeEvent*)
    00106 {
    00107 }
    00108
    00109 void PieDiagram::resize ( const QSizeF& )
    00110 {
    00111 }
    00112
    00113 static QRectF buildReferenceRect( const PolarCoordinatePlane* plane )
    00114 {
    00115     QRectF contentsRect;
    00116 //qDebug() << "..........................................";
    00117     QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
    00118     QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
    00119     const double offset = temp.y();
    00120     referencePointAtTop.setX( referencePointAtTop.x() - offset );
    00121     contentsRect.setTopLeft( referencePointAtTop );
    00122     contentsRect.setBottomRight( referencePointAtTop + QPointF( 2*offset, 2*offset) );
    00123 //qDebug() << contentsRect;
    00124     return contentsRect;
    00125 }
    00126 /*
    00127 void PieDiagram::paint( PaintContext* ctx )
    00128 {
    00129     if ( !checkInvariants(true) ) return;
    00130     const int colCount = model()->columnCount(rootIndex());
    00131     QRectF contentsRect = buildReferenceRect( polarCoordinatePlane() );
    00132     DataValueTextInfoList list;
    00133     double startAngle = startPosition();
    00134     double startAngleValueSpace = valueTotals() / 360 * startAngle;
    00135     for ( int j=0; j<colCount; ++j ) {
    00136         const double nextValue = qAbs( model()->data( model()->index( 0, j,rootIndex() ) ).toDouble() );
    00137         double spanAngle = polarCoordinatePlane()->translatePolar( QPointF( nextValue, 1 ) ).x();
    00138         if ( spanAngle == 0 ) continue;
    00139         QBrush brush = qVariantValue<QBrush>( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetBrushRole ) );
    00140         QPen pen = qVariantValue<QPen>( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetPenRole ) );
    00141         PainterSaver painterSaver( ctx->painter() );
    00142         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00143         ctx->painter()->setBrush( brush );
    00144         ctx->painter()->setPen( pen );
    00145
    00146         // Explosion support
    00147         QRectF pieRect = contentsRect;
    00148         if( explode() ) {
    00149             QPointF oldCenter = contentsRect.center();
    00150             QPointF newCenter = polarCoordinatePlane()->translate( QPointF( explodeFactor( j ),
    00151                                                                             startAngleValueSpace + nextValue/2.0 ) );
    00152             QPointF difference = newCenter - oldCenter;
    00153             pieRect.translate( difference );
    00154         }
    00155
    00156         ctx->painter()->drawPie( pieRect, ( int ) ((-startAngle + 90 )), ( int ) (-spanAngle) );
    00157         startAngle += spanAngle;
    00158         startAngleValueSpace += nextValue;
    00159     }
    00160     DataValueTextInfoListIterator it( list );
    00161     while ( it.hasNext() ) {
    00162         const DataValueTextInfo& info = it.next();
    00163         paintDataValueText( ctx->painter(), info.index, info.pos, info.value );
    00164     }
    00165 }
    00166 */
    00167
    00168
    00169 void PieDiagram::paint( PaintContext* ctx )
    00170 {
    00171     // note: Not having any data model assigned is no bug
    00172     //       but we can not draw a diagram then either.
    00173     if ( !checkInvariants(true) )
    00174         return;
    00175
    00176     const PieAttributes attrs( pieAttributes() );
    00177     const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( model()->index( 0, 0, rootIndex() ) ) );
    00178
    00179     const int colCount = columnCount();
    00180
    00181     QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) );
    00182     contentsRect = ctx->rectangle();
    00183 //    contentsRect = geometry();
    00184 //qDebug() << contentsRect;
    00185     if( contentsRect.isEmpty() )
    00186         return;
    00187
    00188     DataValueTextInfoList list;
    00189     const qreal sum = valueTotals();
    00190
    00191     if( sum == 0.0 ) //nothing to draw
    00192         return;
    00193
    00194     d->startAngles.resize( colCount );
    00195     d->angleLens.resize( colCount );
    00196
    00197     // compute position
    00198     d->size = qMin( contentsRect.width(), contentsRect.height() ); // initial size
    00199
    00200     // if the pies explode, we need to give them additional space =>
    00201     // make the basic size smaller
    00202     qreal maxExplode = 0.0;
    00203     for( int j = 0; j < colCount; ++j ){
    00204         const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
    00205         maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
    00206     }
    00207     d->size /= ( 1.0 + 2.0 * maxExplode );
    00208
    00209
    00210     qreal sizeFor3DEffect = 0.0;
    00211     if ( ! threeDAttrs.isEnabled() ) {
    00212
    00213         qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
    00214         qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 );
    00215         d->position = QRectF( x, y, d->size, d->size );
    00216         d->position.translate( contentsRect.left(), contentsRect.top() );
    00217     } else {
    00218         // threeD: width is the maximum possible width; height is 1/2 of that
    00219         qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
    00220         qreal height = d->size;
    00221         // make sure that the height plus the threeDheight is not more than the
    00222         // available size
    00223         if ( threeDAttrs.depth() >= 0.0 ) {
    00224             // positive pie height: absolute value
    00225             sizeFor3DEffect = threeDAttrs.depth();
    00226             height = d->size - sizeFor3DEffect;
    00227         } else {
    00228             // negative pie height: relative value
    00229             sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height;
    00230             height = d->size - sizeFor3DEffect;
    00231         }
    00232         qreal y = ( contentsRect.height() == height ) ? 0.0 : ( ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0 );
    00233
    00234         d->position = QRectF( contentsRect.left() + x, contentsRect.top() + y,
    00235                 d->size, height );
    00236         //  d->position.moveBy( contentsRect.left(), contentsRect.top() );
    00237     }
    00238
    00239     const PolarCoordinatePlane * plane = polarCoordinatePlane();
    00240     const qreal sectorsPerValue = 360.0 / sum;
    00241     qreal currentValue = plane ? plane->startPosition() : 0.0;
    00242
    00243     bool atLeastOneValue = false; // guard against completely empty tables
    00244     QVariant vValY;
    00245     for ( int iColumn = 0; iColumn < colCount; ++iColumn ) {
    00246         // is there anything at all at this column?
    00247         bool bOK;
    00248         const double cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) )
    00249             .toDouble( &bOK ) );
    00250
    00251         if( bOK ){
    00252             d->startAngles[ iColumn ] = currentValue;
    00253             d->angleLens[ iColumn ] = cellValue * sectorsPerValue;
    00254             atLeastOneValue = true;
    00255         } else { // mark as non-existent
    00256             d->angleLens[ iColumn ] = 0.0;
    00257             if ( iColumn > 0.0 )
    00258                 d->startAngles[ iColumn ] = d->startAngles[ iColumn - 1 ];
    00259             else
    00260                 d->startAngles[ iColumn ] = currentValue;
    00261         }
    00262         //qDebug() << "d->startAngles["<<iColumn<<"] == " << d->startAngles[ iColumn ]
    00263         //         << " +  d->angleLens["<<iColumn<<"]" << d->angleLens[ iColumn ]
    00264         //         << " = " << d->startAngles[ iColumn ]+d->angleLens[ iColumn ];
    00265
    00266         currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ];
    00267     }
    00268
    00269     // If there was no value at all, bail out, to avoid endless loops
    00270     // later on (e.g. in findPieAt()).
    00271     if( ! atLeastOneValue )
    00272         return;
    00273
    00274
    00275     // Find the backmost pie which is at +90° and needs to be drawn
    00276     // first
    00277     int backmostpie = findPieAt( 90, colCount );
    00278     // Find the frontmost pie (at -90°/+270°) that should be drawn last
    00279     int frontmostpie = findPieAt( 270, colCount );
    00280     // the right- and the leftmost (only needed in some special cases...)
    00281     int rightmostpie = findPieAt( 0, colCount );
    00282     int leftmostpie = findPieAt( 180, colCount );
    00283
    00284
    00285     int currentLeftPie = backmostpie;
    00286     int currentRightPie = backmostpie;
    00287
    00288     drawOnePie( ctx->painter(), 0, backmostpie, granularity(), sizeFor3DEffect );
    00289
    00290     if( backmostpie == frontmostpie )
    00291     {
    00292         if( backmostpie == leftmostpie )
    00293             currentLeftPie = findLeftPie( currentLeftPie, colCount );
    00294         if( backmostpie == rightmostpie )
    00295             currentRightPie = findRightPie( currentRightPie, colCount );
    00296     }
    00297     while( currentLeftPie != frontmostpie )
    00298     {
    00299         if( currentLeftPie != backmostpie )
    00300             drawOnePie( ctx->painter(), 0, currentLeftPie, granularity(), sizeFor3DEffect );
    00301         currentLeftPie = findLeftPie( currentLeftPie, colCount );
    00302     }
    00303     while( currentRightPie != frontmostpie )
    00304     {
    00305         if( currentRightPie != backmostpie )
    00306             drawOnePie( ctx->painter(), 0, currentRightPie, granularity(), sizeFor3DEffect );
    00307         currentRightPie = findRightPie( currentRightPie, colCount );
    00308     }
    00309
    00310     // if the backmost pie is not the frontmost pie, we draw the frontmost at last
    00311     if( backmostpie != frontmostpie || ! threeDPieAttributes().isEnabled() )
    00312     {
    00313         drawOnePie( ctx->painter(), 0, frontmostpie, granularity(), sizeFor3DEffect );
    00314     // else, this gets a bit mor complicated...
    00315     } else if( threeDPieAttributes().isEnabled() ) {
    00316         drawPieSurface( ctx->painter(), 0, frontmostpie, granularity() );
    00317         const QModelIndex index = model()->index( 0, frontmostpie, rootIndex() );
    00318         QPen pen = this->pen( index );
    00319         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
    00320         ctx->painter()->setBrush( brush( index ) );
    00321         if ( threeDAttrs.isEnabled() )
    00322             pen.setColor( QColor( 0, 0, 0 ) );
    00323         ctx->painter()->setPen( pen );
    00324
    00325         qreal startAngle = d->startAngles[ frontmostpie ];
    00326         if( startAngle > 360 )
    00327             startAngle -= 360;
    00328
    00329         qreal endAngle = startAngle + d->angleLens[ frontmostpie ];
    00330         startAngle = qMax( startAngle, 180.0 );
    00331
    00332         drawArcEffectSegment( ctx->painter(), piePosition( 0, frontmostpie),
    00333                 sizeFor3DEffect, startAngle, endAngle, granularity() );
    00334     }
    00335 }
    00336
    00337 #if defined ( Q_WS_WIN)
    00338 #define trunc(x) ((int)(x))
    00339 #endif
    00340
    00341 QRectF PieDiagram::piePosition( uint dataset, uint pie ) const
    00342 {
    00343     qreal angleLen = d->angleLens[ pie ];
    00344     qreal startAngle = d->startAngles[ pie ];
    00345     QModelIndex index( model()->index( 0, pie, rootIndex() ) );
    00346     const PieAttributes attrs( pieAttributes( index ) );
    00347     const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
    00348
    00349     QRectF drawPosition( d->position );
    00350
    00351     if ( attrs.explode() ) {
    00352         qreal explodeAngle = ( startAngle + angleLen / 2.0 );
    00353         qreal explodeAngleRad = DEGTORAD( explodeAngle );
    00354         qreal cosAngle = cos( explodeAngleRad );
    00355         qreal sinAngle = -sin( explodeAngleRad );
    00356         qreal explodeX = attrs.explodeFactor() * d->size * cosAngle;
    00357         qreal explodeY = attrs.explodeFactor() * d->size * sinAngle;
    00358         drawPosition.translate( explodeX, explodeY );
    00359     }else{
    00360         drawPosition = d->position;
    00361     }
    00362     return drawPosition;
    00363  }
    00364
    00373 void PieDiagram::drawOnePie( QPainter* painter,
    00374         uint dataset, uint pie,
    00375         qreal granularity,
    00376         qreal threeDPieHeight )
    00377 {
    00378     // Is there anything to draw at all?
    00379     qreal angleLen = d->angleLens[ pie ];
    00380     if ( angleLen ) {
    00381   //      qreal startAngle = d->startAngles[ pie ];
    00382 /*
    00383         KDChartDataRegion* datReg = 0;
    00384         QRegion* region = 0;
    00385         bool mustDeleteRegion = false;
    00386         if ( regions ){
    00387             region = new QRegion();
    00388             mustDeleteRegion = true;
    00389         }
    00390 */
    00391         QModelIndex index( model()->index( 0, pie, rootIndex() ) );
    00392         const PieAttributes attrs( pieAttributes( index ) );
    00393         const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
    00394
    00395         QRectF drawPosition = piePosition( dataset, pie );
    00396
    00397         draw3DEffect( painter,
    00398             drawPosition, dataset, pie,
    00399             granularity,
    00400             threeDAttrs,
    00401             attrs.explode() );
    00402
    00403         drawPieSurface( painter, dataset, pie, granularity );
    00404     }
    00405 }
    00406
    00414 void PieDiagram::drawPieSurface( QPainter* painter,
    00415         uint dataset, uint pie,
    00416         qreal granularity )
    00417 {
    00418     // Is there anything to draw at all?
    00419     qreal angleLen = d->angleLens[ pie ];
    00420     if ( angleLen ) {
    00421         qreal startAngle = d->startAngles[ pie ];
    00422 /*
    00423         KDChartDataRegion* datReg = 0;
    00424         QRegion* region = 0;
    00425         bool mustDeleteRegion = false;
    00426         if ( regions ){
    00427             region = new QRegion();
    00428             mustDeleteRegion = true;
    00429         }
    00430 */
    00431         QModelIndex index( model()->index( 0, pie, rootIndex() ) );
    00432         const PieAttributes attrs( pieAttributes( index ) );
    00433         const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
    00434
    00435         QRectF drawPosition = piePosition( dataset, pie );
    00436 //        QRectF drawPosition( d->position );
    00437
    00438 /*        if ( attrs.explode() ) {
    00439             qreal explodeAngle = ( startAngle + angleLen / 2.0 );
    00440             qreal explodeAngleRad = DEGTORAD( explodeAngle );
    00441             qreal cosAngle = cos( explodeAngleRad );
    00442             qreal sinAngle = -sin( explodeAngleRad );
    00443             qreal explodeX = attrs.explodeFactor() * d->size * cosAngle;
    00444             qreal explodeY = attrs.explodeFactor() * d->size * sinAngle;
    00445             drawPosition.translate( explodeX, explodeY );
    00446         }else{
    00447             drawPosition = d->position;
    00448         }*/
    00449
    00450         QPen pen = this->pen( index );
    00451         PainterSaver painterSaver( painter );
    00452         painter->setRenderHint ( QPainter::Antialiasing );
    00453         painter->setBrush( brush( index ) );
    00454         if ( threeDAttrs.isEnabled() )
    00455             pen.setColor( QColor( 0, 0, 0 ) );
    00456         painter->setPen( pen );
    00457
    00458         if ( angleLen == 360 ) {
    00459             // full circle, avoid nasty line in the middle
    00460             painter->drawEllipse( drawPosition );
    00461 /*
    00462             if ( regions ) {
    00463                 QPointArray hitregion;
    00464                 hitregion.makeEllipse( drawPosition.x(), drawPosition.y(),
    00465                                        drawPosition.width(),
    00466                                        drawPosition.height() );
    00467                 datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ),
    00468                                                 dataset,
    00469                                                 pie,
    00470                                                 chart );
    00471                 datReg->points[ KDChartEnums::PosCenter ]
    00472                     = drawPosition.center();
    00473                 datReg->points[ KDChartEnums::PosCenterRight ]
    00474                     = pointOnCircle( drawPosition,    0 );
    00475                 datReg->points[ KDChartEnums::PosTopRight ]
    00476                     = pointOnCircle( drawPosition,  45 );
    00477                 datReg->points[ KDChartEnums::PosTopCenter ]
    00478                     = pointOnCircle( drawPosition, 90 );
    00479                 datReg->points[ KDChartEnums::PosTopLeft ]
    00480                     = pointOnCircle( drawPosition, 135 );
    00481                 datReg->points[ KDChartEnums::PosCenterLeft ]
    00482                     = pointOnCircle( drawPosition, 180 );
    00483                 datReg->points[ KDChartEnums::PosBottomLeft ]
    00484                     = pointOnCircle( drawPosition, 225 );
    00485                 datReg->points[ KDChartEnums::PosBottomCenter ]
    00486                     = pointOnCircle( drawPosition, 270 );
    00487                 datReg->points[ KDChartEnums::PosBottomRight ]
    00488                     = pointOnCircle( drawPosition, 315 );
    00489                 datReg->startAngle = 180;
    00490                 datReg->angleLen   = 360;
    00491                 regions->append( datReg );
    00492             }
    00493 */
    00494         } else {
    00495             // draw the top of this piece
    00496             // Start with getting the points for the arc.
    00497             const int arcPoints = static_cast<int>(trunc( angleLen / granularity ));
    00498             QPolygonF poly( arcPoints+2 );
    00499             qreal degree=0.0;
    00500             int iPoint = 0;
    00501             bool perfectMatch = false;
    00502
    00503             while ( degree <= angleLen ){
    00504                 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + degree );
    00505                 //qDebug() << degree << angleLen << poly[ iPoint ];
    00506                 perfectMatch = (degree == angleLen);
    00507                 degree += granularity;
    00508                 ++iPoint;
    00509             }
    00510             int last = poly.size();
    00511             // if necessary add one more point to fill the last small gap
    00512             if( ! perfectMatch ){
    00513                 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + angleLen );
    00514
    00515 //qDebug() << "adding" << poly[ iPoint ];
    00516                 // add the center point of the piece
    00517                 poly.append( drawPosition.center() );
    00518 //qDebug() << "center:" << poly[ ++iPoint ];
    00519             }else{
    00520                 poly[ iPoint ] = drawPosition.center();
    00521 //qDebug() << "center:" << poly[ iPoint ];
    00522             }
    00523 //qDebug() << "a";
    00524             //find the value and paint it
    00525             //fix value position
    00526             const qreal sum = valueTotals();
    00527             painter->drawPolygon( poly );
    00528
    00529             QLineF centerLine(  drawPosition.center(),
    00530                             QPointF( (poly[ last - 2].x() + poly.first().x())/2,
    00531                                      ( poly.first().y() + poly[last-2].y() )/2 ) );
    00532             QPointF valuePos( ( centerLine.x1() + centerLine.x2() )/2,
    00533                                   ( centerLine.y1() + centerLine.y2() )/2 ) ;
    00534
    00535             paintDataValueText( painter, index, valuePos, angleLen*sum / 360  );
    00536
    00537 //if( bHelp ){
    00538                             //painter->drawPolyline( collect );
    00539 //bHelp=false;
    00540 //}
    00541
    00542
    00543 /*
    00544             if ( regions ) {
    00545                 QPointArray hitregion;
    00546                 hitregion.makeArc( drawPosition.x(), drawPosition.y(),
    00547                         drawPosition.width(),
    00548                         drawPosition.height(),
    00549                         ( int ) startAngle, ( int ) angleLen );
    00550                 hitregion.resize( hitregion.size() + 1 );
    00551                 hitregion.setPoint( hitregion.size() - 1,
    00552                         drawPosition.center() );
    00553                 datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ),
    00554                                                 dataset,
    00555                                                 pie,
    00556                                                 chart );
    00557
    00558                 datReg->points[ KDChartEnums::PosTopLeft ]
    00559                     = pointOnCircle( drawPosition, startAngle + angleLen );
    00560                 datReg->points[ KDChartEnums::PosTopCenter ]
    00561                     = pointOnCircle( drawPosition, startAngle + angleLen / 2 );
    00562                 datReg->points[ KDChartEnums::PosTopRight ]
    00563                     = pointOnCircle( drawPosition, startAngle );
    00564
    00565                 datReg->points[   KDChartEnums::PosBottomLeft   ] = drawPosition.center();
    00566                 datReg->points[   KDChartEnums::PosBottomCenter ]
    00567                     = datReg->points[ KDChartEnums::PosBottomLeft   ];
    00568                 datReg->points[   KDChartEnums::PosBottomRight  ]
    00569                     = datReg->points[ KDChartEnums::PosBottomLeft   ];
    00570
    00571                 datReg->points[ KDChartEnums::PosCenterLeft ]
    00572                     = QPoint( (   datReg->points[ KDChartEnums::PosTopLeft      ].x()
    00573                                 + datReg->points[ KDChartEnums::PosBottomLeft   ].x() ) / 2,
    00574                             (   datReg->points[ KDChartEnums::PosTopLeft      ].y()
    00575                                 + datReg->points[ KDChartEnums::PosBottomLeft   ].y() ) / 2 );
    00576                 datReg->points[ KDChartEnums::PosCenter ]
    00577                     = QPoint( (   datReg->points[ KDChartEnums::PosTopCenter    ].x()
    00578                                 + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
    00579                             (   datReg->points[ KDChartEnums::PosTopCenter    ].y()
    00580                                 + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
    00581                 datReg->points[ KDChartEnums::PosCenterRight ]
    00582                     = QPoint( (   datReg->points[ KDChartEnums::PosTopRight     ].x()
    00583                                 + datReg->points[ KDChartEnums::PosBottomRight  ].x() ) / 2,
    00584                             (   datReg->points[ KDChartEnums::PosTopRight     ].y()
    00585                                 + datReg->points[ KDChartEnums::PosBottomRight  ].y() ) / 2 );
    00586
    00587                 datReg->startAngle = startAngle;
    00588                 datReg->angleLen   = angleLen;
    00589                 regions->append( datReg );
    00590             }
    00591 */
    00592         }
    00593 //        if( mustDeleteRegion )
    00594 //            delete region;
    00595     }
    00596 }
    00597
    00598
    00608 void PieDiagram::draw3DEffect( QPainter* painter,
    00609         const QRectF& drawPosition,
    00610         uint dataset, uint pie,
    00611         qreal granularity,
    00612         const ThreeDPieAttributes& threeDAttrs,
    00613         bool /*explode*/ )
    00614 {
    00615     if( ! threeDAttrs.isEnabled() )
    00616         return;
    00617
    00618     // NOTE: We cannot optimize away drawing some of the effects (even
    00619     // when not exploding), because some of the pies might be left out
    00620     // in future versions which would make some of the normally hidden
    00621     // pies visible. Complex hidden-line algorithms would be much more
    00622     // expensive than just drawing for nothing.
    00623
    00624     // No need to save the brush, will be changed on return from this
    00625     // method anyway.
    00626     if( threeDAttrs.useShadowColors() ){
    00627         const QPen pen = this->pen( model()->index( 0, pie, rootIndex() ) );
    00628         painter->setBrush( QBrush( pen.color() ) );
    00629     }
    00630     //painter->setBrush( QBrush( threeDAttrs.dataShadow1Color( pie ),
    00631     //            params()->shadowPattern() ) );
    00632
    00633     qreal startAngle = d->startAngles[ pie ];
    00634     qreal endAngle = startAngle + d->angleLens[ pie ];
    00635     // Normalize angles
    00636     while ( startAngle >= 360 )
    00637         startAngle -= 360;
    00638     while ( endAngle >= 360 )
    00639         endAngle -= 360;
    00640     Q_ASSERT( startAngle >= 0 && startAngle <= 360 );
    00641     Q_ASSERT( endAngle >= 0 && endAngle <= 360 );
    00642
    00643     //int centerY = drawPosition.center().y();
    00644
    00645     if ( startAngle == endAngle ||
    00646             startAngle == endAngle - 360 ) { // full circle
    00647         drawArcEffectSegment( painter, drawPosition,
    00648                 threeDAttrs.depth(),
    00649                 180, 360, granularity );
    00650     } else if ( startAngle <= 90 ) {
    00651         if ( endAngle <= 90 ) {
    00652             if ( startAngle <= endAngle ) {
    00654                 drawStraightEffectSegment( painter, drawPosition,
    00655                     threeDAttrs.depth(), startAngle );
    00656                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00657             } else {
    00659                 drawStraightEffectSegment( painter, drawPosition,
    00660                     threeDAttrs.depth(), startAngle );
    00661                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00662                 drawArcEffectSegment( painter, drawPosition,
    00663                     threeDAttrs.depth(),
    00664                     180, 360, granularity );
    00665             }
    00666         } else if ( endAngle <= 180 ) {
    00669             drawStraightEffectSegment( painter, drawPosition,
    00670                 threeDAttrs.depth(), startAngle );
    00671             drawStraightEffectSegment( painter, drawPosition,
    00672                 threeDAttrs.depth(), endAngle );
    00673         } else if ( endAngle <= 270 ) {
    00675             drawStraightEffectSegment( painter, drawPosition,
    00676                 threeDAttrs.depth(), startAngle );
    00677             drawStraightEffectSegment( painter, drawPosition,
    00678                 threeDAttrs.depth(), endAngle );
    00679             drawArcEffectSegment( painter, drawPosition,
    00680                 threeDAttrs.depth(),
    00681                 180, endAngle, granularity );
    00682         } else { // 270*16 < endAngle < 360*16
    00685             drawStraightEffectSegment( painter, drawPosition,
    00686                 threeDAttrs.depth(), startAngle );
    00687             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00688             drawArcEffectSegment( painter, drawPosition,
    00689                 threeDAttrs.depth(),
    00690                 180, endAngle, granularity );
    00691         }
    00692     } else if ( startAngle <= 180 ) {
    00693         if ( endAngle <= 90 ) {
    00694             drawArcEffectSegment( painter, drawPosition,
    00695                 threeDAttrs.depth(),
    00696                 180, 360, granularity );
    00697             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00698             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00699         } else if ( endAngle <= 180 ) {
    00700             if ( startAngle <= endAngle ) {
    00703                 drawStraightEffectSegment( painter, drawPosition,
    00704                     threeDAttrs.depth(), endAngle );
    00705                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00706             } else {
    00709                 drawStraightEffectSegment( painter, drawPosition,
    00710                     threeDAttrs.depth(), endAngle );
    00711                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00712                 drawArcEffectSegment( painter, drawPosition,
    00713                     threeDAttrs.depth(),
    00714                     180, 360, granularity );
    00715             }
    00716         } else if ( endAngle <= 270 ) {
    00717             drawStraightEffectSegment( painter, drawPosition,
    00718                 threeDAttrs.depth(), endAngle );
    00719             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00720             drawArcEffectSegment( painter, drawPosition,
    00721                 threeDAttrs.depth(),
    00722                 180, endAngle, granularity );
    00723         } else { // 270*16 < endAngle < 360*16
    00724             drawArcEffectSegment( painter, drawPosition,
    00725                 threeDAttrs.depth(),
    00726                 180, endAngle, granularity );
    00727             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00728             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00729         }
    00730     } else if ( startAngle <= 270 ) {
    00731         if ( endAngle <= 90 ) {
    00732             drawArcEffectSegment( painter, drawPosition,
    00733                 threeDAttrs.depth(),
    00734                 startAngle, 360, granularity );
    00735             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00736             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00737         } else if ( endAngle <= 180 ) {
    00738             drawStraightEffectSegment( painter, drawPosition,
    00739                 threeDAttrs.depth(), endAngle );
    00740             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00741             drawArcEffectSegment( painter, drawPosition,
    00742                 threeDAttrs.depth(),
    00743                 startAngle, 360, granularity );
    00744         } else if ( endAngle <= 270 ) {
    00745             if ( startAngle <= endAngle ) {
    00748                 drawStraightEffectSegment( painter, drawPosition,
    00749                     threeDAttrs.depth(), endAngle );
    00750                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00751                 drawArcEffectSegment( painter, drawPosition,
    00752                     threeDAttrs.depth(),
    00753                     startAngle, endAngle, granularity );
    00754             } else {
    00757                 drawStraightEffectSegment( painter, drawPosition,
    00758                     threeDAttrs.depth(), endAngle );
    00759                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00760                 drawArcEffectSegment( painter, drawPosition,
    00761                     threeDAttrs.depth(),
    00762                     180, endAngle, granularity );
    00763                 drawArcEffectSegment( painter, drawPosition,
    00764                     threeDAttrs.depth(),
    00765                     startAngle, 360, granularity );
    00766             }
    00767         } else { // 270*16 < endAngle < 360*16
    00768             drawArcEffectSegment( painter, drawPosition,
    00769                 threeDAttrs.depth(),
    00770                 startAngle, endAngle, granularity );
    00771             drawUpperBrinkEffect( painter, drawPosition, startAngle );
    00772             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00773         }
    00774     } else { // 270*16 < startAngle < 360*16
    00775         if ( endAngle <= 90 ) {
    00776             drawStraightEffectSegment( painter, drawPosition,
    00777                 threeDAttrs.depth(), startAngle );
    00778             drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00779             drawArcEffectSegment( painter, drawPosition,
    00780                 threeDAttrs.depth(),
    00781                 startAngle, 360, granularity );
    00782         } else if ( endAngle <= 180 ) {
    00783             drawStraightEffectSegment( painter, drawPosition,
    00784                 threeDAttrs.depth(), startAngle );
    00785             drawStraightEffectSegment( painter, drawPosition,
    00786                 threeDAttrs.depth(), endAngle );
    00787             drawArcEffectSegment( painter, drawPosition,
    00788                 threeDAttrs.depth(),
    00789                 startAngle, 360, granularity );
    00790         } else if ( endAngle <= 270 ) {
    00791             drawStraightEffectSegment( painter, drawPosition,
    00792                 threeDAttrs.depth(), startAngle );
    00793             drawStraightEffectSegment( painter, drawPosition,
    00794                 threeDAttrs.depth(), endAngle );
    00795             drawArcEffectSegment( painter, drawPosition,
    00796                 threeDAttrs.depth(),
    00797                 180, endAngle, granularity );
    00798             drawArcEffectSegment( painter, drawPosition,
    00799                 threeDAttrs.depth(),
    00800                 startAngle, 360, granularity );
    00801         } else { // 270*16 < endAngle < 360*16
    00802             if ( startAngle <= endAngle ) {
    00805                 drawStraightEffectSegment( painter, drawPosition,
    00806                     threeDAttrs.depth(), startAngle );
    00807                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00808                 drawArcEffectSegment( painter, drawPosition,
    00809                     threeDAttrs.depth(),
    00810                     startAngle, endAngle, granularity );
    00811             } else {
    00814                 drawStraightEffectSegment( painter, drawPosition,
    00815                     threeDAttrs.depth(), startAngle );
    00816                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
    00817                 drawArcEffectSegment( painter, drawPosition,
    00818                     threeDAttrs.depth(),
    00819                     startAngle, 360, granularity );
    00820                 drawArcEffectSegment( painter, drawPosition,
    00821                     threeDAttrs.depth(),
    00822                     180, endAngle, granularity );
    00823             }
    00824         }
    00825     }
    00826     drawArcUpperBrinkEffectSegment( painter, drawPosition, startAngle, endAngle, granularity );
    00827 }
    00828
    00829
    00838 void PieDiagram::drawStraightEffectSegment( QPainter* painter,
    00839         const QRectF& rect,
    00840         qreal threeDHeight,
    00841         qreal angle )
    00842 {
    00843     QPolygonF poly( 4 );
    00844     const QPointF center = rect.center();
    00845     const QPointF circlePoint = pointOnCircle( rect, angle );
    00846     poly[0] = center;
    00847     poly[1] = circlePoint;
    00848     poly[2] = QPointF( circlePoint.x(), circlePoint.y() + threeDHeight );
    00849     poly[3] = QPointF( center.x(), center.y() + threeDHeight );
    00850     painter->drawPolygon( poly );
    00851 //    if ( region )
    00852 //        *region += QRegion( points );
    00853 }
    00854
    00862 void PieDiagram::drawUpperBrinkEffect( QPainter* painter,
    00863         const QRectF& rect,
    00864         qreal angle )
    00865 {
    00866     const QPointF center = rect.center();
    00867     const QPointF circlePoint = pointOnCircle( rect, angle );
    00868     painter->drawLine( center, circlePoint );
    00869 }
    00870
    00880 void PieDiagram::drawArcEffectSegment( QPainter* painter,
    00881         const QRectF& rect,
    00882         qreal threeDHeight,
    00883         qreal startAngle,
    00884         qreal endAngle,
    00885         qreal granularity )
    00886 {
    00887     // Start with getting the points for the inner arc.
    00888     qreal startA = qMin( startAngle, endAngle );
    00889     qreal endA   = qMax( startAngle, endAngle );
    00890
    00891     // sometimes we have to draw two segments, which are on different sides of the pie
    00892     if( endA > 540 )
    00893         drawArcEffectSegment( painter, rect, threeDHeight, 180, endA - 360, granularity );
    00894     if( endA > 360 )
    00895         endA = qMin( endA, 360.0 );
    00896
    00897     int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
    00898
    00899     QPolygonF poly( numHalfPoints );
    00900
    00901     qreal degree = endA;
    00902     int iPoint = 0;
    00903     bool perfectMatch = false;
    00904     while ( degree >= startA ){
    00905         poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
    00906
    00907         perfectMatch = (degree == startA);
    00908         degree -= granularity;
    00909         ++iPoint;
    00910     }
    00911     // if necessary add one more point to fill the last small gap
    00912     if( ! perfectMatch ){
    00913         poly.prepend( pointOnCircle( rect, startA ) );
    00914         ++numHalfPoints;
    00915     }
    00916
    00917     poly.resize( numHalfPoints * 2 );
    00918
    00919     // Now copy these arcs again into the final array, but in the
    00920     // opposite direction and moved down by the 3D height.
    00921     for ( int i = numHalfPoints - 1; i >= 0; --i ) {
    00922         QPointF pointOnFirstArc( poly[ i ] );
    00923         pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
    00924         poly[ numHalfPoints * 2 - i - 1 ] = pointOnFirstArc;
    00925     }
    00926
    00927     painter->drawPolygon( poly );
    00928 //    if ( region )
    00929 //        *region += QRegion( collect );
    00930 }
    00931
    00940 void PieDiagram::drawArcUpperBrinkEffectSegment( QPainter* painter,
    00941         const QRectF& rect,
    00942         qreal startAngle,
    00943         qreal endAngle,
    00944         qreal granularity )
    00945 {
    00946     if ( endAngle < startAngle )
    00947         endAngle += 360;
    00948     // Start with getting the poits for the inner arc.
    00949     const qreal startA = qMin( startAngle, endAngle );
    00950     const qreal endA   = qMax( startAngle, endAngle );
    00951
    00952     int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
    00953
    00954     QPolygonF poly( numHalfPoints );
    00955
    00956     qreal degree = endA;
    00957     int iPoint = 0;
    00958     bool perfectMatch = false;
    00959     while ( degree >= startA ){
    00960         poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
    00961
    00962         perfectMatch = (degree == startA);
    00963         degree -= granularity;
    00964         ++iPoint;
    00965     }
    00966     // if necessary add one more point to fill the last small gap
    00967     if( ! perfectMatch ){
    00968         poly.prepend( pointOnCircle( rect, startA ) );
    00969         ++numHalfPoints;
    00970     }
    00971
    00972     painter->drawPolyline( poly );
    00973 //    if ( region )
    00974 //        *region += QRegion( collect );
    00975 }
    00976
    00984 uint PieDiagram::findPieAt( qreal angle, int colCount )
    00985 {
    00986     for ( int i = 0; i < colCount; ++i ) {
    00987         qreal endseg = d->startAngles[ i ] + d->angleLens[ i ];
    00988         if ( ( d->startAngles[ i ] <= angle ) &&
    00989                 ( endseg >= angle ) )
    00990             // found!
    00991             return i;
    00992     }
    00993
    00994     // If we have not found it, try wrap around
    00995     // but only if the current searched angle is < 360 degree
    00996     if ( angle < 360 )
    00997         return findPieAt( angle + 360, colCount );
    00998     // otherwise - what ever went wrong - we return 0
    00999     return 0;
    01000 }
    01001
    01002
    01010 uint PieDiagram::findLeftPie( uint pie, int colCount )
    01011 {
    01012     if ( pie == 0 )
    01013         if ( colCount > 1 )
    01014             return colCount - 1;
    01015         else
    01016             return 0;
    01017     else {
    01018         return pie - 1;
    01019     }
    01020 }
    01021
    01022
    01030 uint PieDiagram::findRightPie( uint pie, int colCount  )
    01031 {
    01032     int rightpie = pie + 1;
    01033     if ( rightpie == colCount )
    01034         rightpie = 0;
    01035     return rightpie;
    01036 }
    01037
    01038 /*
    01039 / **
    01040   This method is a specialization that returns a fallback legend text
    01041   appropriate for pies that do not have more than one dataset
    01042
    01043   This method is only used when automatic legends are used, because
    01044   manual and first-column legends do not need fallback texts.
    01045
    01046   \param uint dataset the dataset number for which to generate a
    01047   fallback text
    01048   \return the fallback text to use for describing the specified
    01049   dataset in the legend
    01050   * /
    01051 QString PieDiagram::fallbackLegendText( uint dataset ) const
    01052 {
    01053     return QObject::tr( "Item " ) + QString::number( dataset + 1 );
    01054 }
    01055
    01056
    01057 / **
    01058   This methods returns the number of elements to be shown in the
    01059   legend in case fallback texts are used.
    01060
    01061   This method is only used when automatic legends are used, because
    01062   manual and first-column legends do not need fallback texts.
    01063
    01064   \return the number of fallback texts to use
    01065   * /
    01066 uint PieDiagram::numLegendFallbackTexts( KDChartTableDataBase* data ) const
    01067 {
    01068     return data->usedCols();
    01069 }
    01070 */
    01071
    01076 QPointF PieDiagram::pointOnCircle( const QRectF& rect, qreal angle )
    01077 {
    01078     qreal angleRad = DEGTORAD( angle );
    01079     qreal cosAngle = cos( angleRad );
    01080     qreal sinAngle = -sin( angleRad );
    01081     qreal posX = cosAngle * rect.width() / 2.0;
    01082     qreal posY = sinAngle * rect.height() / 2.0;
    01083     return QPointF( posX + rect.center().x(),
    01084                     posY + rect.center().y() );
    01085
    01086 }
    01087
    01088 /*virtual*/
    01089 double PieDiagram::valueTotals() const
    01090 {
    01091     const int colCount = columnCount();
    01092     double total = 0.0;
    01093     for ( int j = 0; j < colCount; ++j ) {
    01094       total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toDouble());
    01095       //qDebug() << model()->data( model()->index( 0, j, rootIndex() ) ).toDouble();
    01096     }
    01097     return total;
    01098 }
    01099
    01100 /*virtual*/
    01101 double PieDiagram::numberOfValuesPerDataset() const
    01102 {
    01103     return model() ? model()->columnCount( rootIndex() ) : 0.0;
    01104 }
    01105
    01106 /*virtual*/
    01107 double PieDiagram::numberOfGridRings() const
    01108 {
    01109     return 1;
    01110 }