Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

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 }

Generated on Thu May 10 11:06:25 2007 for KD Chart 2 by doxygen 1.3.6