KD Chart 2  [rev.2.7]
KDChartStackedLyingBarDiagram_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include <QModelIndex>
24 
25 #include "KDChartBarDiagram.h"
26 #include "KDChartTextAttributes.h"
27 #include "KDChartAttributesModel.h"
29 #include "KDChartStackedLyingBarDiagram_p.h"
30 
31 using namespace KDChart;
32 
33 StackedLyingBarDiagram::StackedLyingBarDiagram( BarDiagram* d )
34  : BarDiagramType( d )
35 {
36 }
37 
38 BarDiagram::BarType StackedLyingBarDiagram::type() const
39 {
40  return BarDiagram::Stacked;
41 }
42 
43 const QPair<QPointF, QPointF> StackedLyingBarDiagram::calculateDataBoundaries() const
44 {
45  const int rowCount = compressor().modelDataRows();
46  const int colCount = compressor().modelDataColumns();
47 
48  const qreal xMin = 0;
49  const qreal xMax = rowCount;
50  qreal yMin = 0;
51  qreal yMax = 0;
52 
53  bool isFirst = true;
54  for ( int row = 0; row < rowCount; ++row ) {
55  // calculate sum of values per column - Find out stacked Min/Max
56  qreal stackedValues = 0.0;
57  qreal negativeStackedValues = 0.0;
58  for ( int col = 0; col < colCount; ++col ) {
59  const CartesianDiagramDataCompressor::CachePosition position( row, col );
60  const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
61 
62  if ( point.value > 0.0 )
63  stackedValues += point.value;
64  else
65  negativeStackedValues += point.value;
66 
67  // this is always true yMin can be 0 in case all values
68  // are the same
69  // same for yMax it can be zero if all values are negative
70  if ( isFirst ) {
71  yMin = negativeStackedValues < 0.0 ? negativeStackedValues : stackedValues;
72  yMax = stackedValues > 0.0 ? stackedValues : negativeStackedValues;
73  isFirst = false;
74  } else {
75  yMin = qMin( qMin( yMin, stackedValues ), negativeStackedValues );
76  yMax = qMax( qMax( yMax, stackedValues ), negativeStackedValues );
77  }
78  }
79  }
80 
81  // special cases
82  if ( yMax == yMin ) {
83  if ( yMin == 0.0 ) {
84  yMax = 0.1; // we need at least a range
85  } else if ( yMax < 0.0 ) {
86  yMax = 0.0; // extend the range to zero
87  } else if ( yMin > 0.0 ) {
88  yMin = 0.0; // dito
89  }
90  }
91 
92  return QPair< QPointF, QPointF >( QPointF( yMin, xMin ), QPointF( yMax, xMax ) );
93 }
94 
95 void StackedLyingBarDiagram::paint( PaintContext* ctx )
96 {
97  reverseMapper().clear();
98 
99  const QPair<QPointF,QPointF> boundaries = diagram()->dataBoundaries(); // cached
100 
101  const QPointF boundLeft = ctx->coordinatePlane()->translate( boundaries.first ) ;
102  const QPointF boundRight = ctx->coordinatePlane()->translate( boundaries.second );
103 
104  const int rowCount = compressor().modelDataRows();
105  const int colCount = compressor().modelDataColumns();
106 
107  BarAttributes ba = diagram()->barAttributes();
108  qreal barWidth = 0;
109  qreal maxDepth = 0;
110  qreal width = boundLeft.y() - boundRight.y();
111  qreal groupWidth = width / rowCount;
112  qreal spaceBetweenBars = 0;
113  qreal spaceBetweenGroups = 0;
114 
115  if ( ba.useFixedBarWidth() ) {
116  barWidth = ba.fixedBarWidth();
117  groupWidth += barWidth;
118 
119  // Pending Michel set a min and max value for the groupWidth
120  // related to the area.width
121  if ( groupWidth < 0 )
122  groupWidth = 0;
123 
124  if ( groupWidth * rowCount > width )
125  groupWidth = width / rowCount;
126  }
127 
128  // maxLimit: allow the space between bars to be larger until area.width()
129  // is covered by the groups.
130  qreal maxLimit = rowCount * (groupWidth + ((colCount-1) * ba.fixedDataValueGap()) );
131 
132 
133  //Pending Michel: FixMe
134  if ( ba.useFixedDataValueGap() ) {
135  if ( ctx->rectangle().width() > maxLimit )
136  spaceBetweenBars += ba.fixedDataValueGap();
137  else
138  spaceBetweenBars = ((width/rowCount) - groupWidth)/(colCount-1);
139  }
140 
141  if ( ba.useFixedValueBlockGap() )
142  spaceBetweenGroups += ba.fixedValueBlockGap();
143 
144  calculateValueAndGapWidths( rowCount, colCount,groupWidth,
145  barWidth, spaceBetweenBars, spaceBetweenGroups );
146 
147  LabelPaintCache lpc;
148  for ( int row = 0; row < rowCount; ++row )
149  {
150  qreal offset = spaceBetweenGroups;
151  if ( ba.useFixedBarWidth() )
152  offset -= ba.fixedBarWidth();
153 
154  if ( offset < 0 )
155  offset = 0;
156 
157  for ( int col = 0; col < colCount; ++col )
158  {
159  qreal threeDOffset = 0.0;
160  const CartesianDiagramDataCompressor::CachePosition position( row, col );
161  const CartesianDiagramDataCompressor::DataPoint p = compressor().data( position );
162 
163  const QModelIndex index = attributesModel()->mapToSource( p.index );
164  ThreeDBarAttributes threeDAttrs = diagram()->threeDBarAttributes( index );
165  const qreal value = p.value;
166  qreal stackedValues = 0.0;
167  qreal key = 0.0;
168 
169  if ( threeDAttrs.isEnabled() ) {
170  if ( barWidth > 0 ) {
171  barWidth = (width - ((offset+(threeDAttrs.depth()))*rowCount))/ rowCount;
172  threeDOffset = threeDAttrs.depth();
173  }
174  if ( barWidth <= 0 ) {
175  barWidth = 0.1;
176  threeDOffset = (width - (offset*rowCount))/ rowCount;
177  }
178  } else {
179  barWidth = (width - (offset*rowCount))/ rowCount;
180  }
181 
182  for ( int k = col; k >= 0; --k )
183  {
184  const CartesianDiagramDataCompressor::CachePosition position( row, k );
185  const CartesianDiagramDataCompressor::DataPoint point = compressor().data( position );
186  if ( ( p.value >= 0.0 && point.value >= 0.0 ) || ( p.value < 0.0 && point.value < 0.0 ) )
187  stackedValues += point.value;
188  key = point.key;
189  }
190  QPointF point = ctx->coordinatePlane()->translate( QPointF( stackedValues, key ) );
191  point.ry() -= offset + threeDOffset;
192  const QPointF previousPoint = ctx->coordinatePlane()->translate( QPointF( stackedValues - value, key ) );
193  const qreal barHeight = point.x() - previousPoint.x();
194  point.rx() -= barHeight;
195 
196  const QRectF rect = QRectF( point, QSizeF( barHeight , barWidth ) ).translated( 1, 0 );
197  m_private->addLabel( &lpc, index, 0, PositionPoints( rect ), Position::North,
198  Position::South, value );
199  paintBars( ctx, index, rect, maxDepth );
200  }
201  }
202  m_private->paintDataValueTextsAndMarkers( ctx, lpc, false );
203 }
const QRectF rectangle() const
virtual const QPointF translate(const QPointF &diagramPoint) const =0
Translate the given point in value space coordinates to a position in pixel space.
AbstractCoordinatePlane * coordinatePlane() const
Set of attributes for changing the appearance of bar charts.
static const Position & South
BarDiagram defines a common bar diagram.
Stores information about painting diagrams.
A set of 3D bar attributes.
Stores the absolute target points of a Position.
static const Position & North

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
https://www.kdab.com/
https://www.kdab.com/products/kd-chart/