File indexing completed on 2024-12-15 04:02:27
0001 /* 0002 * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. 0003 * 0004 * This file is part of the KD Chart library. 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "KChartStackedBarDiagram_p.h" 0010 #include <QModelIndex> 0011 0012 #include "KChartBarDiagram.h" 0013 #include "KChartTextAttributes.h" 0014 #include "KChartAttributesModel.h" 0015 #include "KChartAbstractCartesianDiagram.h" 0016 0017 using namespace KChart; 0018 0019 StackedBarDiagram::StackedBarDiagram( BarDiagram* d ) 0020 : BarDiagramType( d ) 0021 { 0022 } 0023 0024 BarDiagram::BarType StackedBarDiagram::type() const 0025 { 0026 return BarDiagram::Stacked; 0027 } 0028 0029 const QPair<QPointF, QPointF> StackedBarDiagram::calculateDataBoundaries() const 0030 { 0031 const int rowCount = compressor().modelDataRows(); 0032 const int colCount = compressor().modelDataColumns(); 0033 0034 const qreal xMin = 0.0; 0035 const qreal xMax = rowCount; 0036 qreal yMin = 0.0; 0037 qreal yMax = 0.0; 0038 0039 bool isFirst = true; 0040 for ( int row = 0; row < rowCount; ++row ) { 0041 // calculate sum of values per column - Find out stacked Min/Max 0042 qreal stackedValues = 0.0; 0043 qreal negativeStackedValues = 0.0; 0044 for ( int col = 0; col < colCount; ++col ) { 0045 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 0046 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 0047 const double value = ISNAN( point.value ) ? 0.0 : point.value; 0048 0049 if ( value > 0.0 ) { 0050 stackedValues += value; 0051 } else { 0052 negativeStackedValues += value; 0053 } 0054 0055 // this is always true yMin can be 0 in case all values 0056 // are the same 0057 // same for yMax it can be zero if all values are negative 0058 if ( isFirst ) { 0059 yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; 0060 yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; 0061 isFirst = false; 0062 } else { 0063 yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); 0064 yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); 0065 } 0066 } 0067 } 0068 0069 // special cases 0070 if ( yMax == yMin ) { 0071 if ( yMin == 0.0 ) { 0072 yMax = 0.1; // we need at least a range 0073 } else if ( yMax < 0.0 ) { 0074 yMax = 0.0; // extend the range to zero 0075 } else if ( yMin > 0.0 ) { 0076 yMin = 0.0; // dito 0077 } 0078 } 0079 0080 return QPair< QPointF, QPointF >( QPointF( xMin, yMin ), QPointF( xMax, yMax ) ); 0081 } 0082 0083 void StackedBarDiagram::paint( PaintContext* ctx ) 0084 { 0085 reverseMapper().clear(); 0086 0087 const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached 0088 0089 const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; 0090 const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); 0091 0092 const int rowCount = compressor().modelDataRows(); 0093 const int colCount = compressor().modelDataColumns(); 0094 0095 BarAttributes ba = diagram()->barAttributes(); 0096 qreal barWidth = 0; 0097 qreal maxDepth = 0; 0098 qreal width = boundRight.x() - boundLeft.x(); 0099 qreal groupWidth = width / rowCount; 0100 qreal spaceBetweenBars = 0; 0101 qreal spaceBetweenGroups = 0; 0102 0103 if ( ba.useFixedBarWidth() ) { 0104 barWidth = ba.fixedBarWidth(); 0105 groupWidth += barWidth; 0106 0107 // Pending Michel set a min and max value for the groupWidth 0108 // related to the area.width 0109 if ( groupWidth < 0 ) 0110 groupWidth = 0; 0111 0112 if ( groupWidth * rowCount > width ) 0113 groupWidth = width / rowCount; 0114 } 0115 0116 // maxLimit: allow the space between bars to be larger until area.width() 0117 // is covered by the groups. 0118 qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); 0119 0120 0121 //Pending Michel: FixMe 0122 if ( ba.useFixedDataValueGap() ) { 0123 if ( width > maxLimit ) 0124 spaceBetweenBars += ba.fixedDataValueGap(); 0125 else 0126 spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); 0127 } 0128 0129 if ( ba.useFixedValueBlockGap() ) 0130 spaceBetweenGroups += ba.fixedValueBlockGap(); 0131 0132 calculateValueAndGapWidths( rowCount, colCount,groupWidth, 0133 barWidth, spaceBetweenBars, spaceBetweenGroups ); 0134 0135 LabelPaintCache lpc; 0136 for ( int col = 0; col < colCount; ++col ) 0137 { 0138 qreal offset = spaceBetweenGroups; 0139 if ( ba.useFixedBarWidth() ) 0140 offset -= ba.fixedBarWidth(); 0141 0142 CartesianCoordinatePlane *plane = static_cast<CartesianCoordinatePlane*>(ctx->coordinatePlane()); 0143 if (plane->isHorizontalRangeReversed()) { 0144 if (offset > 0) { 0145 offset = 0; 0146 } 0147 } else if ( offset < 0 ) { 0148 offset = 0; 0149 } 0150 for ( int row = 0; row < rowCount; ++row ) 0151 { 0152 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 0153 const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); 0154 0155 const QModelIndex index = attributesModel()->mapToSource( p.index ); 0156 ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); 0157 const qreal value = p.value; 0158 qreal stackedValues = 0.0; 0159 qreal key = 0.0; 0160 0161 if ( threeDAttrs.isEnabled() ) { 0162 if ( barWidth > 0 ) 0163 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; 0164 if ( barWidth <= 0 ) { 0165 barWidth = 0; 0166 maxDepth = offset - (width/rowCount); 0167 } 0168 } else { 0169 barWidth = (width - (offset*rowCount))/ rowCount ; 0170 } 0171 0172 for ( int k = col; k >= 0; --k ) 0173 { 0174 const CartesianDiagramDataCompressor::CachePosition position( row, k ); 0175 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 0176 if ( !ISNAN( point.value ) && (( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 )) ) 0177 stackedValues += point.value; 0178 key = point.key; 0179 } 0180 0181 if (!ISNAN( value )) 0182 { 0183 const qreal usedDepth = threeDAttrs.depth(); 0184 0185 QPointF point = ctx->coordinatePlane()->translate( QPointF( key, stackedValues ) ); 0186 0187 const qreal dy = point.y() - usedDepth; 0188 if ( dy < 0 ) { 0189 threeDAttrs.setDepth( point.y() - 1 ); 0190 diagram()->setThreeDBarAttributes( threeDAttrs ); 0191 } 0192 0193 point.rx() += offset / 2; 0194 const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( key, stackedValues - value ) ); 0195 const qreal barHeight = previousPoint.y() - point.y(); 0196 0197 const QRectF rect( point, QSizeF( barWidth , barHeight ) ); 0198 m_private->addLabel( &lpc, index, nullptr, PositionPoints( rect ), Position::North, 0199 Position::South, value ); 0200 paintBars( ctx, index, rect, maxDepth ); 0201 } 0202 } 0203 } 0204 m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); 0205 }