KD Chart 2
[rev.2.5]
|
00001 /**************************************************************************** 00002 ** Copyright (C) 2001-2012 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.txt 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 const qreal xMin = 0.0; 00049 const qreal xMax = rowCount; 00050 qreal yMin = 0.0; 00051 qreal yMax = 0.0; 00052 00053 bool isFirst = true; 00054 for( int row = 0; row < rowCount; ++row ) { 00055 // calculate sum of values per column - Find out stacked Min/Max 00056 qreal stackedValues = 0.0; 00057 qreal negativeStackedValues = 0.0; 00058 for ( int col = 0; col < colCount; ++col ) { 00059 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 00060 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 00061 const double value = ISNAN( point.value ) ? 0.0 : point.value; 00062 00063 if ( value > 0.0 ) { 00064 stackedValues += value; 00065 } else { 00066 negativeStackedValues += value; 00067 } 00068 00069 // this is always true yMin can be 0 in case all values 00070 // are the same 00071 // same for yMax it can be zero if all values are negative 00072 if ( isFirst ) { 00073 yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; 00074 yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; 00075 isFirst = false; 00076 } else { 00077 yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); 00078 yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); 00079 } 00080 } 00081 } 00082 00083 // special cases 00084 if ( yMax == yMin ) { 00085 if ( yMin == 0.0 ) { 00086 yMax = 0.1; // we need at least a range 00087 } else if ( yMax < 0.0 ) { 00088 yMax = 0.0; // extend the range to zero 00089 } else if ( yMin > 0.0 ) { 00090 yMin = 0.0; // dito 00091 } 00092 } 00093 00094 return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax ) ); 00095 } 00096 00097 void StackedBarDiagram::paint( PaintContext* ctx ) 00098 { 00099 reverseMapper().clear(); 00100 00101 const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached 00102 00103 const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; 00104 const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); 00105 00106 const int rowCount = compressor().modelDataRows(); 00107 const int colCount = compressor().modelDataColumns(); 00108 00109 BarAttributes ba = diagram()->barAttributes(); 00110 qreal barWidth = 0; 00111 qreal maxDepth = 0; 00112 qreal width = boundRight.x() - boundLeft.x(); 00113 qreal groupWidth = width/ (rowCount + 2); 00114 qreal spaceBetweenBars = 0; 00115 qreal spaceBetweenGroups = 0; 00116 00117 if ( ba.useFixedBarWidth() ) { 00118 barWidth = ba.fixedBarWidth(); 00119 groupWidth += barWidth; 00120 00121 // Pending Michel set a min and max value for the groupWidth 00122 // related to the area.width 00123 if ( groupWidth < 0 ) 00124 groupWidth = 0; 00125 00126 if ( groupWidth * rowCount > width ) 00127 groupWidth = width / rowCount; 00128 } 00129 00130 // maxLimit: allow the space between bars to be larger until area.width() 00131 // is covered by the groups. 00132 qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); 00133 00134 00135 //Pending Michel: FixMe 00136 if ( ba.useFixedDataValueGap() ) { 00137 if ( width > maxLimit ) 00138 spaceBetweenBars += ba.fixedDataValueGap(); 00139 else 00140 spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); 00141 } 00142 00143 if ( ba.useFixedValueBlockGap() ) 00144 spaceBetweenGroups += ba.fixedValueBlockGap(); 00145 00146 calculateValueAndGapWidths( rowCount, colCount,groupWidth, 00147 barWidth, spaceBetweenBars, spaceBetweenGroups ); 00148 00149 LabelPaintCache lpc; 00150 for( int col = 0; col < colCount; ++col ) 00151 { 00152 qreal offset = spaceBetweenGroups; 00153 if( ba.useFixedBarWidth() ) 00154 offset -= ba.fixedBarWidth(); 00155 00156 if( offset < 0 ) 00157 offset = 0; 00158 00159 for( int row = 0; row < rowCount; ++row ) 00160 { 00161 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 00162 const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); 00163 00164 const QModelIndex index = attributesModel()->mapToSource( p.index ); 00165 ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); 00166 const qreal value = p.value; 00167 qreal stackedValues = 0.0; 00168 qreal key = 0.0; 00169 00170 if ( threeDAttrs.isEnabled() ) { 00171 if ( barWidth > 0 ) 00172 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; 00173 if ( barWidth <= 0 ) { 00174 barWidth = 0; 00175 maxDepth = offset - (width/rowCount); 00176 } 00177 } else { 00178 barWidth = (width - (offset*rowCount))/ rowCount ; 00179 } 00180 00181 for ( int k = col; k >= 0; --k ) 00182 { 00183 const CartesianDiagramDataCompressor::CachePosition position( row, k ); 00184 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 00185 if( !ISNAN( point.value ) && (( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 )) ) 00186 stackedValues += point.value; 00187 key = point.key; 00188 } 00189 00190 if (!ISNAN( value )) 00191 { 00192 const qreal usedDepth = threeDAttrs.depth(); 00193 00194 QPointF point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues ) ); 00195 00196 const qreal dy = point.y() - usedDepth; 00197 if ( dy < 0 ) { 00198 threeDAttrs.setDepth( point.y() - 1 ); 00199 diagram()->setThreeDBarAttributes( threeDAttrs ); 00200 } 00201 00202 point.rx() += offset / 2; 00203 const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( key, stackedValues - value ) ); 00204 const qreal barHeight = previousPoint.y() - point.y(); 00205 00206 const QRectF rect( point, QSizeF( barWidth , barHeight ) ); 00207 addLabel( &lpc, index, PositionPoints( rect ), Position::North, 00208 Position::South, value ); 00209 paintBars( ctx, index, rect, maxDepth ); 00210 } 00211 } 00212 } 00213 paintDataValueTextsAndMarkers( ctx, lpc, false ); 00214 }