kchart

KDChartPiePainter.cpp

00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KDChart library.
00009  **
00010  ** This file may be distributed and/or modified under the terms of the
00011  ** GNU General Public License version 2 as published by the Free Software
00012  ** Foundation and appearing in the file LICENSE.GPL included in the
00013  ** packaging of this file.
00014  **
00015  ** Licensees holding valid commercial KDChart licenses may use this file in
00016  ** accordance with the KDChart Commercial License Agreement provided with
00017  ** the Software.
00018  **
00019  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021  **
00022  ** See http://www.klaralvdalens-datakonsult.se/?page=products for
00023  **   information about KDChart Commercial License Agreements.
00024  **
00025  ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 #include "KDChartEnums.h"
00030 #include "KDChartPiePainter.h"
00031 #include "KDChartParams.h"
00032 
00033 #include <qpainter.h>
00034 #include <qvaluestack.h>
00035 #include <qmessagebox.h>
00036 
00037 #define DEGTORAD(d) (d)*M_PI/180
00038 
00039 #include <math.h>
00040 
00054     KDChartPiePainter::KDChartPiePainter( KDChartParams* params ) :
00055 KDChartPainter( params )
00056 {
00057     // This constructor intentionally left blank so far; we cannot setup the
00058     // geometry yet since we do not know the size of the painter.
00059 }
00060 
00061 
00065 KDChartPiePainter::~KDChartPiePainter()
00066 {
00067     // intentionally left blank
00068 }
00069 
00070 //static bool bHelp=true;
00071 
00081 void KDChartPiePainter::paintData( QPainter* painter,
00082         KDChartTableDataBase* data,
00083         bool paint2nd,
00084         KDChartDataRegionList* regions )
00085 {
00086 //bHelp=true;
00087     uint chart = paint2nd ? 1 : 0;
00088 
00089     QRect ourClipRect( _dataRect );
00090     ourClipRect.addCoords( -1,-1,1,1 );
00091 
00092     const QWMatrix & world = painter->worldMatrix();
00093     ourClipRect =
00094 #if COMPAT_QT_VERSION >= 0x030000
00095         world.mapRect( ourClipRect );
00096 #else
00097     world.map( ourClipRect );
00098 #endif
00099 
00100     painter->setClipRect( ourClipRect );
00101 
00102     // find which dataset to paint
00103     uint dataset;
00104     if ( !params()->findDataset( KDChartParams::DataEntry
00105                 ,
00106                 dataset, dataset ) ) {
00107         return ; // nothing to draw
00108     }
00109 
00110     if ( dataset == KDCHART_ALL_DATASETS )
00111         // setChartSourceMode() has not been used (or all datasets have been
00112         // configured to be used); use the first dataset by
00113         // default
00114         dataset = 0;
00115 
00116 
00117     // Number of values: If -1, use all values, otherwise use the
00118     // specified number of values.
00119     if ( params()->numValues() != -1 )
00120         _numValues = params()->numValues();
00121     else
00122         _numValues = data->usedCols();
00123 
00124     _startAngles.resize( _numValues );
00125     _angleLens.resize( _numValues );
00126 
00127     // compute position
00128     _size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size
00129     // if the pies explode, we need to give them additional space =>
00130     // make the basic size smaller
00131     if ( params()->explode() ) {
00132         double doubleSize = ( double ) _size;
00133         doubleSize /= ( 1.0 + params()->explodeFactor() * 2 );
00134         _size = ( int ) doubleSize;
00135     }
00136 
00137     int sizeFor3DEffect = 0;
00138     if ( !params()->threeDPies() ) {
00139 
00140         int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
00141         int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 );
00142         _position = QRect( x, y, _size, _size );
00143         _position.moveBy( _dataRect.left(), _dataRect.top() );
00144     } else {
00145         // threeD: width is the maximum possible width; height is 1/2 of that
00146         int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
00147         int height = _size;
00148         // make sure that the height plus the threeDheight is not more than the
00149         // available size
00150         if ( params()->threeDPieHeight() >= 0 ) {
00151             // positive pie height: absolute value
00152             sizeFor3DEffect = params()->threeDPieHeight();
00153             height = _size - sizeFor3DEffect;
00154         } else {
00155             // negative pie height: relative value
00156             sizeFor3DEffect = -( int ) ( ( ( double ) params()->threeDPieHeight() / 100.0 ) * ( double ) height );
00157             height = _size - sizeFor3DEffect;
00158         }
00159         int y = ( _dataRect.height() == height ) ? 0 : ( ( _dataRect.height() - height - sizeFor3DEffect ) / 2 );
00160 
00161         _position = QRect( _dataRect.left() + x, _dataRect.top() + y,
00162                 _size, height );
00163         //  _position.moveBy( _dataRect.left(), _dataRect.top() );
00164     }
00165 
00166     double sum = data->rowAbsSum( dataset );
00167     if( sum==0 ) //nothing to draw
00168         return;
00169     double sectorsPerValue = 5760.0 / sum; // 5760 == 16*360, number of sections in Qt circle
00170 
00171     int currentValue = params()->pieStart() * 16;
00172     bool atLeastOneValue = false; // guard against completely empty tables
00173     QVariant vValY;
00174     for ( int value = 0; value < _numValues; value++ ) {
00175         // is there anything at all at this value
00176         /* see above for meaning of 16 */
00177 
00178         if( data->cellCoord( dataset, value, vValY, 1 ) &&
00179             QVariant::Double == vValY.type() ){
00180             _startAngles[ value ] = currentValue;
00181             const double cellValue = fabs( vValY.toDouble() );
00182             _angleLens[ value ] = ( int ) floor( cellValue * sectorsPerValue + 0.5 );
00183             atLeastOneValue = true;
00184         } else { // mark as non-existent
00185             _angleLens[ value ] = 0;
00186             if ( value > 0 )
00187                 _startAngles[ value ] = _startAngles[ value - 1 ];
00188             else
00189                 _startAngles[ value ] = currentValue;
00190         }
00191 
00192         currentValue = _startAngles[ value ] + _angleLens[ value ];
00193     }
00194 
00195     // If there was no value at all, bail out, to avoid endless loops
00196     // later on (e.g. in findPieAt()).
00197     if( !atLeastOneValue )
00198         return;
00199 
00200 
00201     // Find the backmost pie which is at +90° and needs to be drawn
00202     // first
00203     int backmostpie = findPieAt( 90 * 16 );
00204     // Find the frontmost pie (at -90°/+270°) that should be drawn last
00205     int frontmostpie = findPieAt( 270 * 16 );
00206     // and put the backmost pie on the TODO stack to initialize it,
00207     // but only if it is not the frontmostpie
00208     QValueStack < int > todostack;
00209     if ( backmostpie != frontmostpie )
00210         todostack.push( backmostpie );
00211     else {
00212         // Otherwise, try to find something else
00213         int leftOfCurrent = findLeftPie( backmostpie );
00214         if ( leftOfCurrent != frontmostpie ) {
00215             todostack.push( leftOfCurrent );
00216         } else {
00217             int rightOfCurrent = findRightPie( backmostpie );
00218             if ( rightOfCurrent != frontmostpie ) {
00219                 todostack.push( rightOfCurrent );
00220             }
00221         }
00222         // If we get here, there was nothing else, and we will bail
00223         // out of the while loop below.
00224     }
00225 
00226     // The list with pies that have already been drawn
00227 
00228     QValueList < int > donelist;
00229 
00230     // Draw pies until the todostack is empty or only the frontmost
00231     // pie is there
00232     while ( !todostack.isEmpty() &&
00233             !( ( todostack.count() == 1 ) &&
00234                 ( ( todostack.top() == frontmostpie ) ) ) ) {
00235         // The while loop cannot be cancelled if frontmostpie is on
00236         // top of the stack, but this is also backmostpie (can happen
00237         // when one of the pies covers more than 1/2 of the circle. In
00238         // this case, we need to find something else to put on the
00239         // stack to get things going.
00240 
00241         // take one pie from the stack
00242         int currentpie = todostack.pop();
00243         // if this pie was already drawn, ignore it
00244         if ( donelist.find( currentpie ) != donelist.end() )
00245             continue;
00246 
00247         // If this pie is the frontmost pie, put it back, but at the
00248         // second position (otherwise, there would be an endless
00249         // loop). If this pie is the frontmost pie, there must be at
00250         // least one other pie, otherwise the loop would already have
00251         // been terminated by the loop condition.
00252         if ( currentpie == frontmostpie ) {
00253             Q_ASSERT( !todostack.isEmpty() );
00254             // QValueStack::exchange() would be nice here...
00255             int secondpie = todostack.pop();
00256             if ( currentpie == secondpie )
00257                 // no need to have the second pie twice on the stack,
00258                 // forget about one instance and take the third
00259                 // instead
00260                 if ( todostack.isEmpty() )
00261                     break; // done anyway
00262                 else
00263                     secondpie = todostack.pop();
00264             todostack.push( currentpie );
00265             todostack.push( secondpie );
00266             continue;
00267         }
00268 
00269         // When we get here, we can just draw the pie and proceed.
00270         drawOnePie( painter, data, dataset, currentpie, chart,
00271                 sizeFor3DEffect,
00272                 regions );
00273 
00274         // Mark the pie just drawn as done.
00275         donelist.append( currentpie );
00276 
00277         // Now take the pie to the left and to the right, check
00278         // whether these have not been painted already, and put them
00279         // on the stack.
00280         int leftOfCurrent = findLeftPie( currentpie );
00281         if ( donelist.find( leftOfCurrent ) == donelist.end() )
00282             todostack.push( leftOfCurrent );
00283         int rightOfCurrent = findRightPie( currentpie );
00284         if ( donelist.find( rightOfCurrent ) == donelist.end() )
00285             todostack.push( rightOfCurrent );
00286     }
00287 
00288     // now only the frontmost pie is left to draw
00289     drawOnePie( painter, data, dataset, frontmostpie, chart,
00290             sizeFor3DEffect,
00291             regions );
00292 }
00293 
00294 
00305 void KDChartPiePainter::drawOnePie( QPainter* painter,
00306         KDChartTableDataBase* /*data*/,
00307         uint dataset, uint pie, uint chart,
00308         uint threeDPieHeight,
00309         KDChartDataRegionList* regions )
00310 {
00311     // Is there anything to draw at all?
00312     int angleLen = _angleLens[ ( int ) pie ];
00313     if ( angleLen ) {
00314         int startAngle = _startAngles[ ( int ) pie ];
00315 
00316         KDChartDataRegion* datReg = 0;
00317         QRegion* region = 0;
00318         bool mustDeleteRegion = false;
00319         if ( regions ){
00320             region = new QRegion();
00321             mustDeleteRegion = true;
00322         }
00323 
00324         QRect drawPosition = _position;
00325         if ( params()->explode() ) {
00326             // need to compute a new position for each or some of the pie
00327             QValueList<int> explodeList = params()->explodeValues();
00328             if( explodeList.count() == 0 || // nothing on list, explode all
00329                     explodeList.find( pie ) != explodeList.end() ) {
00330                 double explodeAngle = ( startAngle + angleLen / 2 ) / 16;
00331                 double explodeAngleRad = DEGTORAD( explodeAngle );
00332                 double cosAngle = cos( explodeAngleRad );
00333                 double sinAngle = -sin( explodeAngleRad );
00334 
00335                 // find the explode factor for this particular pie
00336                 double explodeFactor = 0.0;
00337                 QMap<int,double> explodeFactors = params()->explodeFactors();
00338                 if( !explodeFactors.contains( pie ) ) // not on factors list, use default
00339                     explodeFactor = params()->explodeFactor();
00340                 else // on factors list, use segment-specific value
00341                     explodeFactor = explodeFactors[pie];
00342 
00343                 double explodeX = explodeFactor * _size * cosAngle;
00344                 double explodeY = explodeFactor * _size * sinAngle;
00345                 drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) );
00346             } else
00347                 drawPosition = _position;
00348         } else
00349             drawPosition = _position;
00350 
00351         // The 3D effect needs to be drawn first because it could
00352         // otherwise partly hide the pie itself.
00353         if ( params()->threeDPies() ) {
00354             draw3DEffect( painter, drawPosition, dataset, pie, chart,
00355                           threeDPieHeight,
00356                           params()->explode(), region );
00357         }
00358 
00359         painter->setBrush( params()->dataColor( pie ) );
00360         if ( angleLen == 5760 ) {
00361             // full circle, avoid nasty line in the middle
00362             painter->drawEllipse( drawPosition );
00363             if ( regions ) {
00364                 QPointArray hitregion;
00365                 hitregion.makeEllipse( drawPosition.x(), drawPosition.y(),
00366                                        drawPosition.width(),
00367                                        drawPosition.height() );
00368                 datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ),
00369                                                 dataset,
00370                                                 pie,
00371                                                 chart );
00372                 datReg->points[ KDChartEnums::PosCenter ]
00373                     = drawPosition.center();
00374                 datReg->points[ KDChartEnums::PosCenterRight ]
00375                     = pointOnCircle( drawPosition,    0 );
00376                 datReg->points[ KDChartEnums::PosTopRight ]
00377                     = pointOnCircle( drawPosition,  720 );
00378                 datReg->points[ KDChartEnums::PosTopCenter ]
00379                     = pointOnCircle( drawPosition, 1440 );
00380                 datReg->points[ KDChartEnums::PosTopLeft ]
00381                     = pointOnCircle( drawPosition, 2160 );
00382                 datReg->points[ KDChartEnums::PosCenterLeft ]
00383                     = pointOnCircle( drawPosition, 2880 );
00384                 datReg->points[ KDChartEnums::PosBottomLeft ]
00385                     = pointOnCircle( drawPosition, 3600 );
00386                 datReg->points[ KDChartEnums::PosBottomCenter ]
00387                     = pointOnCircle( drawPosition, 4320 );
00388                 datReg->points[ KDChartEnums::PosBottomRight ]
00389                     = pointOnCircle( drawPosition, 5040 );
00390                 datReg->startAngle = 2880;
00391                 datReg->angleLen   = 5760;
00392                 regions->append( datReg );
00393             }
00394         } else {
00395             // draw the top of this piece
00396             // Start with getting the points for the arc.
00397             const int arcPoints = angleLen;
00398             QPointArray collect(arcPoints+2);
00399             int i=0;
00400             for ( ; i<=angleLen; ++i){
00401                 collect.setPoint(i, pointOnCircle( drawPosition, startAngle+i ));
00402             }
00403             // Adding the center point of the piece.
00404             collect.setPoint(i, drawPosition.center() );
00405 
00406 
00407 
00408             painter->drawPolygon( collect );
00409 
00410 //if( bHelp ){
00411 //              painter->drawPolyline( collect );
00412 //bHelp=false;
00413 //}
00414 
00415 
00416 
00417             if ( regions ) {
00418                 QPointArray hitregion;
00419                 hitregion.makeArc( drawPosition.x(), drawPosition.y(),
00420                         drawPosition.width(),
00421                         drawPosition.height(),
00422                         ( int ) startAngle, ( int ) angleLen );
00423                 hitregion.resize( hitregion.size() + 1 );
00424                 hitregion.setPoint( hitregion.size() - 1,
00425                         drawPosition.center() );
00426                 datReg = new KDChartDataRegion( region->unite( QRegion( hitregion ) ),
00427                                                 dataset,
00428                                                 pie,
00429                                                 chart );
00430 
00431                 datReg->points[ KDChartEnums::PosTopLeft ]
00432                     = pointOnCircle( drawPosition, startAngle + angleLen );
00433                 datReg->points[ KDChartEnums::PosTopCenter ]
00434                     = pointOnCircle( drawPosition, startAngle + angleLen / 2 );
00435                 datReg->points[ KDChartEnums::PosTopRight ]
00436                     = pointOnCircle( drawPosition, startAngle );
00437 
00438                 datReg->points[   KDChartEnums::PosBottomLeft   ] = drawPosition.center();
00439                 datReg->points[   KDChartEnums::PosBottomCenter ]
00440                     = datReg->points[ KDChartEnums::PosBottomLeft   ];
00441                 datReg->points[   KDChartEnums::PosBottomRight  ]
00442                     = datReg->points[ KDChartEnums::PosBottomLeft   ];
00443 
00444                 datReg->points[ KDChartEnums::PosCenterLeft ]
00445                     = QPoint( (   datReg->points[ KDChartEnums::PosTopLeft      ].x()
00446                                 + datReg->points[ KDChartEnums::PosBottomLeft   ].x() ) / 2,
00447                             (   datReg->points[ KDChartEnums::PosTopLeft      ].y()
00448                                 + datReg->points[ KDChartEnums::PosBottomLeft   ].y() ) / 2 );
00449                 datReg->points[ KDChartEnums::PosCenter ]
00450                     = QPoint( (   datReg->points[ KDChartEnums::PosTopCenter    ].x()
00451                                 + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
00452                             (   datReg->points[ KDChartEnums::PosTopCenter    ].y()
00453                                 + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
00454                 datReg->points[ KDChartEnums::PosCenterRight ]
00455                     = QPoint( (   datReg->points[ KDChartEnums::PosTopRight     ].x()
00456                                 + datReg->points[ KDChartEnums::PosBottomRight  ].x() ) / 2,
00457                             (   datReg->points[ KDChartEnums::PosTopRight     ].y()
00458                                 + datReg->points[ KDChartEnums::PosBottomRight  ].y() ) / 2 );
00459 
00460                 datReg->startAngle = startAngle;
00461                 datReg->angleLen   = angleLen;
00462                 regions->append( datReg );
00463             }
00464         }
00465         if( mustDeleteRegion )
00466             delete region;
00467     }
00468 }
00469 
00470 
00483 void KDChartPiePainter::draw3DEffect( QPainter* painter,
00484         const QRect& drawPosition,
00485         uint dataset, uint pie, uint chart,
00486         uint threeDHeight,
00487         bool /*explode*/,
00488         QRegion* region )
00489 {
00490     // NOTE: We cannot optimize away drawing some of the effects (even
00491     // when not exploding), because some of the pies might be left out
00492     // in future versions which would make some of the normally hidden
00493     // pies visible. Complex hidden-line algorithms would be much more
00494     // expensive than just drawing for nothing.
00495 
00496     // No need to save the brush, will be changed on return from this
00497     // method anyway.
00498     painter->setBrush( QBrush( params()->dataShadow1Color( pie ),
00499                 params()->shadowPattern() ) );
00500 
00501     int startAngle = _startAngles[ ( int ) pie ];
00502     int endAngle = startAngle + _angleLens[ ( int ) pie ];
00503     // Normalize angles
00504     while ( startAngle >= 5760 )
00505         startAngle -= 5760;
00506     while ( endAngle >= 5760 )
00507         endAngle -= 5760;
00508     Q_ASSERT( startAngle >= 0 && startAngle <= 360 * 16 );
00509     Q_ASSERT( endAngle >= 0 && endAngle <= 360 * 16 );
00510 
00511     //int centerY = drawPosition.center().y();
00512 
00513     if ( startAngle == endAngle ||
00514             startAngle == endAngle - 5760 ) { // full circle
00515         drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00516                 threeDHeight, 2880, 5760, region );
00517     } else if ( startAngle <= 90 * 16 ) {
00518         if ( endAngle <= 90 * 16 ) {
00519             if ( startAngle <= endAngle ) {
00521                 drawStraightEffectSegment( painter, drawPosition, dataset,
00522                                            pie, chart, threeDHeight, startAngle,
00523                                            region );
00524             } else {
00526                 drawStraightEffectSegment( painter, drawPosition, dataset,
00527                                            pie, chart, threeDHeight, startAngle,
00528                                            region );
00529                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00530                                       threeDHeight, 2880, 5760, region );
00531             }
00532         } else if ( endAngle <= 180 * 16 ) {
00535             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00536                                        threeDHeight, startAngle, region );
00537             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00538                                        threeDHeight, endAngle, region );
00539         } else if ( endAngle <= 270 * 16 ) {
00541             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00542                     threeDHeight, startAngle,
00543                     region );
00544             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00545                     threeDHeight, endAngle, region );
00546             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00547                     threeDHeight, 2880, endAngle, region );
00548         } else { // 270*16 < endAngle < 360*16
00551             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00552                     threeDHeight, startAngle, region );
00553             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00554                     threeDHeight, 2880, endAngle, region );
00555         }
00556     } else if ( startAngle <= 180 * 16 ) {
00557         if ( endAngle <= 90 * 16 ) {
00558             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00559                     threeDHeight, 2880, 5760, region );
00560         } else if ( endAngle <= 180 * 16 ) {
00561             if ( startAngle <= endAngle ) {
00564                 drawStraightEffectSegment( painter, drawPosition, dataset,
00565                         pie, chart, threeDHeight, endAngle,
00566                         region );
00567             } else {
00570                 drawStraightEffectSegment( painter, drawPosition, dataset,
00571                         pie, chart, threeDHeight, endAngle,
00572                         region );
00573                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00574                         threeDHeight, 2880, 5760, region );
00575             }
00576         } else if ( endAngle <= 270 * 16 ) {
00577             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00578                     threeDHeight, endAngle, region );
00579             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00580                     threeDHeight, 2880, endAngle, region );
00581         } else { // 270*16 < endAngle < 360*16
00582             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00583                     threeDHeight, 2880, endAngle, region );
00584         }
00585     } else if ( startAngle <= 270 * 16 ) {
00586         if ( endAngle <= 90 * 16 ) {
00587             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00588                     threeDHeight, startAngle, 5760, region );
00589         } else if ( endAngle <= 180 * 16 ) {
00590             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00591                     threeDHeight, endAngle, region );
00592             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00593                     threeDHeight, startAngle, 5760, region );
00594         } else if ( endAngle <= 270 * 16 ) {
00595             if ( startAngle <= endAngle ) {
00598                 drawStraightEffectSegment( painter, drawPosition, dataset,
00599                         pie, chart, threeDHeight, endAngle,
00600                         region );
00601                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00602                         threeDHeight, startAngle, endAngle,
00603                         region );
00604             } else {
00607                 drawStraightEffectSegment( painter, drawPosition, dataset,
00608                         pie, chart, threeDHeight, endAngle,
00609                         region );
00610                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00611                         threeDHeight, 2880, endAngle,
00612                         region );
00613                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00614                         threeDHeight, startAngle, 5760,
00615                         region );
00616             }
00617         } else { // 270*16 < endAngle < 360*16
00618             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00619                     threeDHeight, startAngle, endAngle,
00620                     region );
00621         }
00622     } else { // 270*16 < startAngle < 360*16
00623         if ( endAngle <= 90 * 16 ) {
00624             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00625                     threeDHeight, startAngle, region );
00626             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00627                     threeDHeight, startAngle, 5760, region );
00628         } else if ( endAngle <= 180 * 16 ) {
00629             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00630                     threeDHeight, startAngle, region );
00631             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00632                     threeDHeight, endAngle, region );
00633             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00634                     threeDHeight, startAngle, 5760, region );
00635         } else if ( endAngle <= 270 * 16 ) {
00636             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00637                     threeDHeight, startAngle, region );
00638             drawStraightEffectSegment( painter, drawPosition, dataset, pie, chart,
00639                     threeDHeight, endAngle, region );
00640             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00641                     threeDHeight, 2880, endAngle, region );
00642             drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00643                     threeDHeight, startAngle, 5760, region );
00644         } else { // 270*16 < endAngle < 360*16
00645             if ( startAngle <= endAngle ) {
00648                 drawStraightEffectSegment( painter, drawPosition, dataset,
00649                         pie, chart, threeDHeight, startAngle,
00650                         region );
00651                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00652                         threeDHeight, startAngle, endAngle,
00653                         region );
00654             } else {
00657                 drawStraightEffectSegment( painter, drawPosition, dataset,
00658                         pie, chart, threeDHeight, startAngle,
00659                         region );
00660                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00661                         threeDHeight, startAngle, 5760,
00662                         region );
00663                 drawArcEffectSegment( painter, drawPosition, dataset, pie, chart,
00664                         threeDHeight, 2880, endAngle, region );
00665             }
00666         }
00667     }
00668 }
00669 
00670 
00681 void KDChartPiePainter::drawStraightEffectSegment( QPainter* painter,
00682         const QRect& rect,
00683         uint /*dataset*/, uint /*pie*/, uint /*chart*/,
00684         int threeDHeight,
00685         int angle,
00686         QRegion* region )
00687 {
00688     QPoint center = rect.center();
00689     QPointArray points( 4 );
00690     QPoint circlePoint = pointOnCircle( rect, angle );
00691     points.setPoint( 0, center );
00692     points.setPoint( 1, circlePoint );
00693     points.setPoint( 2, circlePoint.x(), circlePoint.y() + threeDHeight );
00694     points.setPoint( 3, center.x(),
00695             center.y() + threeDHeight );
00696     painter->drawPolygon( points );
00697     if ( region )
00698         *region += QRegion( points );
00699 }
00700 
00701 
00713 void KDChartPiePainter::drawArcEffectSegment( QPainter* painter,
00714         const QRect& rect,
00715         uint /*dataset*/, uint /*pie*/, uint /*chart*/,
00716         int threeDHeight,
00717         int startAngle,
00718         int endAngle,
00719         QRegion* region )
00720 {
00721     // Start with getting the points for the inner arc.
00722     const int startA = QMIN(startAngle, endAngle);
00723     const int endA   = QMAX(startAngle, endAngle);
00724     const int arcPoints = endA-startA+1;
00725     QPointArray collect(arcPoints * 2);
00726     for ( int angle=endA; angle>=startA; --angle){
00727         collect.setPoint(endA-angle, pointOnCircle( rect, angle ));
00728     }
00729 
00730     // Now copy these arcs again into the same array, but in the
00731     // opposite direction and moved down by the 3D height.
00732     for ( int i = arcPoints - 1; i >= 0; --i ) {
00733         QPoint pointOnFirstArc = collect.point( i );
00734         pointOnFirstArc.setY( pointOnFirstArc.y() + threeDHeight );
00735         collect.setPoint( arcPoints * 2 - i - 1, pointOnFirstArc );
00736     }
00737     painter->drawPolygon( collect );
00738     if ( region )
00739         *region += QRegion( collect );
00740 }
00741 
00742 
00750 uint KDChartPiePainter::findPieAt( int angle )
00751 {
00752     for ( int i = 0; i < _numValues; i++ ) {
00753         int endseg = _startAngles[ i ] + _angleLens[ i ];
00754         if ( ( _startAngles[ i ] <= angle ) &&
00755                 ( endseg >= angle ) )
00756             // found!
00757             return i;
00758     }
00759 
00760     // If we have not found it, try wrap around
00761     return findPieAt( angle + 5760 );
00762 }
00763 
00764 
00772 uint KDChartPiePainter::findLeftPie( uint pie )
00773 {
00774     if ( pie == 0 )
00775         if ( _numValues > 1 )
00776             return _numValues - 1;
00777         else
00778             return 0;
00779     else {
00780         return pie - 1;
00781     }
00782 }
00783 
00784 
00792 uint KDChartPiePainter::findRightPie( uint pie )
00793 {
00794     int rightpie = pie + 1;
00795     if ( rightpie == _numValues )
00796         rightpie = 0;
00797     return rightpie;
00798 }
00799 
00800 
00813 QString KDChartPiePainter::fallbackLegendText( uint dataset ) const
00814 {
00815     return QObject::tr( "Item " ) + QString::number( dataset + 1 );
00816 }
00817 
00818 
00828 uint KDChartPiePainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
00829 {
00830     return data->usedCols();
00831 }
KDE Home | KDE Accessibility Home | Description of Access Keys