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