KD Chart 2 [rev.2.4]
|
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 "KDChartStackedLyingBarDiagram_p.h" 00030 00031 using namespace KDChart; 00032 00033 StackedLyingBarDiagram::StackedLyingBarDiagram( BarDiagram* d ) 00034 : BarDiagramType( d ) 00035 { 00036 } 00037 00038 BarDiagram::BarType StackedLyingBarDiagram::type() const 00039 { 00040 return BarDiagram::Stacked; 00041 } 00042 00043 const QPair<QPointF, QPointF> StackedLyingBarDiagram::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( yMin, xMin ) ); 00091 const QPointF topRight ( QPointF( yMax, xMax ) ); 00092 00093 return QPair< QPointF, QPointF >( bottomLeft, topRight ); 00094 } 00095 00096 void StackedLyingBarDiagram::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 = boundLeft.y() - boundRight.y(); 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 ( ctx->rectangle().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 row = rowCount - 1; row >= 0; --row ) 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 col = 0; col < colCount; ++col ) 00159 { 00160 double threeDOffset = 0.0; 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 double value = p.value; 00167 double stackedValues = 0.0; 00168 double key = 0.0; 00169 00170 if ( threeDAttrs.isEnabled() ){ 00171 if ( barWidth > 0 ) { 00172 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; 00173 threeDOffset = threeDAttrs.depth(); 00174 } 00175 if ( barWidth <= 0 ) { 00176 barWidth = 0.1; 00177 threeDOffset = (width - (offset*rowCount))/ rowCount; 00178 } 00179 }else{ 00180 barWidth = (width - (offset*rowCount))/ rowCount; 00181 } 00182 00183 for ( int k = col; k >= 0; --k ) 00184 { 00185 const CartesianDiagramDataCompressor::CachePosition position( row, k ); 00186 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 00187 if( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) ) 00188 stackedValues += point.value; 00189 key = point.key; 00190 } 00191 QPointF point = ctx->coordinatePlane()->translate( QPointF( stackedValues, rowCount - key ) ); 00192 point.ry() += offset / 2 + threeDOffset; 00193 const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( stackedValues - value, rowCount - key ) ); 00194 const double barHeight = point.x() - previousPoint.x(); 00195 point.rx() -= barHeight; 00196 00197 const QRectF rect( point, QSizeF( barHeight , barWidth ) ); 00198 appendDataValueTextInfoToList( diagram(), list, index, PositionPoints( rect ), 00199 Position::NorthEast, Position::SouthWest, 00200 value ); 00201 paintBars( ctx, index, rect, maxDepth ); 00202 } 00203 } 00204 paintDataValueTextsAndMarkers( diagram(), ctx, list, false ); 00205 }