1 /*
2 * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3 *
4 * This file is part of the KD Chart library.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "KChartStackedLineDiagram_p.h"
21
22 #include <QAbstractItemModel>
23
24 #include "KChartBarDiagram.h"
25 #include "KChartLineDiagram.h"
26 #include "KChartTextAttributes.h"
27 #include "KChartAttributesModel.h"
28 #include "KChartAbstractCartesianDiagram.h"
29 #include "PaintingHelpers_p.h"
30
31 using namespace KChart;
32 using namespace std;
33
StackedLineDiagram(LineDiagram * d)34 StackedLineDiagram::StackedLineDiagram( LineDiagram* d )
35 : LineDiagramType( d )
36 {
37 }
38
type() const39 LineDiagram::LineType StackedLineDiagram::type() const
40 {
41 return LineDiagram::Stacked;
42 }
43
calculateDataBoundaries() const44 const QPair<QPointF, QPointF> StackedLineDiagram::calculateDataBoundaries() const
45 {
46 const int rowCount = compressor().modelDataRows();
47 const int colCount = compressor().modelDataColumns();
48 const qreal xMin = 0;
49 qreal xMax = diagram()->model() ? diagram()->model()->rowCount( diagram()->rootIndex() ) : 0;
50 if ( !diagram()->centerDataPoints() && diagram()->model() )
51 xMax -= 1;
52 qreal yMin = 0, yMax = 0;
53
54 bool bStarting = true;
55 for ( int row = 0; row < rowCount; ++row )
56 {
57 // calculate sum of values per column - Find out stacked Min/Max
58 qreal stackedValues = 0.0;
59 qreal negativeStackedValues = 0.0;
60 for ( int col = datasetDimension() - 1; col < colCount; col += datasetDimension() ) {
61 const CartesianDiagramDataCompressor::CachePosition position( row, col );
62 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
63
64 if ( ISNAN( point.value ) )
65 continue;
66
67 if ( point.value >= 0.0 )
68 stackedValues += point.value;
69 else
70 negativeStackedValues += point.value;
71 }
72
73 if ( bStarting ) {
74 yMin = stackedValues;
75 yMax = stackedValues;
76 bStarting = false;
77 } else {
78 // take in account all stacked values
79 yMin = qMin( qMin( yMin, negativeStackedValues ), stackedValues );
80 yMax = qMax( qMax( yMax, negativeStackedValues ), stackedValues );
81 }
82 }
83
84 const QPointF bottomLeft( xMin, yMin );
85 const QPointF topRight( xMax, yMax );
86
87 return QPair<QPointF, QPointF> ( bottomLeft, topRight );
88 }
89
paint(PaintContext * ctx)90 void StackedLineDiagram::paint( PaintContext* ctx )
91 {
92 reverseMapper().clear();
93
94 const int columnCount = compressor().modelDataColumns();
95 const int rowCount = compressor().modelDataRows();
96
97 // FIXME integrate column index retrieval to compressor:
98 // int maxFound = 0;
99 // { // find the last column number that is not hidden
100 // for ( int iColumn = datasetDimension() - 1;
101 // iColumn < columnCount;
102 // iColumn += datasetDimension() )
103 // if ( ! diagram()->isHidden( iColumn ) )
104 // maxFound = iColumn;
105 // }
106 //maxFound = columnCount;
107 // ^^^ temp
108
109 LabelPaintCache lpc;
110 LineAttributesInfoList lineList;
111
112 QVector< qreal > percentSumValues;
113
114 QList<QPointF> bottomPoints;
115 bool bFirstDataset = true;
116
117 for ( int column = 0; column < columnCount; ++column )
118 {
119 CartesianDiagramDataCompressor::CachePosition previousCellPosition;
120
121 //display area can be set by dataset ( == column) and/or by cell
122 LineAttributes laPreviousCell; // by default no area is drawn
123 QModelIndex indexPreviousCell;
124 QList<QPolygonF> areas;
125 QList<QPointF> points;
126
127 for ( int row = 0; row < rowCount; ++row ) {
128 const CartesianDiagramDataCompressor::CachePosition position( row, column );
129 CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
130 const QModelIndex sourceIndex = attributesModel()->mapToSource( point.index );
131
132 const LineAttributes laCell = diagram()->lineAttributes( sourceIndex );
133 const bool bDisplayCellArea = laCell.displayArea();
134
135 const LineAttributes::MissingValuesPolicy policy = laCell.missingValuesPolicy();
136
137 if ( ISNAN( point.value ) && policy == LineAttributes::MissingValuesShownAsZero )
138 point.value = 0.0;
139
140 qreal stackedValues = 0, nextValues = 0, nextKey = 0;
141 for ( int column2 = column; column2 >= 0; --column2 )
142 {
143 const CartesianDiagramDataCompressor::CachePosition position( row, column2 );
144 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
145 if ( !ISNAN( point.value ) )
146 {
147 stackedValues += point.value;
148 }
149 else if ( policy == LineAttributes::MissingValuesAreBridged )
150 {
151 const qreal interpolation = interpolateMissingValue( position );
152 if ( !ISNAN( interpolation ) )
153 stackedValues += interpolation;
154 }
155
156 //qDebug() << valueForCell( iRow, iColumn2 );
157 if ( row + 1 < rowCount ) {
158 const CartesianDiagramDataCompressor::CachePosition position( row + 1, column2 );
159 const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
160 if ( !ISNAN( point.value ) )
161 {
162 nextValues += point.value;
163 }
164 else if ( policy == LineAttributes::MissingValuesAreBridged )
165 {
166 const qreal interpolation = interpolateMissingValue( position );
167 if ( !ISNAN( interpolation ) )
168 nextValues += interpolation;
169 }
170 nextKey = point.key;
171 }
172 }
173 //qDebug() << stackedValues << endl;
174 const QPointF nextPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, stackedValues ) );
175 points << nextPoint;
176
177 const QPointF ptNorthWest( nextPoint );
178 const QPointF ptSouthWest(
179 bDisplayCellArea
180 ? ( bFirstDataset
181 ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? point.key + 0.5 : point.key, 0.0 ) )
182 : bottomPoints.at( row )
183 )
184 : nextPoint );
185 QPointF ptNorthEast;
186 QPointF ptSouthEast;
187
188 if ( row + 1 < rowCount ) {
189 QPointF toPoint = ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, nextValues ) );
190 lineList.append( LineAttributesInfo( sourceIndex, nextPoint, toPoint ) );
191 ptNorthEast = toPoint;
192 ptSouthEast =
193 bDisplayCellArea
194 ? ( bFirstDataset
195 ? ctx->coordinatePlane()->translate( QPointF( diagram()->centerDataPoints() ? nextKey + 0.5 : nextKey, 0.0 ) )
196 : bottomPoints.at( row + 1 )
197 )
198 : toPoint;
199 if ( areas.count() && laCell != laPreviousCell ) {
200 PaintingHelpers::paintAreas( m_private, ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
201 areas.clear();
202 }
203 if ( bDisplayCellArea ) {
204 QPolygonF poly;
205 poly << ptNorthWest << ptNorthEast << ptSouthEast << ptSouthWest;
206 areas << poly;
207 laPreviousCell = laCell;
208 indexPreviousCell = sourceIndex;
209 } else {
210 //qDebug() << "no area shown for row"<<iRow<<" column"<<iColumn;
211 }
212 } else {
213 ptNorthEast = ptNorthWest;
214 ptSouthEast = ptSouthWest;
215 }
216
217 const PositionPoints pts( ptNorthWest, ptNorthEast, ptSouthEast, ptSouthWest );
218 if ( !ISNAN( point.value ) )
219 m_private->addLabel( &lpc, sourceIndex, &position, pts, Position::NorthWest,
220 Position::NorthWest, point.value );
221 }
222 if ( areas.count() ) {
223 PaintingHelpers::paintAreas( m_private, ctx, indexPreviousCell, areas, laPreviousCell.transparency() );
224 areas.clear();
225 }
226 bottomPoints = points;
227 bFirstDataset = false;
228 }
229 PaintingHelpers::paintElements( m_private, ctx, lpc, lineList );
230 }
231