00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "KDChartBWPainter.h"
00030 #include <KDChartParams.h>
00031 #include "KDChartTextPiece.h"
00032
00033 #include <qpainter.h>
00034 #if COMPAT_QT_VERSION >= 0x030000
00035 #include <qmemarray.h>
00036 #else
00037 #include <qarray.h>
00038 #define QMemArray QArray
00039 #endif
00040
00041 #include <stdlib.h>
00042
00054 KDChartBWPainter::KDChartBWPainter( KDChartParams* params ) :
00055 KDChartAxesPainter( params )
00056 {
00057
00058
00059 }
00060
00061
00065 KDChartBWPainter::~KDChartBWPainter()
00066 {
00067
00068 }
00069
00070
00071 void quicksort( QMemArray<double>& a, int lo, int hi )
00072 {
00073 int i=lo, j=hi;
00074 double h;
00075 double x=a[(lo+hi)/2];
00076 do
00077 {
00078 while (a[i]<x) i++;
00079 while (a[j]>x) j--;
00080 if (i<=j)
00081 {
00082 h=a[i]; a[i]=a[j]; a[j]=h;
00083 i++; j--;
00084 }
00085 } while (i<=j);
00086 if (lo<j) quicksort(a, lo, j);
00087 if (i<hi) quicksort(a, i, hi);
00088 }
00089
00090
00091
00092 int KDChartBWPainter::calculateStats( KDChartTableDataBase& data,
00093 uint dataset )
00094 {
00095 const uint nMax = data.usedCols();
00096 int nUsed = 0;
00097 QMemArray<double> values( nMax );
00098 double sum = 0.0;
00099 QVariant vVal;
00100 if( data.sorted() ){
00101 for( uint i=0; i<nMax; ++i){
00102 if( data.cellCoord( dataset, i, vVal, 1 ) &&
00103 QVariant::Double == vVal.type() ) {
00104 values[nUsed] = vVal.toDouble();
00105 sum += values[nUsed];
00106 ++nUsed;
00107 }
00108 }
00109 }else{
00110
00111 bool sorted = true;
00112 double last = 0.0;
00113 for( uint i=0; i<nMax; ++i){
00114 if( data.cellCoord( dataset, i, vVal, 1 ) &&
00115 QVariant::Double == vVal.type() ) {
00116 values[nUsed] = vVal.toDouble();
00117 if( nUsed
00118 && last > values[nUsed] )
00119 sorted = false;
00120 last = values[nUsed];
00121 sum += last;
00122 ++nUsed;
00123 }
00124 }
00125 if( !sorted ){
00126
00127 quicksort( values, 0, nUsed-1 );
00128 }
00129 }
00130
00131
00132
00133
00134 if( nUsed ){
00135
00136 stats[ KDChartParams::MaxValue ] = values[nUsed-1];
00137 stats[ KDChartParams::MinValue ] = values[0];
00138
00139 stats[ KDChartParams::MeanValue ] = sum / nUsed;
00140
00141 bool bOdd = 1 == nUsed % 2;
00142
00143 int nUd2 = nUsed/2;
00144 if( bOdd )
00145 stats[ KDChartParams::Median ] = values[ nUd2 ];
00146 else
00147 stats[ KDChartParams::Median ] =
00148 (values[ QMAX(nUd2-1, 0) ] + values[ nUd2 ]) /2;
00149
00150 nLastQ1 = QMAX( nUd2-1, 0 );
00151
00152 nFirstQ1 = nLastQ1 / 2;
00153
00154
00155 int nLowerCount = nLastQ1 - nFirstQ1 + 1;
00156
00157
00158 nFirstQ3 = bOdd ? QMIN( nUd2+1, nUsed-1 ) : nUd2;
00159
00160 nLastQ3 = nFirstQ3 + nLowerCount - 1;
00161
00162
00163 bool bOdd2 = 1 == nLowerCount % 2;
00164
00165 if( bOdd2 )
00166 stats[ KDChartParams::Quartile1 ] = values[ nFirstQ1 ];
00167 else
00168 stats[ KDChartParams::Quartile1 ] =
00169 (values[ QMAX(nFirstQ1-1, 0) ] + values[ nFirstQ1 ]) /2;
00170
00171 if( bOdd2 ){
00172 stats[ KDChartParams::Quartile3 ] = values[ nLastQ3 ];
00173 }
00174 else{
00175
00176
00177 stats[ KDChartParams::Quartile3 ] =
00178 (values[ nLastQ3 ] + values[ QMIN(nLastQ3+1, nUsed-1) ]) /2;
00179 }
00180
00181 double iqr = stats[ KDChartParams::Quartile3 ] - stats[ KDChartParams::Quartile1 ];
00182
00183
00184 double upperInner, lowerInner, upperOuter, lowerOuter;
00185 params()->bWChartFences( upperInner, lowerInner,
00186 upperOuter, lowerOuter );
00187 stats[ KDChartParams::UpperInnerFence ] =
00188 stats[ KDChartParams::Quartile3 ] + iqr * upperInner;
00189 stats[ KDChartParams::LowerInnerFence ] =
00190 stats[ KDChartParams::Quartile1 ] - iqr * lowerInner;
00191 stats[ KDChartParams::UpperOuterFence ] =
00192 stats[ KDChartParams::Quartile3 ] + iqr * upperOuter;
00193 stats[ KDChartParams::LowerOuterFence ] =
00194 stats[ KDChartParams::Quartile1 ] - iqr * lowerOuter;
00195 }
00196 return nUsed;
00197 }
00198
00199
00213 QString KDChartBWPainter::fallbackLegendText( uint dataset ) const
00214 {
00215 return QObject::tr( "Series " ) + QString::number( dataset + 1 );
00216 }
00217
00218
00228 uint KDChartBWPainter::numLegendFallbackTexts( KDChartTableDataBase* data ) const
00229 {
00230 return data->usedRows();
00231 }
00232
00233
00234 bool KDChartBWPainter::isNormalMode() const
00235 {
00236 return KDChartParams::BWNormal == params()->bWChartSubType();
00237 }
00238
00239 int KDChartBWPainter::clipShiftUp( bool, double ) const
00240 {
00241 return 0;
00242 }
00243
00254 void KDChartBWPainter::specificPaintData( QPainter* painter,
00255 const QRect& ourClipRect,
00256 KDChartTableDataBase* data,
00257 KDChartDataRegionList* ,
00258 const KDChartAxisParams* axisPara,
00259 bool ,
00260 uint ,
00261 double logWidth,
00262 double ,
00263 double logHeight,
00264 double ,
00265 double ,
00266 double ,
00267 double ,
00268 uint chartDatasetStart,
00269 uint chartDatasetEnd,
00270 uint datasetStart,
00271 uint datasetEnd )
00272 {
00273
00274
00275 uint datasetNum = ( chartDatasetEnd - chartDatasetStart ) + 1;
00276
00277 double pixelsPerUnit = 0.0;
00278 if( 0.0 != axisPara->trueAxisHigh() - axisPara->trueAxisLow() )
00279 pixelsPerUnit = logHeight / (axisPara->trueAxisHigh() - axisPara->trueAxisLow());
00280 else
00281 pixelsPerUnit = logHeight / 10;
00282
00283
00284 double pointDist = logWidth / ( ( double ) datasetNum );
00285
00286
00287 double zeroXAxisI = axisPara->axisZeroLineStartY() - _dataRect.y();
00288
00289 const int lineWidth = static_cast<int>( pointDist / 66.0 ) * QMAX(params()->lineWidth(), 1);
00290 const int lineWidthD2 = lineWidth * 2 / 3;
00291
00292 const bool noBrush = Qt::NoBrush == params()->bWChartBrush().style();
00293
00294
00295 for ( uint dataset = chartDatasetStart;
00296 dataset <= chartDatasetEnd;
00297 ++dataset ) {
00298
00299 if( dataset >= datasetStart &&
00300 dataset <= datasetEnd &&
00301 0 < calculateStats( *data, dataset ) ) {
00302 const QColor color( params()->dataColor( dataset ) );
00303
00304 double drawUOF = stats[ KDChartParams::UpperOuterFence ] * pixelsPerUnit;
00305 double drawUIF = stats[ KDChartParams::UpperInnerFence ] * pixelsPerUnit;
00306 double drawQu3 = stats[ KDChartParams::Quartile3 ] * pixelsPerUnit;
00307 double drawMed = stats[ KDChartParams::Median ] * pixelsPerUnit;
00308 double drawQu1 = stats[ KDChartParams::Quartile1 ] * pixelsPerUnit;
00309 double drawLIF = stats[ KDChartParams::LowerInnerFence ] * pixelsPerUnit;
00310 double drawLOF = stats[ KDChartParams::LowerOuterFence ] * pixelsPerUnit;
00311 double drawMax = stats[ KDChartParams::MaxValue ] * pixelsPerUnit;
00312 double drawMin = stats[ KDChartParams::MinValue ] * pixelsPerUnit;
00313 double drawMean= stats[ KDChartParams::MeanValue ] * pixelsPerUnit;
00314
00315 double drawUWhisker = QMIN(drawUIF, drawMax);
00316 double drawLWhisker = QMAX(drawLIF, drawMin);
00317
00318 const int boxWidth = QMAX( 6, static_cast<int>( pointDist * 0.2 ) );
00319
00320 int markWidth = params()->bWChartOutValMarkerSize();
00321 bool drawOutliers = ( 0 != markWidth );
00322 if( drawOutliers ){
00323 if( 0 > markWidth)
00324 markWidth = QMAX( 4, markWidth * boxWidth / -100 );
00325 else
00326 markWidth = QMAX( 4, markWidth );
00327 }
00328 else
00329 markWidth = boxWidth * 25 / 100;
00330
00331 painter->setPen( QPen( color, lineWidth ) );
00332 painter->setBrush( params()->bWChartBrush() );
00333
00334 int boxWidthD2 = boxWidth / 2;
00335 int xPos = static_cast<int>(
00336 pointDist * ( (double)(dataset - chartDatasetStart) + 0.5 )
00337 - lineWidth / 2);
00338 painter->drawRect( xPos - boxWidthD2,
00339 static_cast<int>( zeroXAxisI - drawQu3 ),
00340 boxWidth,
00341 static_cast<int>( drawQu3 - drawQu1) + 1 );
00342
00343 painter->drawLine( xPos - boxWidthD2,
00344 static_cast<int>( zeroXAxisI - drawMed ),
00345 xPos - boxWidthD2 + boxWidth,
00346 static_cast<int>( zeroXAxisI - drawMed ) );
00347
00348 painter->drawLine( xPos - boxWidthD2,
00349 static_cast<int>( zeroXAxisI - drawUWhisker ),
00350 xPos - boxWidthD2 + boxWidth,
00351 static_cast<int>( zeroXAxisI - drawUWhisker ) );
00352 painter->drawLine( xPos,
00353 static_cast<int>( zeroXAxisI - drawUWhisker ),
00354 xPos,
00355 static_cast<int>( zeroXAxisI - drawQu3 ) );
00356 painter->drawLine( xPos - boxWidthD2,
00357 static_cast<int>( zeroXAxisI - drawLWhisker ),
00358 xPos - boxWidthD2 + boxWidth,
00359 static_cast<int>( zeroXAxisI - drawLWhisker ) );
00360 painter->drawLine( xPos,
00361 static_cast<int>( zeroXAxisI - drawLWhisker ),
00362 xPos,
00363 static_cast<int>( zeroXAxisI - drawQu1 ) );
00364
00365 int xPos2 = static_cast<int>(
00366 pointDist * ( (double)(dataset - chartDatasetStart) + 0.5 )
00367 - lineWidthD2 / 2);
00368 int markWidthD2 = QMAX(markWidth / 2, 2);
00369 int markWidthD25 = QMAX(static_cast<int>( 0.85 * markWidth / 2.0), 2);
00370 int markWidthD35 = QMAX(static_cast<int>( 0.85 * markWidth / 3.0), 2);
00371
00372 if( drawOutliers ){
00373 const uint nMax = data->usedCols();
00374 int drawVal;
00375 QVariant vVal;
00376 for( uint i=0; i<nMax; ++i)
00377 if( data->cellCoord( dataset, i, vVal, 1 ) &&
00378 QVariant::Double == vVal.type() ) {
00379 drawVal = static_cast<int>( pixelsPerUnit * vVal.toDouble() );
00380 if( drawLOF > drawVal || drawUOF < drawVal ) {
00381 painter->setPen( Qt::NoPen );
00382 painter->drawChord( xPos2 - markWidthD2,
00383 static_cast<int>(zeroXAxisI - drawVal) - markWidthD2,
00384 markWidth,
00385 markWidth,
00386 0, 5760 );
00387 painter->setPen( QPen( color, lineWidthD2 ) );
00388 painter->drawArc( xPos2 - markWidthD2,
00389 static_cast<int>(zeroXAxisI - drawVal) - markWidthD2,
00390 markWidth,
00391 markWidth,
00392 0, 5760 );
00393 } else if( drawLIF > drawVal || drawUIF < drawVal ) {
00394 painter->setPen( Qt::NoPen );
00395 painter->drawChord( xPos2 - markWidthD2,
00396 static_cast<int>( zeroXAxisI - drawVal) - markWidthD2,
00397 markWidth,
00398 markWidth,
00399 0, 5760 );
00400 painter->setPen( QPen( color, lineWidthD2 ) );
00401 painter->drawLine( xPos2,
00402 static_cast<int>(zeroXAxisI - drawVal) - markWidthD2,
00403 xPos2,
00404 static_cast<int>(zeroXAxisI - drawVal) + markWidthD2 );
00405 painter->drawLine( xPos2 - markWidthD25,
00406 static_cast<int>(zeroXAxisI - drawVal) - markWidthD35,
00407 xPos2 + markWidthD25,
00408 static_cast<int>(zeroXAxisI - drawVal) + markWidthD35 );
00409 painter->drawLine( xPos2 + markWidthD25,
00410 static_cast<int>(zeroXAxisI - drawVal) - markWidthD35,
00411 xPos2 - markWidthD25,
00412 static_cast<int>(zeroXAxisI - drawVal) + markWidthD35 );
00413 }
00414 }
00415 }
00416
00417 bool evenLineWidthD2 = floor( ((double)lineWidthD2)/2.0 ) == ((double)lineWidthD2)/2.0;
00418 painter->setPen( params()->bWChartBrush().color() );
00419 painter->drawChord( xPos2 - markWidthD2-1 - (evenLineWidthD2 ? 0 : 1),
00420 static_cast<int>( zeroXAxisI - drawMean ) - markWidthD2 - 1,
00421 markWidthD2*2 + (evenLineWidthD2 ? 2 : 3),
00422 markWidthD2*2 + (evenLineWidthD2 ? 2 : 3),
00423 0, 5760 );
00424 if( noBrush ) {
00425
00426 int h,s,v;
00427 color.hsv(&h,&s,&v);
00428 painter->setPen( QPen( 128 > v ? color.light(150) : color.dark(150),
00429 lineWidthD2 ) );
00430 } else
00431 painter->setPen( QPen( color, lineWidthD2 ) );
00432 painter->drawLine( xPos2 - markWidthD2 - (evenLineWidthD2 ? 0 : 1),
00433 static_cast<int>( zeroXAxisI - drawMean ),
00434 xPos2 + markWidthD2,
00435 static_cast<int>( zeroXAxisI - drawMean ) );
00436 painter->drawLine( xPos2 - (evenLineWidthD2 ? 0 : 1),
00437 static_cast<int>( zeroXAxisI - drawMean ) - markWidthD2,
00438 xPos2 - (evenLineWidthD2 ? 0 : 1),
00439 static_cast<int>( zeroXAxisI - drawMean ) + markWidthD2 + (evenLineWidthD2 ? 0 : 1) );
00440
00441
00442 painter->setPen( Qt::NoPen );
00443 for( int ii = KDChartParams::BWStatValSTART;
00444 ii <= KDChartParams::BWStatValEND;
00445 ++ii ){
00446 KDChartParams::BWStatVal i = (KDChartParams::BWStatVal)ii;
00447 if( params()->bWChartPrintStatistics( i ) ) {
00448 QFont statFont( params()->bWChartStatisticsFont( i ) );
00449 float nTxtHeight = statFont.pointSizeFloat();
00450 if ( params()->bWChartStatisticsUseRelSize( i ) ) {
00451 nTxtHeight = params()->bWChartStatisticsFontRelSize( i )
00452 * boxWidth / 100;
00453 statFont.setPointSizeFloat( nTxtHeight );
00454 }
00455 double drawStat = pixelsPerUnit * stats[i];
00456 KDChartTextPiece statText( painter, QString::number( stats[i] ),
00457 statFont );
00458 int tw = statText.width();
00459 int xDelta = ( KDChartParams::MaxValue == i
00460 || KDChartParams::MeanValue == i
00461 || KDChartParams::MinValue == i )
00462 ? -1 * (tw + static_cast<int>( 1.3*boxWidthD2 ))
00463 : static_cast<int>( 1.3*boxWidthD2 );
00464 QBrush brush( params()->bWChartStatisticsBrush( i ) );
00465 painter->setBrush( brush );
00466 int y = static_cast<int>( zeroXAxisI - drawStat - nTxtHeight/2);
00467 painter->drawRect( xPos + xDelta - 1,
00468 y,
00469 tw + 2,
00470 QMAX(static_cast < int > ( nTxtHeight ), 8) + 1 );
00471 statText.draw( painter,
00472 xPos + xDelta,
00473 y,
00474 ourClipRect,
00475 params()->bWChartStatisticsColor( i ),
00476 0 );
00477 }
00478 }
00479
00480 } else
00481 continue;
00482 }
00483 }