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 }