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 "KChartPercentLyingBarDiagram_p.h" 0010 0011 #include <QModelIndex> 0012 0013 #include "KChartBarDiagram.h" 0014 #include "KChartTextAttributes.h" 0015 #include "KChartAttributesModel.h" 0016 #include "KChartAbstractCartesianDiagram.h" 0017 0018 using namespace KChart; 0019 0020 PercentLyingBarDiagram::PercentLyingBarDiagram( BarDiagram* d ) 0021 : BarDiagramType( d ) 0022 { 0023 } 0024 0025 BarDiagram::BarType PercentLyingBarDiagram::type() const 0026 { 0027 return BarDiagram::Percent; 0028 } 0029 0030 const QPair<QPointF, QPointF> PercentLyingBarDiagram::calculateDataBoundaries() const 0031 { 0032 //const int rowCount = compressor().modelDataRows(); 0033 //const int colCount = compressor().modelDataColumns(); 0034 0035 const qreal xMin = 0; 0036 const qreal xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0; 0037 qreal yMin = 0.0, yMax = 100.0; 0038 /*for ( int col = 0; col < colCount; ++col ) 0039 { 0040 for ( int row = 0; row < rowCount; ++row ) 0041 { 0042 // Ordinate should begin at 0 the max value being the 100% pos 0043 const QModelIndex idx = diagram()->model()->index( row, col, diagram()->rootIndex() ); 0044 // only positive values are handled 0045 qreal value = diagram()->model()->data( idx ).toReal(); 0046 if ( value > 0 ) 0047 yMax = qMax( yMax, value ); 0048 } 0049 }*/ 0050 // special cases 0051 if ( yMax == yMin ) { 0052 if ( yMin == 0.0 ) 0053 yMax = 0.1; //we need at least a range 0054 else 0055 yMax = 0.0; // they are the same but negative 0056 } 0057 const QPointF bottomLeft( QPointF( yMin, xMin ) ); 0058 const QPointF topRight( QPointF( yMax, xMax ) ); 0059 0060 //qDebug() << "BarDiagram::calculateDataBoundaries () returns ( " << bottomLeft << topRight <<")"; 0061 return QPair< QPointF, QPointF >( bottomLeft, topRight ); 0062 } 0063 0064 void PercentLyingBarDiagram::paint( PaintContext* ctx ) 0065 { 0066 reverseMapper().clear(); 0067 0068 const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached 0069 0070 const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ; 0071 const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second ); 0072 0073 const int rowCount = compressor().modelDataRows(); 0074 const int colCount = compressor().modelDataColumns(); 0075 0076 BarAttributes ba = diagram()->barAttributes(); 0077 qreal barWidth = 0; 0078 qreal maxDepth = 0; 0079 qreal width = boundLeft.y() - boundRight.y(); 0080 qreal groupWidth = width / rowCount; 0081 qreal spaceBetweenBars = 0; 0082 qreal spaceBetweenGroups = 0; 0083 0084 if ( ba.useFixedBarWidth() ) { 0085 barWidth = ba.fixedBarWidth(); 0086 groupWidth += barWidth; 0087 0088 // Pending Michel set a min and max value for the groupWidth 0089 // related to the area.width 0090 if ( groupWidth < 0 ) 0091 groupWidth = 0; 0092 0093 if ( groupWidth * rowCount > width ) 0094 groupWidth = width / rowCount; 0095 } 0096 0097 // maxLimit: allow the space between bars to be larger until area.width() 0098 // is covered by the groups. 0099 qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) ); 0100 0101 0102 //Pending Michel: FixMe 0103 if ( ba.useFixedDataValueGap() ) { 0104 if ( width > maxLimit ) 0105 spaceBetweenBars += ba.fixedDataValueGap(); 0106 else 0107 spaceBetweenBars = ((ctx->rectangle().width()/rowCount) - groupWidth)/(colCount-1); 0108 } 0109 0110 if ( ba.useFixedValueBlockGap() ) 0111 spaceBetweenGroups += ba.fixedValueBlockGap(); 0112 0113 calculateValueAndGapWidths( rowCount, colCount,groupWidth, 0114 barWidth, spaceBetweenBars, spaceBetweenGroups ); 0115 0116 LabelPaintCache lpc; 0117 const qreal maxValue = 100.0; // always 100 % 0118 qreal sumValues = 0; 0119 QVector <qreal > sumValuesVector; 0120 0121 //calculate sum of values for each column and store 0122 for ( int row = 0; row < rowCount; ++row ) 0123 { 0124 for ( int col = 0; col < colCount; ++col ) 0125 { 0126 const CartesianDiagramDataCompressor::CachePosition position( row, col ); 0127 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 0128 //if ( point.value > 0 ) 0129 sumValues += qMax( point.value, -point.value ); 0130 if ( col == colCount - 1 ) { 0131 sumValuesVector << sumValues ; 0132 sumValues = 0; 0133 } 0134 } 0135 } 0136 0137 // calculate stacked percent value 0138 for ( int curRow = rowCount - 1; curRow >= 0; --curRow ) 0139 { 0140 qreal offset = spaceBetweenGroups; 0141 if ( ba.useFixedBarWidth() ) 0142 offset -= ba.fixedBarWidth(); 0143 0144 CartesianCoordinatePlane *plane = static_cast<CartesianCoordinatePlane*>(ctx->coordinatePlane()); 0145 if (plane->isVerticalRangeReversed()) { 0146 if (offset > 0) { 0147 offset = 0; 0148 } 0149 } else if ( offset < 0 ) { 0150 offset = 0; 0151 } 0152 for ( int col = 0; col < colCount ; ++col ) 0153 { 0154 qreal threeDOffset = 0.0; 0155 const CartesianDiagramDataCompressor::CachePosition position( curRow, col ); 0156 const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position ); 0157 QModelIndex sourceIndex = attributesModel()->mapToSource( p.index ); 0158 ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( sourceIndex ); 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 const qreal value = qMax( p.value, -p.value ); 0174 qreal stackedValues = 0.0; 0175 qreal key = 0.0; 0176 0177 // calculate stacked percent value 0178 // we only take in account positives values for now. 0179 for ( int k = col; k >= 0 ; --k ) 0180 { 0181 const CartesianDiagramDataCompressor::CachePosition position( curRow, k ); 0182 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position ); 0183 stackedValues += qMax( point.value, -point.value ); 0184 key = point.key; 0185 } 0186 0187 QPointF point, previousPoint; 0188 if ( sumValuesVector.at( curRow ) != 0 && value > 0 ) { 0189 QPointF dataPoint( ( stackedValues / sumValuesVector.at( curRow ) * maxValue ), key + 1 ); 0190 point = ctx->coordinatePlane()->translate( dataPoint ); 0191 point.ry() += offset / 2 + threeDOffset; 0192 0193 previousPoint = ctx->coordinatePlane()->translate( QPointF( ( ( stackedValues - value) / sumValuesVector.at( curRow ) * maxValue ), key + 1 ) ); 0194 } 0195 0196 const qreal barHeight = point.x() - previousPoint.x(); 0197 0198 point.setX( point.x() - barHeight ); 0199 0200 const QRectF rect = QRectF( point, QSizeF( barHeight, barWidth ) ).translated( 1, 0 ); 0201 m_private->addLabel( &lpc, sourceIndex, nullptr, PositionPoints( rect ), Position::North, 0202 Position::South, value ); 0203 paintBars( ctx, sourceIndex, rect, maxDepth ); 0204 } 0205 } 0206 m_private->paintDataValueTextsAndMarkers( ctx, lpc, false ); 0207 }