KDChartNormalLineDiagram_p.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB.  All rights reserved.
00003 **
00004 ** This file is part of the KD Chart library.
00005 **
00006 ** Licensees holding valid commercial KD Chart licenses may use this file in
00007 ** accordance with the KD Chart Commercial License Agreement provided with
00008 ** the Software.
00009 **
00010 **
00011 ** This file may be distributed and/or modified under the terms of the
00012 ** GNU General Public License version 2 and version 3 as published by the
00013 ** Free Software Foundation and appearing in the file LICENSE.GPL included.
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 ** Contact info@kdab.com if any conditions of this licensing are not
00019 ** clear to you.
00020 **
00021 **********************************************************************/
00022 
00023 #include <limits>
00024 
00025 #include <QAbstractItemModel>
00026 
00027 #include "KDChartBarDiagram.h"
00028 #include "KDChartLineDiagram.h"
00029 #include "KDChartTextAttributes.h"
00030 #include "KDChartAttributesModel.h"
00031 #include "KDChartAbstractCartesianDiagram.h"
00032 #include "KDChartNormalLineDiagram_p.h"
00033 
00034 using namespace KDChart;
00035 using namespace std;
00036 
00037 NormalLineDiagram::NormalLineDiagram( LineDiagram* d )
00038     : LineDiagramType( d )
00039 {
00040 }
00041 
00042 LineDiagram::LineType NormalLineDiagram::type() const
00043 {
00044     return LineDiagram::Normal;
00045 }
00046 
00047 const QPair< QPointF, QPointF > NormalLineDiagram::calculateDataBoundaries() const
00048 {
00049     const int rowCount = compressor().modelDataRows();
00050     const int colCount = compressor().modelDataColumns();
00051     const double xMin = 0.0;
00052     double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0;
00053     if ( !diagram()->centerDataPoints() && diagram()->model() )
00054         xMax -= 1;
00055     double yMin = std::numeric_limits< double >::quiet_NaN();
00056     double yMax = std::numeric_limits< double >::quiet_NaN();
00057 
00058     for( int column = 0; column < colCount; ++column )
00059     {
00060         for ( int row = 0; row < rowCount; ++row )
00061         {
00062             const CartesianDiagramDataCompressor::CachePosition position( row, column );
00063             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00064             const double value = ISNAN( point.value ) ? 0.0 : point.value;
00065 
00066             if ( ISNAN( yMin ) ) {
00067                     yMin = value;
00068                     yMax = value;
00069             } else {
00070                 yMin = qMin( yMin, value );
00071                 yMax = qMax( yMax, value );
00072             }
00073         }
00074     }
00075 
00076     // NOTE: calculateDataBoundaries must return the *real* data boundaries!
00077     //       i.e. we may NOT fake yMin to be qMin( 0.0, yMin )
00078     //       (khz, 2008-01-24)
00079     const QPointF bottomLeft( QPointF( xMin, yMin ) );
00080     const QPointF topRight( QPointF( xMax, yMax ) );
00081     return QPair< QPointF, QPointF >( bottomLeft, topRight );
00082 }
00083 
00084 void NormalLineDiagram::paint( PaintContext* ctx )
00085 {
00086     reverseMapper().clear();
00087     Q_ASSERT( dynamic_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() ) );
00088     CartesianCoordinatePlane* plane = static_cast<CartesianCoordinatePlane*>( ctx->coordinatePlane() );
00089     const int columnCount = compressor().modelDataColumns();
00090     const int rowCount = compressor().modelDataRows();
00091     if ( columnCount == 0 || rowCount == 0 ) return; // maybe blank out the area?
00092 
00093 // FIXME integrate column index retrieval to compressor:
00094 // the compressor should only pass through visiblel columns
00095     int maxFound = 0;
00096 //     {   // find the last column number that is not hidden
00097 //         for( int column =  datasetDimension() - 1;
00098 //              column <  columnCount;
00099 //              column += datasetDimension() )
00100 //             if( ! diagram()->isHidden( column ) )
00101 //                 maxFound = column;
00102 //     }
00103     maxFound = columnCount;
00104     // ^^^ temp
00105 
00106     // Reverse order of data sets?
00107     bool rev = diagram()->reverseDatasetOrder();
00108     for( int column = rev ? columnCount - 1 : 0;
00109          rev ? (column >= 0) : (column < columnCount);
00110          rev ? --column : ++column ) {
00111         DataValueTextInfoList textInfoList;
00112         LineAttributesInfoList lineList;
00113         LineAttributes laPreviousCell;
00114         CartesianDiagramDataCompressor::DataPoint lastPoint;
00115         qreal lastAreaBoundingValue = 0;
00116 
00117         // Get min. y value, used as lower or upper bounding for area highlighting
00118         const qreal minYValue = qMin(plane->visibleDataRange().bottom(), plane->visibleDataRange().top());
00119 
00120         CartesianDiagramDataCompressor::CachePosition previousCellPosition;
00121         for ( int row = 0; row < rowCount; ++row ) {
00122             const CartesianDiagramDataCompressor::CachePosition position( row, column );
00123             // get where to draw the line from:
00124             CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00125 
00126             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
00127 
00128             const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
00129             const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();
00130 
00131             // lower or upper bounding for the highlighted area
00132             qreal areaBoundingValue;
00133             if ( laCell.areaBoundingDataset() != -1 ) {
00134                 const CartesianDiagramDataCompressor::CachePosition areaBoundingCachePosition( row, laCell.areaBoundingDataset() );
00135                 areaBoundingValue = compressor().data( areaBoundingCachePosition ).value;
00136             } else
00137                 // Use min. y value (i.e. zero line in most cases) if no bounding dataset is set
00138                 areaBoundingValue = minYValue;
00139 
00140             if( ISNAN( point.value ) )
00141             {
00142                 switch( policy )
00143                 {
00144                 case LineAttributes::MissingValuesAreBridged:
00145                     // we just bridge both values
00146                     continue;
00147                 case LineAttributes::MissingValuesShownAsZero:
00148                     // set it to zero
00149                     point.value = 0.0;
00150                     break;
00151                 case LineAttributes::MissingValuesHideSegments:
00152                     // they're just hidden
00153                     break;
00154                 default:
00155                     break;
00156                     // hm....
00157                 }
00158             }
00159 
00160             // area corners, a + b are the line ends:
00161             const QPointF a( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastPoint.value ) ) );
00162             const QPointF b( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, point.value ) ) );
00163             const QPointF c( plane->translate( QPointF( diagram()->centerDataPoints() ? lastPoint.key + 0.5 : lastPoint.key, lastAreaBoundingValue ) ) );
00164             const QPointF d( plane->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, areaBoundingValue ) ) );
00165             // add the line to the list:
00166            // add data point labels:
00167             const PositionPoints pts = PositionPoints( b, a, d, c );
00168             // if necessary, add the area to the area list:
00169             QList<QPolygonF> areas;
00170             if ( !ISNAN( point.value ) && !ISNAN( lastPoint.value ) && laCell.displayArea() ) {
00171                 areas << ( QPolygonF() << a << b << d << c );//polygon;
00172             }
00173             // add the pieces to painting if this is not hidden:
00174             if ( ! point.hidden && !ISNAN( point.value ) )
00175             {
00176                 appendDataValueTextInfoToList( diagram(), textInfoList, sourceIndex, &position,
00177                                                pts, Position::NorthWest, Position::SouthWest,
00178                                                point.value );
00179                 paintAreas( ctx, attributesModel()->mapToSource( lastPoint.index ), areas, laCell.transparency() );
00180                 // position 0 is not really painted, since it takes two points to make a line :-)
00181                 if( row > 0 && !ISNAN( lastPoint.value ) )
00182                     lineList.append( LineAttributesInfo( sourceIndex, a, b ) );
00183             }
00184 
00185             // wrap it up:
00186             previousCellPosition = position;
00187             laPreviousCell = laCell;
00188             lastAreaBoundingValue = areaBoundingValue;
00189             lastPoint = point;
00190         }
00191 
00192         LineAttributes::MissingValuesPolicy policy = LineAttributes::MissingValuesAreBridged; //unused
00193         paintElements( ctx, textInfoList, lineList, policy );
00194     }
00195 }