File indexing completed on 2024-12-15 04:02:26

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