kchart

KDChartRingPainter.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 "KDChartRingPainter.h"
00030 #include "KDChartParams.h"
00031 
00032 #include <qpainter.h>
00033 #include <qvaluestack.h>
00034 
00035 #include <stdlib.h>
00036 
00037 #define DEGTORAD(d) (d)*M_PI/180
00038 
00052     KDChartRingPainter::KDChartRingPainter( KDChartParams* params ) :
00053 KDChartPainter( params )
00054 {
00055     // This constructor intentionally left blank so far; we cannot setup the
00056     // geometry yet since we do not know the size of the painter.
00057 }
00058 
00059 
00063 KDChartRingPainter::~KDChartRingPainter()
00064 {
00065     // intentionally left blank
00066 }
00067 
00068 
00078 void KDChartRingPainter::paintData( QPainter* painter,
00079         KDChartTableDataBase* data,
00080         bool paint2nd,
00081         KDChartDataRegionList* regions )
00082 {
00083     uint chart = paint2nd ? 1 : 0;
00084 
00085     QRect ourClipRect( _dataRect );
00086 
00087     const QWMatrix & world = painter->worldMatrix();
00088     ourClipRect =
00089 #if COMPAT_QT_VERSION >= 0x030000
00090         world.mapRect( ourClipRect );
00091 #else
00092     world.map( ourClipRect );
00093 #endif
00094 
00095     ourClipRect.setTop(ourClipRect.top()-1);
00096     ourClipRect.setLeft(ourClipRect.left()-1);
00097     ourClipRect.setBottom(ourClipRect.bottom()+1);
00098     ourClipRect.setRight(ourClipRect.right()+1);
00099     painter->setClipRect( ourClipRect );
00100 
00101     uint datasetStart, datasetEnd;
00102     if ( params()->neverUsedSetChartSourceMode()
00103             || !params()->findDataset( KDChartParams::DataEntry,
00104                 datasetStart,
00105                 datasetEnd,
00106                 chart ) ) {
00107         uint maxRow, maxRowMinus1;
00108         switch ( data->usedRows() ) {
00109             case 0:
00110                 return ;
00111             case 1:
00112                 maxRow = 0;
00113                 maxRowMinus1 = 0;
00114                 break;
00115             default:
00116                 maxRow = data->usedRows() - 1;
00117                 maxRowMinus1 = maxRow - 1;
00118         }
00119         datasetStart = paint2nd ? maxRow
00120             : 0;
00121         datasetEnd = paint2nd ? maxRow
00122             : ( ( KDChartParams::NoType
00123                         == params()->additionalChartType() )
00124                     ? maxRow
00125                     : maxRowMinus1 );
00126     }
00127     uint datasetNum = abs( (int)( datasetEnd - datasetStart ) + 1 );
00128 
00129 
00130     // Number of values: If -1, use all values, otherwise use the
00131     // specified number of values.
00132     if ( params()->numValues() != -1 )
00133         _numValues = params()->numValues();
00134     else
00135         _numValues = data->usedCols();
00136 
00137     // compute position
00138     _size = QMIN( _dataRect.width(), _dataRect.height() ); // initial size
00139     // if the rings explode, we need to give them additional space =>
00140     // make the basic size smaller
00141     if ( params()->explode() ) {
00142         double doubleSize = ( double ) _size;
00143         doubleSize /= ( 1.0 + params()->explodeFactor() * 2 );
00144         _size = ( int ) doubleSize;
00145     }
00146 
00147     int x = ( _dataRect.width() == _size ) ? 0 : ( ( _dataRect.width() - _size ) / 2 );
00148     int y = ( _dataRect.height() == _size ) ? 0 : ( ( _dataRect.height() - _size ) / 2 );
00149     _position = QRect( x, y, _size, _size );
00150     _position.moveBy( _dataRect.left(), _dataRect.top() );
00151 
00152     // We need the row sums anyway later, so we can just as well compute them
00153     // here, because we need them in case of relative ring thicknesses.
00154     QMemArray<double> rowsums;
00155     double totalSum = 0.0;
00156     rowsums.resize( datasetEnd+1 ); // not datasetNum!
00157     for( int d1 = (int)datasetStart; d1 <= (int)datasetEnd; d1++ ) {
00158         rowsums[d1] = data->rowAbsSum( d1 );
00159         totalSum += rowsums[d1];
00160     }
00161 
00162     QMemArray<int> ringthicknesses;
00163     ringthicknesses.resize( datasetEnd+1 ); // not datasetNum!
00164 
00165     // constant ring thickness
00166     int ringthickness = _size / ( datasetNum * 2 );
00167     // Never let the ring thickness be more than 1/10 of the size to
00168     // ensure "ringness"
00169     if( ringthickness > ( _size/10 ) )
00170         ringthickness = _size / 10;
00171 
00172     for( int d2 = (int)datasetStart; d2 <= (int)datasetEnd; d2++ )
00173         if( params()->relativeRingThickness() ) {
00174             // 50% should be the same thickness as the one used when ring
00175             // thickness is constant.
00176             ringthicknesses[d2] = (uint)floor( (rowsums[d2] / totalSum) *
00177                     ( 2.0 * (double)ringthickness ) + 0.5 );
00178         } else {
00179             ringthicknesses[d2] = ringthickness;
00180         }
00181 
00182     int currentouterradius = _size/2;
00183 
00184     // Loop through all the displayable datasets; each dataset is one ring
00185     for( int dataset = (int)datasetStart; dataset <= (int)datasetEnd; dataset++ ) {
00186         double sectorsPerValue = 5760.0 / rowsums[dataset]; // 5760 == 16*360, number of sections in Qt circle
00187         //int sectorsPerValueI = static_cast<int>( sectorsPerValue );
00188         double currentstartpos = (double)params()->ringStart() * 16.0;
00189         // Loop through all the values; each value is one piece on the ring.
00190         QVariant vValY;
00191         for( int value = 0; value < _numValues; value++ ) {
00192             // is there anything at all at this value?
00193             double cellValue = 0.0;
00194             if( data->cellCoord( dataset, value, vValY, 1 ) &&
00195                 QVariant::Double == vValY.type() ){
00196                 cellValue = fabs( vValY.toDouble() );
00197                 // Explosion: Only explode if explosion is turned on generally
00198                 // and we are on the first ring. Besides, if there is a list
00199                 // of explodeable values, the current value must be on this
00200                 // list.
00201 
00202                 QValueList<int> explodeList = params()->explodeValues();
00203                 bool explode = params()->explode() && // explosion is on at all
00204                     ( dataset == (int)datasetStart ) && // outermost ring
00205                     ( ( explodeList.count() == 0 ) || // either nothing on explode list
00206                       ( explodeList.find( value ) != explodeList.end() ) ); // or pie is on it
00207 
00208                 drawOneSegment( painter,
00209                         currentouterradius,
00210                         currentouterradius-ringthicknesses[dataset],
00211                         currentstartpos,
00212                         sectorsPerValue * cellValue,
00213                         dataset, value, chart, explode, regions );
00214             }
00215             currentstartpos += sectorsPerValue * cellValue;
00216         }
00217         currentouterradius -= ringthicknesses[dataset];
00218     }
00219 }
00220 
00221 
00222 
00223 void KDChartRingPainter::drawOneSegment( QPainter* painter,
00224         uint outerRadius,
00225         uint innerRadius,
00226         double startAngle,
00227         double angles,
00228         uint dataset,
00229         uint value,
00230         uint chart,
00231         bool explode,
00232         KDChartDataRegionList* regions )
00233 {
00234     // special case for full circle
00235     if( angles == 5760.0 )
00236         startAngle =  0.0;
00237 
00238     painter->setPen( QPen( params()->outlineDataColor(),
00239                 params()->outlineDataLineWidth() ) );
00240     painter->setBrush( params()->dataColor( value ) );
00241 
00242     uint outerRadius2 = outerRadius * 2;
00243     uint innerRadius2 = innerRadius * 2;
00244 
00245     QRect drawPosition = _position;
00246     if ( explode ) {
00247         // need to compute a new position for each pie
00248         double explodeAngle = ( startAngle + angles / 2.0 ) / 16.0;
00249         double explodeAngleRad = DEGTORAD( explodeAngle );
00250         double cosAngle = cos( explodeAngleRad );
00251         double sinAngle = -sin( explodeAngleRad );
00252 
00253         // find the explode factor for this particular ring segment
00254         double explodeFactor = 0.0;
00255         QMap<int,double> explodeFactors = params()->explodeFactors();
00256         if( !explodeFactors.contains( value ) ) // not on factors list, use default
00257             explodeFactor = params()->explodeFactor();
00258         else // on factors list, use segment-specific value
00259             explodeFactor = explodeFactors[value];
00260 
00261         double explodeX = explodeFactor * _size * cosAngle;
00262         double explodeY = explodeFactor * _size * sinAngle;
00263         drawPosition.moveBy( static_cast<int>( explodeX ), static_cast<int>( explodeY ) );
00264     }
00265 
00266     QRect outerRect( drawPosition.x() +
00267             ( drawPosition.width() - outerRadius2 ) / 2,
00268             drawPosition.y() +
00269             ( drawPosition.height() - outerRadius2 ) / 2,
00270             outerRadius2, outerRadius2 );
00271     QRect innerRect( drawPosition.x() +
00272             ( drawPosition.width() - innerRadius2 ) / 2,
00273             drawPosition.y() +
00274             ( drawPosition.height() - innerRadius2 ) / 2,
00275             innerRadius2, innerRadius2 );
00276 
00277     // Start with getting the points for the inner arc.
00278     QPointArray innerArc;
00279     makeArc( innerArc, innerRect, startAngle, angles );
00280 
00281     // And the points for the outer arc
00282     QPointArray outerArc;
00283     makeArc( outerArc, outerRect, startAngle, angles );
00284 
00285     // Now copy the points from the outer arc in the reverse order onto the
00286     // inner arc array and draw that.
00287     uint innerArcPoints = innerArc.size();
00288     uint outerArcPoints = outerArc.size();
00289     innerArc.resize( innerArcPoints + outerArcPoints );
00290     for ( int i = outerArcPoints - 1; i >= 0; i-- ) {
00291         innerArc.setPoint( innerArcPoints+outerArcPoints-i-1,
00292                 outerArc.point( i ) );
00293     }
00294 
00295     painter->drawPolygon( innerArc );
00296     if ( regions /* && ( innerArc.size() > 2 )*/ ) {
00297         KDChartDataRegion* datReg = new KDChartDataRegion( dataset,
00298                                                            value,
00299                                                            chart,
00300                                                            innerArc );
00301 
00302         const int aA = static_cast<int>( startAngle );
00303         const int aM = static_cast<int>( startAngle + angles / 2.0 );
00304         const int aZ = static_cast<int>( startAngle + angles );
00305 
00306         datReg->points[ KDChartEnums::PosTopLeft ]
00307             = pointOnCircle( outerRect, aZ );
00308         datReg->points[ KDChartEnums::PosTopCenter ]
00309             = pointOnCircle( outerRect, aM );
00310         datReg->points[ KDChartEnums::PosTopRight ]
00311             = pointOnCircle( outerRect, aA );
00312 
00313         datReg->points[ KDChartEnums::PosBottomLeft ]
00314             = pointOnCircle( innerRect, aZ );
00315         datReg->points[ KDChartEnums::PosBottomCenter ]
00316             = pointOnCircle( innerRect, aM );
00317         datReg->points[ KDChartEnums::PosBottomRight ]
00318             = pointOnCircle( innerRect, aA );
00319 
00320         datReg->points[ KDChartEnums::PosCenterLeft ]
00321             = QPoint( (   datReg->points[ KDChartEnums::PosTopLeft      ].x()
00322                         + datReg->points[ KDChartEnums::PosBottomLeft   ].x() ) / 2,
00323                       (   datReg->points[ KDChartEnums::PosTopLeft      ].y()
00324                         + datReg->points[ KDChartEnums::PosBottomLeft   ].y() ) / 2 );
00325         datReg->points[ KDChartEnums::PosCenter ]
00326             = QPoint( (   datReg->points[ KDChartEnums::PosTopCenter    ].x()
00327                         + datReg->points[ KDChartEnums::PosBottomCenter ].x() ) / 2,
00328                       (   datReg->points[ KDChartEnums::PosTopCenter    ].y()
00329                         + datReg->points[ KDChartEnums::PosBottomCenter ].y() ) / 2 );
00330         datReg->points[ KDChartEnums::PosCenterRight ]
00331             = QPoint( (   datReg->points[ KDChartEnums::PosTopRight     ].x()
00332                         + datReg->points[ KDChartEnums::PosBottomRight  ].x() ) / 2,
00333                       (   datReg->points[ KDChartEnums::PosTopRight     ].y()
00334                         + datReg->points[ KDChartEnums::PosBottomRight  ].y() ) / 2 );
00335 
00336         // test the 9 positions:
00337         /*
00338         painter->drawEllipse( datReg->points[ KDChartEnums::PosTopLeft     ].x() - 2,
00339                               datReg->points[ KDChartEnums::PosTopLeft     ].y() - 2,  5, 5);
00340         painter->drawEllipse( datReg->points[ KDChartEnums::PosCenterLeft  ].x() - 2,
00341                               datReg->points[ KDChartEnums::PosCenterLeft  ].y() - 2,  5, 5);
00342         painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomLeft  ].x() - 2,
00343                               datReg->points[ KDChartEnums::PosBottomLeft  ].y() - 2,  5, 5);
00344 
00345         qDebug( "\ncenter: (%i, %i)",
00346                 datReg->points[ KDChartEnums::PosCenter   ].x(),
00347                 datReg->points[ KDChartEnums::PosCenter   ].y() );
00348         painter->drawEllipse( datReg->points[ KDChartEnums::PosTopCenter   ].x() - 2,
00349                               datReg->points[ KDChartEnums::PosTopCenter   ].y() - 2,  5, 5);
00350         painter->drawEllipse( datReg->points[ KDChartEnums::PosCenter      ].x() - 2,
00351                               datReg->points[ KDChartEnums::PosCenter      ].y() - 2,  5, 5);
00352         painter->drawEllipse( datReg->points[ KDChartEnums::PosBottomCenter].x() - 2,
00353                               datReg->points[ KDChartEnums::PosBottomCenter].y() - 2,  5, 5);
00354 
00355         painter->drawRect( datReg->points[ KDChartEnums::PosCenterRight ].x() - 2,
00356                               datReg->points[ KDChartEnums::PosCenterRight ].y() - 2,  5, 5);
00357         //painter->drawRect( datReg->points[ KDChartEnums::PosTopRight    ].x() - 2,
00358         //                    datReg->points[ KDChartEnums::PosTopRight    ].y() - 2,  5, 5);
00359         painter->drawRect( datReg->points[ KDChartEnums::PosBottomRight ].x() - 2,
00360                               datReg->points[ KDChartEnums::PosBottomRight ].y() - 2,  5, 5);
00361         */
00362         datReg->startAngle = static_cast<int>( startAngle );
00363         datReg->angleLen   = static_cast<int>( angles );
00364         regions->append( datReg );
00365     }
00366 }
00367 
00368 
00382 QString KDChartRingPainter::fallbackLegendText( uint dataset ) const
00383 {
00384     return QObject::tr( "Item " ) + QString::number( dataset + 1 );
00385 }
00386 
00387 
00397 uint KDChartRingPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
00398 {
00399     return data->usedCols();
00400 }
KDE Home | KDE Accessibility Home | Description of Access Keys