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 "KChartStackedLyingBarDiagram_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 StackedLyingBarDiagram::StackedLyingBarDiagram( BarDiagram* d ) 0020 : BarDiagramType( d ) 0021 { 0022 } 0023 0024 BarDiagram::BarType StackedLyingBarDiagram::type() const 0025 { 0026 return BarDiagram::Stacked; 0027 } 0028 0029 const QPair<QPointF, QPointF> StackedLyingBarDiagram::calculateDataBoundaries() const 0030 { 0031 const int rowCount = compressor().modelDataRows(); 0032 const int colCount = compressor().modelDataColumns(); 0033 0034 const qreal xMin = 0; 0035 const qreal xMax = rowCount; 0036 qreal yMin = 0; 0037 qreal yMax = 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 0048 if ( point.value > 0.0 ) 0049 stackedValues += point.value; 0050 else 0051 negativeStackedValues += point.value; 0052 0053 // this is always true yMin can be 0 in case all values 0054 // are the same 0055 // same for yMax it can be zero if all values are negative 0056 if ( isFirst ) { 0057 yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues; 0058 yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues; 0059 isFirst = false; 0060 } else { 0061 yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues ); 0062 yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues ); 0063 } 0064 } 0065 } 0066 0067 // special cases 0068 if ( yMax == yMin ) { 0069 if ( yMin == 0.0 ) { 0070 yMax = 0.1; // we need at least a range 0071 } else if ( yMax < 0.0 ) { 0072 yMax = 0.0; // extend the range to zero 0073 } else if ( yMin > 0.0 ) { 0074 yMin = 0.0; // dito 0075 } 0076 } 0077 0078 return QPair< QPointF, QPointF >( QPointF( yMin, xMin ), QPointF( yMax, xMax ) ); 0079 } 0080 0081 void StackedLyingBarDiagram::paint( PaintContext* ctx ) 0082 { 0083 reverseMapper().clear(); 0084 0085 const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached 0086 0087 const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; 0088 const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); 0089 0090 const int rowCount = compressor().modelDataRows(); 0091 const int colCount = compressor().modelDataColumns(); 0092 0093 BarAttributes ba = diagram()->barAttributes(); 0094 qreal barWidth = 0; 0095 qreal maxDepth = 0; 0096 qreal width = boundLeft.y() - boundRight.y(); 0097 qreal groupWidth = width / rowCount; 0098 qreal spaceBetweenBars = 0; 0099 qreal spaceBetweenGroups = 0; 0100 0101 if ( ba.useFixedBarWidth() ) { 0102 barWidth = ba.fixedBarWidth(); 0103 groupWidth += barWidth; 0104 0105 // Pending Michel set a min and max value for the groupWidth 0106 // related to the area.width 0107 if ( groupWidth < 0 ) 0108 groupWidth = 0; 0109 0110 if ( groupWidth * rowCount > width ) 0111 groupWidth = width / rowCount; 0112 } 0113 0114 // maxLimit: allow the space between bars to be larger until area.width() 0115 // is covered by the groups. 0116 qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); 0117 0118 0119 //Pending Michel: FixMe 0120 if ( ba.useFixedDataValueGap() ) { 0121 if ( ctx->rectangle().width() > maxLimit ) 0122 spaceBetweenBars += ba.fixedDataValueGap(); 0123 else 0124 spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1); 0125 } 0126 0127 if ( ba.useFixedValueBlockGap() ) 0128 spaceBetweenGroups += ba.fixedValueBlockGap(); 0129 0130 calculateValueAndGapWidths( rowCount, colCount,groupWidth, 0131 barWidth, spaceBetweenBars, spaceBetweenGroups ); 0132 0133 LabelPaintCache lpc; 0134 for ( int row = rowCount - 1; row >= 0; --row ) 0135 { 0136 qreal offset = spaceBetweenGroups; 0137 if ( ba.useFixedBarWidth() ) 0138 offset -= ba.fixedBarWidth(); 0139 0140 CartesianCoordinatePlane *plane = static_cast<CartesianCoordinatePlane*>(ctx->coordinatePlane()); 0141 if (plane->isVerticalRangeReversed()) { 0142 if (offset > 0) { 0143 offset = 0; 0144 } 0145 } else if ( offset < 0 ) { 0146 offset = 0; 0147 } 0148 for ( int col = 0; col < colCount; ++col ) 0149 { 0150 qreal threeDOffset = 0.0; 0151 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 0152 const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); 0153 0154 const QModelIndex index = attributesModel()->mapToSource( p.index ); 0155 ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index ); 0156 const qreal value = p.value; 0157 qreal stackedValues = 0.0; 0158 qreal key = 0.0; 0159 0160 if ( threeDAttrs.isEnabled() ) { 0161 if ( barWidth > 0 ) { 0162 barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount; 0163 threeDOffset = threeDAttrs.depth(); 0164 } 0165 if ( barWidth <= 0 ) { 0166 barWidth = 0.1; 0167 threeDOffset = (width - (offset*rowCount))/ rowCount; 0168 } 0169 } else { 0170 barWidth = (width - (offset*rowCount))/ rowCount; 0171 } 0172 0173 for ( int k = col; k >= 0; --k ) 0174 { 0175 const CartesianDiagramDataCompressor::CachePosition position( row, k ); 0176 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 0177 if ( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) ) 0178 stackedValues += point.value; 0179 key = point.key; 0180 } 0181 QPointF point = ctx->coordinatePlane()->translate( QPointF( stackedValues, key + 1 ) ); 0182 point.ry() += offset / 2 + threeDOffset; 0183 const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( stackedValues - value, key + 1 ) ); 0184 const qreal barHeight = point.x() - previousPoint.x(); 0185 point.rx() -= barHeight; 0186 0187 const QRectF rect = QRectF( point, QSizeF( barHeight , barWidth ) ).translated( 1, 0 ); 0188 m_private->addLabel( &lpc, index, nullptr, PositionPoints( rect ), Position::North, 0189 Position::South, value ); 0190 paintBars( ctx, index, rect, maxDepth ); 0191 } 0192 } 0193 m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); 0194 }