KDChartPercentPlotter_p.cpp

Go to the documentation of this file.
00001 /* -*- Mode: C++ -*-
00002    KDChart - a multi-platform charting engine
00003    */
00004 
00005 /****************************************************************************
00006  ** Copyright (C) 2005-2007 Klarälvdalens Datakonsult AB.  All rights reserved.
00007  **
00008  ** This file is part of the KD Chart 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 KD Chart licenses may use this file in
00016  ** accordance with the KD Chart 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.kdab.net/kdchart for
00023  **   information about KD Chart Commercial License Agreements.
00024  **
00025  ** Contact info@kdab.net if any conditions of this
00026  ** licensing are not clear to you.
00027  **
00028  **********************************************************************/
00029 
00030 #include "KDChartPercentPlotter_p.h"
00031 #include "KDChartPlotter.h"
00032 
00033 #include <limits>
00034 
00035 using namespace KDChart;
00036 using namespace std;
00037 
00038 PercentPlotter::PercentPlotter( Plotter* d )
00039     : PlotterType( d )
00040 {
00041 }
00042 
00043 Plotter::PlotType PercentPlotter::type() const
00044 {
00045     return Plotter::Percent;
00046 }
00047 
00048 const QPair< QPointF, QPointF > PercentPlotter::calculateDataBoundaries() const
00049 {
00050     const int rowCount = compressor().modelDataRows();
00051     const int colCount = compressor().modelDataColumns();
00052     double xMin = std::numeric_limits< double >::quiet_NaN();
00053     double xMax = std::numeric_limits< double >::quiet_NaN();
00054     const double yMin = 0.0;
00055     const double yMax = 100.0;
00056 
00057     for( int column = 0; column < colCount; ++column )
00058     {
00059         for ( int row = 0; row < rowCount; ++row )
00060         {
00061             const CartesianDiagramDataCompressor::CachePosition position( row, column );
00062             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00063 
00064             const double valueX = ISNAN( point.key ) ? 0.0 : point.key;
00065 
00066             if( ISNAN( xMin ) )
00067             {
00068                 xMin = valueX;
00069                 xMax = valueX;
00070             }
00071             else
00072             {
00073                 xMin = qMin( xMin, valueX );
00074                 xMax = qMax( xMax, valueX );
00075             }
00076         }
00077     }
00078 
00079     // NOTE: calculateDataBoundaries must return the *real* data boundaries!
00080     //       i.e. we may NOT fake yMin to be qMin( 0.0, yMin )
00081     //       (khz, 2008-01-24)
00082     const QPointF bottomLeft( QPointF( xMin, yMin ) );
00083     const QPointF topRight( QPointF( xMax, yMax ) );
00084     return QPair< QPointF, QPointF >( bottomLeft, topRight );
00085 }
00086 
00087 class Value
00088 {
00089 public:
00090     Value()
00091         : value( std::numeric_limits< double >::quiet_NaN() )
00092     {
00093     }
00094     // allow implicit conversion
00095     Value( double value )
00096         : value( value )
00097     {
00098     }
00099     operator double() const
00100     {
00101         return value;
00102     }
00103 
00104 private:
00105     double value;
00106 };
00107 
00108 void PercentPlotter::paint( PaintContext* ctx )
00109 {
00110     reverseMapper().clear();
00111 
00112     Q_ASSERT( dynamic_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() ) );
00113     const CartesianCoordinatePlane* const plane = static_cast< CartesianCoordinatePlane* >( ctx->coordinatePlane() );
00114     const int colCount = compressor().modelDataColumns();
00115     const int rowCount = compressor().modelDataRows();
00116 
00117     if( colCount == 0 || rowCount == 0 )
00118         return;
00119 
00120     DataValueTextInfoList textInfoList;
00121     LineAttributes::MissingValuesPolicy policy; // ???
00122 
00123     // this map contains the y-values to each x-value
00124     QMap< double, QVector< QPair< Value, QModelIndex > > > diagramValues;
00125 
00126     for( int col = 0; col < colCount; ++col )
00127     {
00128         for( int row = 0; row < rowCount; ++row )
00129         {
00130             const CartesianDiagramDataCompressor::CachePosition position( row, col );
00131             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00132             diagramValues[ point.key ].resize( colCount );
00133             diagramValues[ point.key ][ col ].first = point.value;
00134             diagramValues[ point.key ][ col ].second = point.index;
00135         }
00136     }
00137 
00138     // the sums of the y-values per x-value
00139     QMap< double, double > yValueSums;
00140     // the x-values
00141     QList< double > xValues = diagramValues.keys();
00142     // make sure it's sorted
00143     qSort( xValues );
00144     Q_FOREACH( const double xValue, xValues )
00145     {
00146         // the y-values to the current x-value
00147         QVector< QPair< Value, QModelIndex > >& yValues = diagramValues[ xValue ];
00148         Q_ASSERT( yValues.count() == colCount );
00149 
00150         for( int column = 0; column < colCount; ++column )
00151         {
00152             QPair< Value, QModelIndex >& data = yValues[ column ];
00153             // if the index is invalid, there was no value. Let's interpolate.
00154             if( !data.second.isValid() )
00155             {
00156                 QPair< QPair< double, Value >, QModelIndex > left;
00157                 QPair< QPair< double, Value >, QModelIndex > right;
00158                 int xIndex = 0;
00159                 // let's find the next lower value
00160                 for( xIndex = xValues.indexOf( xValue ); xIndex >= 0; --xIndex )
00161                 {
00162                     if( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() )
00163                     {
00164                         left.first.first = xValues[ xIndex ];
00165                         left.first.second = diagramValues[ left.first.first ][ column ].first;
00166                         left.second = diagramValues[ xValues[ xIndex ] ][ column ].second;
00167                         break;
00168                     }
00169                 }
00170                 // let's find the next higher value
00171                 for( xIndex = xValues.indexOf( xValue ); xIndex < xValues.count(); ++xIndex )
00172                 {
00173                     if( diagramValues[ xValues[ xIndex ] ][ column ].second.isValid() )
00174                     {
00175                         right.first.first = xValues[ xIndex ];
00176                         right.first.second = diagramValues[ right.first.first ][ column ].first;
00177                         right.second = diagramValues[ xValues[ xIndex ] ][ column ].second;
00178                         break;
00179                     }
00180                 }
00181 
00182                 // interpolate out of them (left and/or right might be invalid, but this doesn't matter here)
00183                 const double leftX = left.first.first;
00184                 const double rightX = right.first.first;
00185                 const double leftY = left.first.second;
00186                 const double rightY = right.first.second;
00187 
00188                 data.first = leftY + ( rightY - leftY ) * ( xValue - leftX ) / ( rightX - leftX );
00189                 // if the result is a valid value, let's assign the index, too
00190                 if( !ISNAN( data.first.operator double() ) )
00191                     data.second = left.second;
00192             }
00193 
00194             // sum it up
00195             if( !ISNAN( yValues[ column ].first.operator double() ) )
00196                 yValueSums[ xValue ] += yValues[ column ].first;
00197         }
00198     }
00199 
00200     for( int column = 0; column < colCount; ++column )
00201     {
00202         LineAttributesInfoList lineList;
00203         LineAttributes laPreviousCell;
00204         CartesianDiagramDataCompressor::CachePosition previousCellPosition;
00205 
00206         CartesianDiagramDataCompressor::DataPoint lastPoint;
00207 
00208         qreal lastExtraY = 0.0;
00209         qreal lastValue = 0.0;
00210 
00211         QMapIterator< double, QVector< QPair< Value, QModelIndex > > >  i( diagramValues );
00212         while( i.hasNext() )
00213         {
00214             i.next();
00215             CartesianDiagramDataCompressor::DataPoint point;
00216             point.key = i.key();
00217             const QPair< Value, QModelIndex >& data = i.value().at( column );
00218             point.value = data.first;
00219             point.index = data.second;
00220 
00221             if( ISNAN( point.key ) || ISNAN( point.value ) )
00222             {
00223                 previousCellPosition = CartesianDiagramDataCompressor::CachePosition();
00224                 continue;
00225             }
00226 
00227             double extraY = 0.0;
00228             for( int col = column - 1; col >= 0; --col )
00229             {
00230                 const double y = i.value().at( col ).first;
00231                 if( !ISNAN( y ) )
00232                     extraY += y;
00233             }
00234 
00235             LineAttributes laCell;
00236 
00237             const qreal value = ( point.value + extraY ) / yValueSums[ i.key() ] * 100;
00238 
00239             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
00240             // area corners, a + b are the line ends:
00241             const QPointF a( plane->translate( QPointF( lastPoint.key, lastValue ) ) );
00242             const QPointF b( plane->translate( QPointF( point.key, value ) ) );
00243             const QPointF c( plane->translate( QPointF( lastPoint.key, lastExtraY / yValueSums[ i.key() ] * 100 ) ) );
00244             const QPointF d( plane->translate( QPointF( point.key, extraY / yValueSums[ i.key() ] * 100 ) ) );
00245             // add the line to the list:
00246             laCell = diagram()->lineAttributes( sourceIndex );
00247             // add data point labels:
00248             const PositionPoints pts = PositionPoints( b, a, d, c );
00249             // if necessary, add the area to the area list:
00250             QList<QPolygonF> areas;
00251             if ( laCell.displayArea() ) {
00252                 QPolygonF polygon;
00253                 polygon << a << b << d << c;
00254                 areas << polygon;
00255             }
00256             // add the pieces to painting if this is not hidden:
00257             if ( !point.hidden /*&& !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) */) {
00258                 appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, pts,
00259                                                Position::NorthWest, Position::SouthWest,
00260                                                value );
00261                 if( !ISNAN( lastPoint.key ) && !ISNAN( lastPoint.value ) )
00262                 {
00263                     paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() );
00264                     lineList.append( LineAttributesInfo( sourceIndex, a, b ) );
00265                 }
00266             }
00267 
00268             // wrap it up:
00269             laPreviousCell = laCell;
00270             lastPoint = point;
00271             lastExtraY = extraY;
00272             lastValue = value;
00273         }
00274         paintElements( ctx, textInfoList, lineList, policy );
00275     }
00276 }

Generated on Thu Mar 4 23:19:12 2010 for KD Chart 2 by  doxygen 1.5.4