KDChartNormalBarDiagram_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 "KDChartNormalBarDiagram_p.h"
00024 
00025 #include <QModelIndex>
00026 
00027 #include "KDChartBarDiagram.h"
00028 #include "KDChartTextAttributes.h"
00029 #include "KDChartAttributesModel.h"
00030 #include "KDChartAbstractCartesianDiagram.h"
00031 
00032 using namespace KDChart;
00033 using namespace std;
00034 
00035 NormalBarDiagram::NormalBarDiagram( BarDiagram* d )
00036     : BarDiagramType( d )
00037 {
00038 }
00039 
00040 BarDiagram::BarType NormalBarDiagram::type() const
00041 {
00042     return BarDiagram::Normal;
00043 }
00044 
00045 const QPair<QPointF, QPointF> NormalBarDiagram::calculateDataBoundaries() const
00046 {
00047     const int rowCount = compressor().modelDataRows();
00048     const int colCount = compressor().modelDataColumns();
00049 
00050     double xMin = 0.0;
00051     double xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0;
00052     double yMin = 0.0, yMax = 0.0;
00053 
00054     bool bStarting = true;
00055     for ( int column = 0; column < colCount; ++column )
00056     {
00057         for ( int row = 0; row < rowCount; ++row )
00058         {
00059             const CartesianDiagramDataCompressor::CachePosition position( row, column );
00060             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00061             const double value = ISNAN( point.value ) ? 0.0 : point.value;
00062             // this is always true yMin can be 0 in case all values
00063             // are the same
00064             // same for yMax it can be zero if all values are negative
00065             if( bStarting ){
00066                 yMin = value;
00067                 yMax = value;
00068                 bStarting = false;
00069             }else{
00070                 yMin = qMin( yMin, value );
00071                 yMax = qMax( yMax, value );
00072             }
00073         }
00074     }
00075 
00076     // special cases
00077     if (  yMax == yMin ) {
00078         if ( yMin == 0.0 )
00079             yMax = 0.1; //we need at least a range
00080         else if( yMax < 0.0 )
00081             yMax = 0.0; // they are the same and negative
00082         else if( yMin > 0.0 )
00083             yMin = 0.0; // they are the same but positive
00084     }
00085     const QPointF bottomLeft ( QPointF( xMin, yMin ) );
00086     const QPointF topRight ( QPointF( xMax, yMax ) );
00087 
00088     return QPair< QPointF, QPointF >( bottomLeft,  topRight );
00089 }
00090 
00091 void NormalBarDiagram::paint(  PaintContext* ctx )
00092 {
00093     reverseMapper().clear();
00094 
00095     const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached
00096 
00097     const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ;
00098     const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second );
00099 
00100     const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
00101     const int colCount = attributesModel()->columnCount(attributesModelRootIndex());
00102 
00103     BarAttributes ba = diagram()->barAttributes( diagram()->model()->index( 0, 0, diagram()->rootIndex() ) );
00104     double barWidth = 0;
00105     double maxDepth = 0;
00106     double width = boundRight.x() - boundLeft.x();
00107     double groupWidth = width / (rowCount + 2);
00108     double spaceBetweenBars = 0;
00109     double spaceBetweenGroups = 0;
00110 
00111     if ( ba.useFixedBarWidth() ) {
00112 
00113         barWidth = ba.fixedBarWidth();
00114         groupWidth += barWidth;
00115 
00116         // Pending Michel set a min and max value for the groupWidth
00117         // related to the area.width
00118         if ( groupWidth < 0 )
00119             groupWidth = 0;
00120 
00121         if ( groupWidth  * rowCount > width )
00122             groupWidth = width / rowCount;
00123     }
00124 
00125     // maxLimit: allow the space between bars to be larger until area.width()
00126     // is covered by the groups.
00127     double maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) );
00128 
00129     //Pending Michel: FixMe
00130     if ( ba.useFixedDataValueGap() ) {
00131         if ( width > maxLimit )
00132             spaceBetweenBars += ba.fixedDataValueGap();
00133         else
00134             spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1);
00135     }
00136 
00137     if ( ba.useFixedValueBlockGap() ) {
00138         spaceBetweenGroups += ba.fixedValueBlockGap();
00139     }
00140 
00141     calculateValueAndGapWidths( rowCount, colCount,groupWidth,
00142                                 barWidth, spaceBetweenBars, spaceBetweenGroups );
00143 
00144     DataValueTextInfoList list;
00145 
00146     for( int row = 0; row < rowCount; ++row )
00147     {
00148         double offset = -groupWidth/2 + spaceBetweenGroups/2;
00149 
00150         if ( ba.useFixedDataValueGap() )
00151         {
00152             if ( spaceBetweenBars > 0 )
00153             {
00154                 if ( width > maxLimit )
00155                     offset -= ba.fixedDataValueGap();
00156                 else
00157                     offset -= ((width/rowCount) - groupWidth)/(colCount-1);
00158 
00159             }
00160             else
00161             {
00162                 offset += barWidth/2;
00163             }
00164         }
00165 
00166         for( int column=0; column< colCount; ++column )
00167         {
00168             // paint one group
00169             const CartesianDiagramDataCompressor::CachePosition position( row,  column );
00170             const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
00171             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
00172             const qreal value = point.value;//attributesModel()->data( sourceIndex ).toDouble();
00173             if ( ! point.hidden && !ISNAN( value ) ) {
00174                 QPointF topPoint = ctx->coordinatePlane()->translate( QPointF( point.key + 0.5, value ) );
00175                 QPointF bottomPoint =  ctx->coordinatePlane()->translate( QPointF( point.key, 0 ) );
00176                 const double barHeight = bottomPoint.y() - topPoint.y();
00177                 topPoint.setX( topPoint.x() + offset );
00178                 const QRectF rect( topPoint, QSizeF( barWidth, barHeight ) );
00179                 appendDataValueTextInfoToList( diagram(), list, sourceIndex, PositionPoints( rect ),
00180                                                Position::NorthWest, Position::SouthEast,
00181                                                point.value );
00182                 paintBars( ctx, sourceIndex, rect, maxDepth );
00183             }
00184             offset += barWidth + spaceBetweenBars;
00185         }
00186     }
00187     paintDataValueTextsAndMarkers(  diagram(),  ctx,  list,  false );
00188 }