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 "KChartPercentLineDiagram_p.h"
0010 
0011 #include <QModelIndex>
0012 
0013 #include "KChartBarDiagram.h"
0014 #include "KChartLineDiagram.h"
0015 #include "KChartTextAttributes.h"
0016 #include "KChartAttributesModel.h"
0017 #include "KChartAbstractCartesianDiagram.h"
0018 #include "PaintingHelpers_p.h"
0019 
0020 using namespace KChart;
0021 using namespace std;
0022 
0023 PercentLineDiagram::PercentLineDiagram( LineDiagram* d )
0024     : LineDiagramType( d )
0025 {
0026 }
0027 
0028 LineDiagram::LineType PercentLineDiagram::type() const
0029 {
0030     return LineDiagram::Percent;
0031 }
0032 
0033 const QPair<QPointF, QPointF> PercentLineDiagram::calculateDataBoundaries() const
0034 {
0035     const qreal xMin = 0.0;
0036     qreal xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0;
0037     if ( !diagram()->centerDataPoints() && diagram()->model() )
0038         xMax -= 1;
0039     const qreal yMin = 0.0;
0040     const qreal yMax = 100.0;
0041 
0042     QPointF bottomLeft( QPointF( xMin, yMin ) );
0043     QPointF topRight( QPointF( xMax, yMax ) );
0044     return QPair<QPointF, QPointF> ( bottomLeft, topRight );
0045 }
0046 
0047 void PercentLineDiagram::paint( PaintContext* ctx )
0048 {
0049     reverseMapper().clear();
0050 
0051     const int columnCount = compressor().modelDataColumns();
0052     const int rowCount = compressor().modelDataRows();
0053 
0054 // FIXME integrade column index retrieval to compressor:
0055     int maxFound = 0;
0056 //    {   // find the last column number that is not hidden
0057 //        for ( int iColumn =  datasetDimension() - 1;
0058 //             iColumn <  columnCount;
0059 //             iColumn += datasetDimension() )
0060 //            if ( ! diagram()->isHidden( iColumn ) )
0061 //                maxFound = iColumn;
0062 //    }
0063     maxFound = columnCount;
0064     // ^^^ temp
0065     const int lastVisibleColumn = maxFound - 1;
0066 
0067     LabelPaintCache lpc;
0068     LineAttributesInfoList lineList;
0069 
0070     //FIXME(khz): add LineAttributes::MissingValuesPolicy support for LineDiagram::Stacked and ::Percent
0071 
0072     qreal maxValue = 100; // always 100%
0073     qreal sumValues = 0;
0074     QVector <qreal > percentSumValues;
0075 
0076     //calculate sum of values for each column and store
0077     for ( int row = 0; row < rowCount; ++row )
0078     {
0079         for ( int col = 0; col < columnCount; ++col )
0080         {
0081             const CartesianDiagramDataCompressor::CachePosition position( row, col );
0082             CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0083             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
0084             const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
0085             const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();
0086             if ( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged )
0087                 point.value = interpolateMissingValue( position );
0088             if ( point.value > 0 )
0089                 sumValues += point.value;
0090             if ( col == lastVisibleColumn )
0091             {
0092                 percentSumValues << sumValues ;
0093                 sumValues = 0;
0094             }
0095         }
0096     }
0097 
0098     QList<QPointF> bottomPoints;
0099     bool bFirstDataset = true;
0100 
0101     for ( int column = 0; column < columnCount; ++column )
0102     {
0103         //display area can be set by dataset ( == column) and/or by cell
0104         LineAttributes laPreviousCell; // by default no area is drawn
0105         QModelIndex indexPreviousCell;
0106         QList<QPolygonF> areas;
0107         QList<QPointF> points;
0108 
0109         for ( int row = 0; row < rowCount; ++row )
0110         {
0111             const CartesianDiagramDataCompressor::CachePosition position( row, column );
0112             CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0113             const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
0114             const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
0115             const bool bDisplayCellArea = laCell.displayArea();
0116 
0117             qreal stackedValues = 0, nextValues = 0, nextKey = 0;
0118             for ( int column2 = column;
0119                   column2 >= 0;//datasetDimension() - 1;
0120                   column2 -= 1 )//datasetDimension() )
0121             {
0122                 const CartesianDiagramDataCompressor::CachePosition position( row, column2 );
0123                 CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0124 
0125                 const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();
0126                 if ( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged )
0127                     point.value = interpolateMissingValue( position );
0128 
0129                 const qreal val = point.value;
0130                 if ( val > 0 )
0131                     stackedValues += val;
0132                 //qDebug() << valueForCell( iRow, iColumn2 );
0133                 if ( row + 1 < rowCount ) {
0134                     const CartesianDiagramDataCompressor::CachePosition position( row + 1, column2 );
0135                     CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
0136 
0137                     const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();
0138                     if ( ISNAN( point.value ) && policy == LineAttributes::MissingValuesAreBridged )
0139                         point.value = interpolateMissingValue( position );
0140 
0141                     const qreal val = point.value;
0142                     if ( val > 0 )
0143                         nextValues += val;
0144                     nextKey = point.key;
0145                 }
0146             }
0147             if ( percentSumValues.at( row ) != 0 )
0148                 stackedValues = stackedValues / percentSumValues.at( row ) * maxValue;
0149             else
0150                 stackedValues = 0.0;
0151             //qDebug() << stackedValues << endl;
0152             QPointF nextPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, stackedValues ) );
0153             points << nextPoint;
0154 
0155             const QPointF ptNorthWest( nextPoint );
0156             const QPointF ptSouthWest(
0157                 bDisplayCellArea
0158                 ? ( bFirstDataset
0159                     ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, 0.0 ) )
0160                     : bottomPoints.at( row )
0161                     )
0162                 : nextPoint );
0163             QPointF ptNorthEast;
0164             QPointF ptSouthEast;
0165 
0166             if ( row + 1 < rowCount ) {
0167                  if ( percentSumValues.at( row + 1 ) != 0 )
0168                      nextValues = nextValues / percentSumValues.at( row + 1 ) * maxValue;
0169                  else
0170                      nextValues = 0.0;
0171                 QPointF toPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, nextValues ) );
0172                 lineList.append( LineAttributesInfo( sourceIndex, nextPoint, toPoint ) );
0173                 ptNorthEast = toPoint;
0174                 ptSouthEast =
0175                     bDisplayCellArea
0176                     ? ( bFirstDataset
0177                         ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, 0.0 ) )
0178                         : bottomPoints.at( row + 1 )
0179                         )
0180                     : toPoint;
0181                 if ( areas.count() && laCell != laPreviousCell ) {
0182                     PaintingHelpers::paintAreas( m_private, ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
0183                     areas.clear();
0184                 }
0185                 if ( bDisplayCellArea ) {
0186                     QPolygonF poly;
0187                     poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest;
0188                     areas << poly;
0189                     laPreviousCell = laCell;
0190                     indexPreviousCell = sourceIndex;
0191                 } else {
0192                     //qDebug() << "no area shown for row"<<iRow<<"  column"<<iColumn;
0193                 }
0194             } else {
0195                 ptNorthEast = ptNorthWest;
0196                 ptSouthEast = ptSouthWest;
0197             }
0198 
0199             if ( !ISNAN( point.value ) )
0200             {
0201                 const PositionPoints pts( ptNorthWest, ptNorthEast, ptSouthEast, ptSouthWest );
0202                 m_private->addLabel( &lpc, sourceIndex, &position, pts, Position::NorthWest,
0203                                      Position::NorthWest, point.value );
0204             }
0205         }
0206         if ( areas.count() ) {
0207             PaintingHelpers::paintAreas( m_private, ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
0208             areas.clear();
0209         }
0210         bottomPoints = points;
0211         bFirstDataset = false;
0212     }
0213     PaintingHelpers::paintElements( m_private, ctx, lpc, lineList );
0214 }