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 }