KDChartPieDiagram.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002  ** Copyright (C) 2007 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 
00069 PieDiagram * PieDiagram::clone() const
00070 {
00071     return new PieDiagram( new Private( *d ) );
00072 }
00073 
00074 const QPair<QPointF, QPointF> PieDiagram::calculateDataBoundaries () const
00075 {
00076     if ( !checkInvariants( true ) ) return QPair<QPointF, QPointF>( QPointF( 0, 0 ), QPointF( 0, 0 ) );
00077 
00078     const PieAttributes attrs( pieAttributes( model()->index( 0, 0, rootIndex() ) ) );
00079 
00080     QPointF bottomLeft ( QPointF( 0, 0 ) );
00081     QPointF topRight;
00082     // If we explode, we need extra space for the pie slice that has
00083     // the largest explosion distance.
00084     if ( attrs.explode() ) {
00085         const int colCount = columnCount();
00086         qreal maxExplode = 0.0;
00087         for( int j = 0; j < colCount; ++j ){
00088             const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
00089             maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
00090         }
00091         topRight = QPointF( 1.0+maxExplode, 1.0+maxExplode );
00092     }else{
00093         topRight = QPointF( 1.0, 1.0 );
00094     }
00095     return QPair<QPointF, QPointF> ( bottomLeft,  topRight );
00096 }
00097 
00098 
00099 void PieDiagram::paintEvent( QPaintEvent* )
00100 {
00101     QPainter painter ( viewport() );
00102     PaintContext ctx;
00103     ctx.setPainter ( &painter );
00104     ctx.setRectangle( QRectF ( 0, 0, width(), height() ) );
00105     paint ( &ctx );
00106 }
00107 
00108 void PieDiagram::resizeEvent ( QResizeEvent*)
00109 {
00110 }
00111 
00112 void PieDiagram::resize ( const QSizeF& )
00113 {
00114 }
00115 
00116 static QRectF buildReferenceRect( const PolarCoordinatePlane* plane )
00117 {
00118     QRectF contentsRect;
00119 //qDebug() << "..........................................";
00120     QPointF referencePointAtTop = plane->translate( QPointF( 1, 0 ) );
00121     QPointF temp = plane->translate( QPointF( 0, 0 ) ) - referencePointAtTop;
00122     const double offset = temp.y();
00123     referencePointAtTop.setX( referencePointAtTop.x() - offset );
00124     contentsRect.setTopLeft( referencePointAtTop );
00125     contentsRect.setBottomRight( referencePointAtTop + QPointF( 2*offset, 2*offset) );
00126 //qDebug() << contentsRect;
00127     return contentsRect;
00128 }
00129 /*
00130 void PieDiagram::paint( PaintContext* ctx )
00131 {
00132     if ( !checkInvariants(true) ) return;
00133     const int colCount = model()->columnCount(rootIndex());
00134     QRectF contentsRect = buildReferenceRect( polarCoordinatePlane() );
00135     DataValueTextInfoList list;
00136     double startAngle = startPosition();
00137     double startAngleValueSpace = valueTotals() / 360 * startAngle;
00138     for ( int j=0; j<colCount; ++j ) {
00139         const double nextValue = qAbs( model()->data( model()->index( 0, j,rootIndex() ) ).toDouble() );
00140         double spanAngle = polarCoordinatePlane()->translatePolar( QPointF( nextValue, 1 ) ).x();
00141         if ( spanAngle == 0 ) continue;
00142         QBrush brush = qVariantValue<QBrush>( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetBrushRole ) );
00143         QPen pen = qVariantValue<QPen>( attributesModel()->headerData( j, Qt::Vertical, KDChart::DatasetPenRole ) );
00144         PainterSaver painterSaver( ctx->painter() );
00145         ctx->painter()->setRenderHint ( QPainter::Antialiasing );
00146         ctx->painter()->setBrush( brush );
00147         ctx->painter()->setPen( pen );
00148 
00149         // Explosion support
00150         QRectF pieRect = contentsRect;
00151         if( explode() ) {
00152             QPointF oldCenter = contentsRect.center();
00153             QPointF newCenter = polarCoordinatePlane()->translate( QPointF( explodeFactor( j ),
00154                                                                             startAngleValueSpace + nextValue/2.0 ) );
00155             QPointF difference = newCenter - oldCenter;
00156             pieRect.translate( difference );
00157         }
00158 
00159         ctx->painter()->drawPie( pieRect, ( int ) ((-startAngle + 90 )), ( int ) (-spanAngle) );
00160         startAngle += spanAngle;
00161         startAngleValueSpace += nextValue;
00162     }
00163     DataValueTextInfoListIterator it( list );
00164     while ( it.hasNext() ) {
00165         const DataValueTextInfo& info = it.next();
00166         paintDataValueText( ctx->painter(), info.index, info.pos, info.value );
00167     }
00168 }
00169 */
00170 
00171 
00172 void PieDiagram::paint( PaintContext* ctx )
00173 {
00174     // note: Not having any data model assigned is no bug
00175     //       but we can not draw a diagram then either.
00176     if ( !checkInvariants(true) )
00177         return;
00178 
00179     const PieAttributes attrs( pieAttributes() );
00180     const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( model()->index( 0, 0, rootIndex() ) ) );
00181 
00182     const int colCount = columnCount();
00183 
00184     QRectF contentsRect( buildReferenceRect( polarCoordinatePlane() ) );
00185     contentsRect = ctx->rectangle();
00186 //    contentsRect = geometry();
00187 //qDebug() << contentsRect;
00188     if( contentsRect.isEmpty() )
00189         return;
00190 
00191     DataValueTextInfoList list;
00192     const qreal sum = valueTotals();
00193 
00194     if( sum == 0.0 ) //nothing to draw
00195         return;
00196 
00197     d->startAngles.resize( colCount );
00198     d->angleLens.resize( colCount );
00199 
00200     // compute position
00201     d->size = qMin( contentsRect.width(), contentsRect.height() ); // initial size
00202 
00203     // if the pies explode, we need to give them additional space =>
00204     // make the basic size smaller
00205     qreal maxExplode = 0.0;
00206     for( int j = 0; j < colCount; ++j ){
00207         const PieAttributes columnAttrs( pieAttributes( model()->index( 0, j, rootIndex() ) ) );
00208         maxExplode = qMax( maxExplode, columnAttrs.explodeFactor() );
00209     }
00210     d->size /= ( 1.0 + 2.0 * maxExplode );
00211 
00212 
00213     qreal sizeFor3DEffect = 0.0;
00214     if ( ! threeDAttrs.isEnabled() ) {
00215 
00216         qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
00217         qreal y = ( contentsRect.height() == d->size ) ? 0.0 : ( ( contentsRect.height() - d->size ) / 2.0 );
00218         d->position = QRectF( x, y, d->size, d->size );
00219         d->position.translate( contentsRect.left(), contentsRect.top() );
00220     } else {
00221         // threeD: width is the maximum possible width; height is 1/2 of that
00222         qreal x = ( contentsRect.width() == d->size ) ? 0.0 : ( ( contentsRect.width() - d->size ) / 2.0 );
00223         qreal height = d->size;
00224         // make sure that the height plus the threeDheight is not more than the
00225         // available size
00226         if ( threeDAttrs.depth() >= 0.0 ) {
00227             // positive pie height: absolute value
00228             sizeFor3DEffect = threeDAttrs.depth();
00229             height = d->size - sizeFor3DEffect;
00230         } else {
00231             // negative pie height: relative value
00232             sizeFor3DEffect = - threeDAttrs.depth() / 100.0 * height;
00233             height = d->size - sizeFor3DEffect;
00234         }
00235         qreal y = ( contentsRect.height() == height ) ? 0.0 : ( ( contentsRect.height() - height - sizeFor3DEffect ) / 2.0 );
00236 
00237         d->position = QRectF( contentsRect.left() + x, contentsRect.top() + y,
00238                 d->size, height );
00239         //  d->position.moveBy( contentsRect.left(), contentsRect.top() );
00240     }
00241 
00242     const PolarCoordinatePlane * plane = polarCoordinatePlane();
00243     const qreal sectorsPerValue = 360.0 / sum;
00244     qreal currentValue = plane ? plane->startPosition() : 0.0;
00245 
00246     bool atLeastOneValue = false; // guard against completely empty tables
00247     QVariant vValY;
00248     for ( int iColumn = 0; iColumn < colCount; ++iColumn ) {
00249         // is there anything at all at this column?
00250         bool bOK;
00251         const double cellValue = qAbs( model()->data( model()->index( 0, iColumn, rootIndex() ) )
00252             .toDouble( &bOK ) );
00253 
00254         if( bOK ){
00255             d->startAngles[ iColumn ] = currentValue;
00256             d->angleLens[ iColumn ] = cellValue * sectorsPerValue;
00257             atLeastOneValue = true;
00258         } else { // mark as non-existent
00259             d->angleLens[ iColumn ] = 0.0;
00260             if ( iColumn > 0.0 )
00261                 d->startAngles[ iColumn ] = d->startAngles[ iColumn - 1 ];
00262             else
00263                 d->startAngles[ iColumn ] = currentValue;
00264         }
00265         //qDebug() << "d->startAngles["<<iColumn<<"] == " << d->startAngles[ iColumn ]
00266         //         << " +  d->angleLens["<<iColumn<<"]" << d->angleLens[ iColumn ]
00267         //         << " = " << d->startAngles[ iColumn ]+d->angleLens[ iColumn ];
00268 
00269         currentValue = d->startAngles[ iColumn ] + d->angleLens[ iColumn ];
00270     }
00271 
00272     // If there was no value at all, bail out, to avoid endless loops
00273     // later on (e.g. in findPieAt()).
00274     if( ! atLeastOneValue )
00275         return;
00276 
00277 
00278     // Find the backmost pie which is at +90° and needs to be drawn
00279     // first
00280     int backmostpie = findPieAt( 90, colCount );
00281     // Find the frontmost pie (at -90°/+270°) that should be drawn last
00282     int frontmostpie = findPieAt( 270, colCount );
00283     // the right- and the leftmost (only needed in some special cases...)
00284     int rightmostpie = findPieAt( 0, colCount );
00285     int leftmostpie = findPieAt( 180, colCount );
00286 
00287 
00288     int currentLeftPie = backmostpie;
00289     int currentRightPie = backmostpie;
00290 
00291     drawOnePie( ctx->painter(), 0, backmostpie, granularity(), sizeFor3DEffect );
00292 
00293     if( backmostpie == frontmostpie )
00294     {
00295         if( backmostpie == leftmostpie )
00296             currentLeftPie = findLeftPie( currentLeftPie, colCount );
00297         if( backmostpie == rightmostpie )
00298             currentRightPie = findRightPie( currentRightPie, colCount );
00299     }
00300     while( currentLeftPie != frontmostpie )
00301     {
00302         if( currentLeftPie != backmostpie )
00303             drawOnePie( ctx->painter(), 0, currentLeftPie, granularity(), sizeFor3DEffect );
00304         currentLeftPie = findLeftPie( currentLeftPie, colCount );
00305     }
00306     while( currentRightPie != frontmostpie )
00307     {
00308         if( currentRightPie != backmostpie )
00309             drawOnePie( ctx->painter(), 0, currentRightPie, granularity(), sizeFor3DEffect );
00310         currentRightPie = findRightPie( currentRightPie, colCount );
00311     }
00312 
00313     // if the backmost pie is not the frontmost pie, we draw the frontmost at last
00314     if( backmostpie != frontmostpie || ! threeDPieAttributes().isEnabled() )
00315     {
00316         drawOnePie( ctx->painter(), 0, frontmostpie, granularity(), sizeFor3DEffect );
00317     // otherwise, this gets a bit more complicated...
00318 /*    } else if( threeDPieAttributes().isEnabled() ) {
00319         //drawPieSurface( ctx->painter(), 0, frontmostpie, granularity() );
00320         const QModelIndex index = model()->index( 0, frontmostpie, rootIndex() );
00321         QPen pen = this->pen( index );
00322         ctx->painter()->setBrush( brush( index ) );
00323         if ( threeDAttrs.isEnabled() )
00324             pen.setColor( QColor( 0, 0, 0 ) );
00325         ctx->painter()->setPen( pen );
00326 
00327         qreal startAngle = d->startAngles[ frontmostpie ];
00328         if( startAngle > 360 )
00329             startAngle -= 360;
00330 
00331         qreal endAngle = startAngle + d->angleLens[ frontmostpie ];
00332         startAngle = qMax( startAngle, 180.0 );
00333 
00334         drawArcEffectSegment( ctx->painter(), piePosition( 0, frontmostpie),
00335                 sizeFor3DEffect, startAngle, endAngle, granularity() );*/
00336     }
00337 }
00338 
00339 #if defined ( Q_WS_WIN)
00340 #define trunc(x) ((int)(x))
00341 #endif
00342 
00343 QRectF PieDiagram::piePosition( uint dataset, uint pie ) const
00344 {
00345     Q_UNUSED( dataset );
00346     qreal angleLen = d->angleLens[ pie ];
00347     qreal startAngle = d->startAngles[ pie ];
00348     QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00349     const PieAttributes attrs( pieAttributes( index ) );
00350     const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00351 
00352     QRectF drawPosition( d->position );
00353 
00354     if ( attrs.explode() ) {
00355         qreal explodeAngle = ( startAngle + angleLen / 2.0 );
00356         qreal explodeAngleRad = DEGTORAD( explodeAngle );
00357         qreal cosAngle = cos( explodeAngleRad );
00358         qreal sinAngle = -sin( explodeAngleRad );
00359         qreal explodeX = attrs.explodeFactor() * d->size * cosAngle;
00360         qreal explodeY = attrs.explodeFactor() * d->size * sinAngle;
00361         drawPosition.translate( explodeX, explodeY );
00362     }else{
00363         drawPosition = d->position;
00364     }
00365     return drawPosition;
00366  }
00367 
00376 void PieDiagram::drawOnePie( QPainter* painter,
00377         uint dataset, uint pie,
00378         qreal granularity,
00379         qreal threeDPieHeight )
00380 {
00381     Q_UNUSED( threeDPieHeight );
00382     // Is there anything to draw at all?
00383     const qreal angleLen = d->angleLens[ pie ];
00384     if ( angleLen ) {
00385         const QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00386         const PieAttributes attrs( pieAttributes( index ) );
00387         const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00388 
00389         const QRectF drawPosition = piePosition( dataset, pie );
00390 
00391         draw3DEffect( painter,
00392             drawPosition, dataset, pie,
00393             granularity,
00394             threeDAttrs,
00395             attrs.explode() );
00396 
00397         drawPieSurface( painter, dataset, pie, granularity );
00398     }
00399 }
00400 
00408 void PieDiagram::drawPieSurface( QPainter* painter,
00409         uint dataset, uint pie,
00410         qreal granularity )
00411 {
00412     // Is there anything to draw at all?
00413     qreal angleLen = d->angleLens[ pie ];
00414     if ( angleLen ) {
00415         qreal startAngle = d->startAngles[ pie ];
00416         
00417         QModelIndex index( model()->index( 0, pie, rootIndex() ) );
00418         const PieAttributes attrs( pieAttributes( index ) );
00419         const ThreeDPieAttributes threeDAttrs( threeDPieAttributes( index ) );
00420 
00421         QRectF drawPosition = piePosition( dataset, pie );
00422 
00423         QPen pen = this->pen( index );
00424         painter->setRenderHint ( QPainter::Antialiasing );
00425         painter->setBrush( brush( index ) );
00426 //        if ( threeDAttrs.isEnabled() )
00427 //            pen.setColor( QColor( 0, 0, 0 ) );
00428 //        painter->setPen( pen );
00429 
00430         if ( angleLen == 360 ) {
00431             // full circle, avoid nasty line in the middle
00432             painter->drawEllipse( drawPosition );
00433         } else {
00434             // draw the top of this piece
00435             // Start with getting the points for the arc.
00436             const int arcPoints = static_cast<int>(trunc( angleLen / granularity ));
00437             QPolygonF poly( arcPoints+2 );
00438             qreal degree=0.0;
00439             int iPoint = 0;
00440             bool perfectMatch = false;
00441 
00442             while ( degree <= angleLen ){
00443                 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + degree );
00444                 //qDebug() << degree << angleLen << poly[ iPoint ];
00445                 perfectMatch = (degree == angleLen);
00446                 degree += granularity;
00447                 ++iPoint;
00448             }
00449             int last = poly.size();
00450             // if necessary add one more point to fill the last small gap
00451             if( ! perfectMatch ){
00452                 poly[ iPoint ] = pointOnCircle( drawPosition, startAngle + angleLen );
00453 
00454                 // add the center point of the piece
00455                 poly.append( drawPosition.center() );
00456             }else{
00457                 poly[ iPoint ] = drawPosition.center();
00458             }
00459             //find the value and paint it
00460             //fix value position
00461             const qreal sum = valueTotals();
00462             painter->drawPolygon( poly );
00463 
00464             QLineF centerLine(  drawPosition.center(),
00465                             QPointF( (poly[ last - 2].x() + poly.first().x())/2,
00466                                      ( poly.first().y() + poly[last-2].y() )/2 ) );
00467             QPointF valuePos( ( centerLine.x1() + centerLine.x2() )/2,
00468                                   ( centerLine.y1() + centerLine.y2() )/2 ) ;
00469 
00470             paintDataValueText( painter, index, valuePos, angleLen*sum / 360  );
00471 
00472         }
00473     }
00474 }
00475 
00476 
00486 void PieDiagram::draw3DEffect( QPainter* painter,
00487         const QRectF& drawPosition,
00488         uint dataset, uint pie,
00489         qreal granularity,
00490         const ThreeDPieAttributes& threeDAttrs,
00491         bool /*explode*/ )
00492 {
00493     Q_UNUSED( dataset );
00494 
00495     if( ! threeDAttrs.isEnabled() )
00496         return;
00497 
00498     // NOTE: We cannot optimize away drawing some of the effects (even
00499     // when not exploding), because some of the pies might be left out
00500     // in future versions which would make some of the normally hidden
00501     // pies visible. Complex hidden-line algorithms would be much more
00502     // expensive than just drawing for nothing.
00503 
00504     // No need to save the brush, will be changed on return from this
00505     // method anyway.
00506     if( threeDAttrs.useShadowColors() ){
00507         const QPen pen = this->pen( model()->index( 0, pie, rootIndex() ) );
00508         painter->setBrush( QBrush( pen.color() ) );
00509     }
00510     //painter->setBrush( QBrush( threeDAttrs.dataShadow1Color( pie ),
00511     //            params()->shadowPattern() ) );
00512 
00513     qreal startAngle = d->startAngles[ pie ];
00514     qreal endAngle = startAngle + d->angleLens[ pie ];
00515     // Normalize angles
00516     while ( startAngle >= 360 )
00517         startAngle -= 360;
00518     while ( endAngle >= 360 )
00519         endAngle -= 360;
00520     Q_ASSERT( startAngle >= 0 && startAngle <= 360 );
00521     Q_ASSERT( endAngle >= 0 && endAngle <= 360 );
00522 
00523     //int centerY = drawPosition.center().y();
00524 
00525     if ( startAngle == endAngle ||
00526             startAngle == endAngle - 360 ) { // full circle
00527         drawArcEffectSegment( painter, drawPosition,
00528                 threeDAttrs.depth(),
00529                 180, 360, granularity );
00530     } else if ( startAngle <= 90 ) {
00531         if ( endAngle <= 90 ) {
00532             if ( startAngle <= endAngle ) {
00534                 drawStraightEffectSegment( painter, drawPosition,
00535                     threeDAttrs.depth(), startAngle );
00536                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00537             } else {
00539                 drawStraightEffectSegment( painter, drawPosition,
00540                     threeDAttrs.depth(), startAngle );
00541                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00542                 drawArcEffectSegment( painter, drawPosition,
00543                     threeDAttrs.depth(),
00544                     180, 360, granularity );
00545             }
00546         } else if ( endAngle <= 180 ) {
00549             drawStraightEffectSegment( painter, drawPosition,
00550                 threeDAttrs.depth(), startAngle );
00551             drawStraightEffectSegment( painter, drawPosition,
00552                 threeDAttrs.depth(), endAngle );
00553         } else if ( endAngle <= 270 ) {
00555             drawStraightEffectSegment( painter, drawPosition,
00556                 threeDAttrs.depth(), startAngle );
00557             drawStraightEffectSegment( painter, drawPosition,
00558                 threeDAttrs.depth(), endAngle );
00559             drawArcEffectSegment( painter, drawPosition,
00560                 threeDAttrs.depth(),
00561                 180, endAngle, granularity );
00562         } else { // 270*16 < endAngle < 360*16
00565             drawStraightEffectSegment( painter, drawPosition,
00566                 threeDAttrs.depth(), startAngle );
00567             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00568             drawArcEffectSegment( painter, drawPosition,
00569                 threeDAttrs.depth(),
00570                 180, endAngle, granularity );
00571         }
00572     } else if ( startAngle <= 180 ) {
00573         if ( endAngle <= 90 ) {
00574             drawArcEffectSegment( painter, drawPosition,
00575                 threeDAttrs.depth(),
00576                 180, 360, granularity );
00577             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00578             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00579         } else if ( endAngle <= 180 ) {
00580             if ( startAngle <= endAngle ) {
00583                 drawStraightEffectSegment( painter, drawPosition,
00584                     threeDAttrs.depth(), endAngle );
00585                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00586             } else {
00589                 drawStraightEffectSegment( painter, drawPosition,
00590                     threeDAttrs.depth(), endAngle );
00591                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00592                 drawArcEffectSegment( painter, drawPosition,
00593                     threeDAttrs.depth(),
00594                     180, 360, granularity );
00595             }
00596         } else if ( endAngle <= 270 ) {
00597             drawStraightEffectSegment( painter, drawPosition,
00598                 threeDAttrs.depth(), endAngle );
00599             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00600             drawArcEffectSegment( painter, drawPosition,
00601                 threeDAttrs.depth(),
00602                 180, endAngle, granularity );
00603         } else { // 270*16 < endAngle < 360*16
00604             drawArcEffectSegment( painter, drawPosition,
00605                 threeDAttrs.depth(),
00606                 180, endAngle, granularity );
00607             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00608             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00609         }
00610     } else if ( startAngle <= 270 ) {
00611         if ( endAngle <= 90 ) {
00612             drawArcEffectSegment( painter, drawPosition,
00613                 threeDAttrs.depth(),
00614                 startAngle, 360, granularity );
00615             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00616             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00617         } else if ( endAngle <= 180 ) {
00618             drawStraightEffectSegment( painter, drawPosition,
00619                 threeDAttrs.depth(), endAngle );
00620             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00621             drawArcEffectSegment( painter, drawPosition,
00622                 threeDAttrs.depth(),
00623                 startAngle, 360, granularity );
00624         } else if ( endAngle <= 270 ) {
00625             if ( startAngle <= endAngle ) {
00628                 drawStraightEffectSegment( painter, drawPosition,
00629                     threeDAttrs.depth(), endAngle );
00630                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00631                 drawArcEffectSegment( painter, drawPosition,
00632                     threeDAttrs.depth(),
00633                     startAngle, endAngle, granularity );
00634             } else {
00637                 drawStraightEffectSegment( painter, drawPosition,
00638                     threeDAttrs.depth(), endAngle );
00639                 drawUpperBrinkEffect( painter, drawPosition, startAngle );
00640                 drawArcEffectSegment( painter, drawPosition,
00641                     threeDAttrs.depth(),
00642                     180, endAngle, granularity );
00643                 drawArcEffectSegment( painter, drawPosition,
00644                     threeDAttrs.depth(),
00645                     startAngle, 360, granularity );
00646             }
00647         } else { // 270*16 < endAngle < 360*16
00648             drawArcEffectSegment( painter, drawPosition,
00649                 threeDAttrs.depth(),
00650                 startAngle, endAngle, granularity );
00651             drawUpperBrinkEffect( painter, drawPosition, startAngle );
00652             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00653         }
00654     } else { // 270*16 < startAngle < 360*16
00655         if ( endAngle <= 90 ) {
00656             drawStraightEffectSegment( painter, drawPosition,
00657                 threeDAttrs.depth(), startAngle );
00658             drawUpperBrinkEffect( painter, drawPosition, endAngle );
00659             drawArcEffectSegment( painter, drawPosition,
00660                 threeDAttrs.depth(),
00661                 startAngle, 360, granularity );
00662         } else if ( endAngle <= 180 ) {
00663             drawStraightEffectSegment( painter, drawPosition,
00664                 threeDAttrs.depth(), startAngle );
00665             drawStraightEffectSegment( painter, drawPosition,
00666                 threeDAttrs.depth(), endAngle );
00667             drawArcEffectSegment( painter, drawPosition,
00668                 threeDAttrs.depth(),
00669                 startAngle, 360, granularity );
00670         } else if ( endAngle <= 270 ) {
00671             drawStraightEffectSegment( painter, drawPosition,
00672                 threeDAttrs.depth(), startAngle );
00673             drawStraightEffectSegment( painter, drawPosition,
00674                 threeDAttrs.depth(), endAngle );
00675             drawArcEffectSegment( painter, drawPosition,
00676                 threeDAttrs.depth(),
00677                 180, endAngle, granularity );
00678             drawArcEffectSegment( painter, drawPosition,
00679                 threeDAttrs.depth(),
00680                 startAngle, 360, granularity );
00681         } else { // 270*16 < endAngle < 360*16
00682             if ( startAngle <= endAngle ) {
00685                 drawStraightEffectSegment( painter, drawPosition,
00686                     threeDAttrs.depth(), startAngle );
00687                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00688                 drawArcEffectSegment( painter, drawPosition,
00689                     threeDAttrs.depth(),
00690                     startAngle, endAngle, granularity );
00691             } else {
00694                 drawStraightEffectSegment( painter, drawPosition,
00695                     threeDAttrs.depth(), startAngle );
00696                 drawUpperBrinkEffect( painter, drawPosition, endAngle );
00697                 drawArcEffectSegment( painter, drawPosition,
00698                     threeDAttrs.depth(),
00699                     startAngle, 360, granularity );
00700                 drawArcEffectSegment( painter, drawPosition,
00701                     threeDAttrs.depth(),
00702                     180, endAngle, granularity );
00703             }
00704         }
00705     }
00706     drawArcUpperBrinkEffectSegment( painter, drawPosition, startAngle, endAngle, granularity );
00707 }
00708 
00709 
00718 void PieDiagram::drawStraightEffectSegment( QPainter* painter,
00719         const QRectF& rect,
00720         qreal threeDHeight,
00721         qreal angle )
00722 {
00723     QPolygonF poly( 4 );
00724     const QPointF center = rect.center();
00725     const QPointF circlePoint = pointOnCircle( rect, angle );
00726     poly[0] = center;
00727     poly[1] = circlePoint;
00728     poly[2] = QPointF( circlePoint.x(), circlePoint.y() + threeDHeight );
00729     poly[3] = QPointF( center.x(), center.y() + threeDHeight );
00730     painter->drawPolygon( poly );
00731 //    if ( region )
00732 //        *region += QRegion( points );
00733 }
00734 
00742 void PieDiagram::drawUpperBrinkEffect( QPainter* painter,
00743         const QRectF& rect,
00744         qreal angle )
00745 {
00746     const QPointF center = rect.center();
00747     const QPointF circlePoint = pointOnCircle( rect, angle );
00748     painter->drawLine( center, circlePoint );
00749 }
00750 
00760 void PieDiagram::drawArcEffectSegment( QPainter* painter,
00761         const QRectF& rect,
00762         qreal threeDHeight,
00763         qreal startAngle,
00764         qreal endAngle,
00765         qreal granularity )
00766 {
00767     // Start with getting the points for the inner arc.
00768     qreal startA = qMin( startAngle, endAngle );
00769     qreal endA   = qMax( startAngle, endAngle );
00770 
00771     // sometimes we have to draw two segments, which are on different sides of the pie
00772     if( endA > 540 )
00773         drawArcEffectSegment( painter, rect, threeDHeight, 180, endA - 360, granularity );
00774     if( endA > 360 )
00775         endA = qMin( endA, 360.0 );
00776 
00777     int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
00778 
00779     QPolygonF poly( numHalfPoints );
00780 
00781     qreal degree = endA;
00782     int iPoint = 0;
00783     bool perfectMatch = false;
00784     while ( degree >= startA ){
00785         poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
00786 
00787         perfectMatch = (degree == startA);
00788         degree -= granularity;
00789         ++iPoint;
00790     }
00791     // if necessary add one more point to fill the last small gap
00792     if( ! perfectMatch ){
00793         poly.prepend( pointOnCircle( rect, startA ) );
00794         ++numHalfPoints;
00795     }
00796 
00797     poly.resize( numHalfPoints * 2 );
00798 
00799     // Now copy these arcs again into the final array, but in the
00800     // opposite direction and moved down by the 3D height.
00801     for ( int i = numHalfPoints - 1; i >= 0; --i ) {
00802         QPointF pointOnFirstArc( poly[ i ] );
00803         pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
00804         poly[ numHalfPoints * 2 - i - 1 ] = pointOnFirstArc;
00805     }
00806 
00807     painter->drawPolygon( poly );
00808 //    if ( region )
00809 //        *region += QRegion( collect );
00810 }
00811 
00820 void PieDiagram::drawArcUpperBrinkEffectSegment( QPainter* painter,
00821         const QRectF& rect,
00822         qreal startAngle,
00823         qreal endAngle,
00824         qreal granularity )
00825 {
00826     if ( endAngle < startAngle )
00827         endAngle += 360;
00828     // Start with getting the poits for the inner arc.
00829     const qreal startA = qMin( startAngle, endAngle );
00830     const qreal endA   = qMax( startAngle, endAngle );
00831 
00832     int numHalfPoints = static_cast<int>( trunc( ( endA - startA ) / granularity ) ) + 1;
00833 
00834     QPolygonF poly( numHalfPoints );
00835 
00836     qreal degree = endA;
00837     int iPoint = 0;
00838     bool perfectMatch = false;
00839     while ( degree >= startA ){
00840         poly[ numHalfPoints - iPoint - 1 ] = pointOnCircle( rect, degree );
00841 
00842         perfectMatch = (degree == startA);
00843         degree -= granularity;
00844         ++iPoint;
00845     }
00846     // if necessary add one more point to fill the last small gap
00847     if( ! perfectMatch ){
00848         poly.prepend( pointOnCircle( rect, startA ) );
00849         ++numHalfPoints;
00850     }
00851 
00852     painter->drawPolyline( poly );
00853 //    if ( region )
00854 //        *region += QRegion( collect );
00855 }
00856 
00864 uint PieDiagram::findPieAt( qreal angle, int colCount )
00865 {
00866     for ( int i = 0; i < colCount; ++i ) {
00867         qreal endseg = d->startAngles[ i ] + d->angleLens[ i ];
00868         if ( ( d->startAngles[ i ] <= angle ) &&
00869                 ( endseg >= angle ) )
00870             // found!
00871             return i;
00872     }
00873 
00874     // If we have not found it, try wrap around
00875     // but only if the current searched angle is < 360 degree
00876     if ( angle < 360 )
00877         return findPieAt( angle + 360, colCount );
00878     // otherwise - what ever went wrong - we return 0
00879     return 0;
00880 }
00881 
00882 
00890 uint PieDiagram::findLeftPie( uint pie, int colCount )
00891 {
00892     if ( pie == 0 )
00893         if ( colCount > 1 )
00894             return colCount - 1;
00895         else
00896             return 0;
00897     else {
00898         return pie - 1;
00899     }
00900 }
00901 
00902 
00910 uint PieDiagram::findRightPie( uint pie, int colCount  )
00911 {
00912     int rightpie = pie + 1;
00913     if ( rightpie == colCount )
00914         rightpie = 0;
00915     return rightpie;
00916 }
00917 
00918 /*
00919 / **
00920   This method is a specialization that returns a fallback legend text
00921   appropriate for pies that do not have more than one dataset
00922 
00923   This method is only used when automatic legends are used, because
00924   manual and first-column legends do not need fallback texts.
00925 
00926   \param uint dataset the dataset number for which to generate a
00927   fallback text
00928   \return the fallback text to use for describing the specified
00929   dataset in the legend
00930   * /
00931 QString PieDiagram::fallbackLegendText( uint dataset ) const
00932 {
00933     return QObject::tr( "Item " ) + QString::number( dataset + 1 );
00934 }
00935 
00936 
00937 / **
00938   This methods returns the number of elements to be shown in the
00939   legend in case fallback texts are used.
00940 
00941   This method is only used when automatic legends are used, because
00942   manual and first-column legends do not need fallback texts.
00943 
00944   \return the number of fallback texts to use
00945   * /
00946 uint PieDiagram::numLegendFallbackTexts( KDChartTableDataBase* data ) const
00947 {
00948     return data->usedCols();
00949 }
00950 */
00951 
00956 QPointF PieDiagram::pointOnCircle( const QRectF& rect, qreal angle )
00957 {
00958     qreal angleRad = DEGTORAD( angle );
00959     qreal cosAngle = cos( angleRad );
00960     qreal sinAngle = -sin( angleRad );
00961     qreal posX = cosAngle * rect.width() / 2.0;
00962     qreal posY = sinAngle * rect.height() / 2.0;
00963     return QPointF( posX + rect.center().x(),
00964                     posY + rect.center().y() );
00965 
00966 }
00967 
00968 /*virtual*/
00969 double PieDiagram::valueTotals() const
00970 {
00971     const int colCount = columnCount();
00972     double total = 0.0;
00973     for ( int j = 0; j < colCount; ++j ) {
00974       total += qAbs(model()->data( model()->index( 0, j, rootIndex() ) ).toDouble());
00975       //qDebug() << model()->data( model()->index( 0, j, rootIndex() ) ).toDouble();
00976     }
00977     return total;
00978 }
00979 
00980 /*virtual*/
00981 double PieDiagram::numberOfValuesPerDataset() const
00982 {
00983     return model() ? model()->columnCount( rootIndex() ) : 0.0;
00984 }
00985 
00986 /*virtual*/
00987 double PieDiagram::numberOfGridRings() const
00988 {
00989     return 1;
00990 }

Generated on Mon Sep 17 16:16:50 2007 for KD Chart 2 by  doxygen 1.5.1