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