1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Charts module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include <private/horizontalstackedbarchartitem_p.h>
31 #include <private/qabstractbarseries_p.h>
32 #include <private/qbarset_p.h>
33 #include <private/bar_p.h>
34 
35 QT_CHARTS_BEGIN_NAMESPACE
36 
HorizontalStackedBarChartItem(QAbstractBarSeries * series,QGraphicsItem * item)37 HorizontalStackedBarChartItem::HorizontalStackedBarChartItem(QAbstractBarSeries *series, QGraphicsItem* item)
38     : AbstractBarChartItem(series, item)
39 {
40 }
41 
initializeLayout(int set,int category,int layoutIndex,bool resetAnimation)42 void HorizontalStackedBarChartItem::initializeLayout(int set, int category,
43                                                      int layoutIndex, bool resetAnimation)
44 {
45     Q_UNUSED(set)
46     Q_UNUSED(resetAnimation)
47 
48     QRectF rect;
49     if (set > 0) {
50         const QBarSet *barSet = m_series->barSets().at(set);
51         const qreal value = barSet->at(category);
52         int checkIndex = set;
53         bool found = false;
54         // Negative values stack to negative side and positive values to positive side, so we need
55         // to find the previous set that stacks to the same side
56         while (checkIndex > 0 && !found) {
57             checkIndex--;
58             QBarSet *checkSet = m_series->barSets().at(checkIndex);
59             const qreal checkValue = checkSet->at(category);
60             if ((value < 0.0) == (checkValue < 0.0)) {
61                 Bar *checkBar = m_indexForBarMap.value(checkSet).value(category);
62                 rect = m_layout.at(checkBar->layoutIndex());
63                 found = true;
64                 break;
65             }
66         }
67         // If we didn't find a previous set to the same direction, just stack next to the first set
68         if (!found) {
69             QBarSet *firsSet = m_series->barSets().at(0);
70             Bar *firstBar = m_indexForBarMap.value(firsSet).value(category);
71             rect = m_layout.at(firstBar->layoutIndex());
72         }
73         if (value < 0)
74             rect.setRight(rect.left());
75         else
76             rect.setLeft(rect.right());
77     } else {
78         QPointF topLeft;
79         QPointF bottomRight;
80         const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
81 
82         if (domain()->type() == AbstractDomain::LogXYDomain
83                 || domain()->type() == AbstractDomain::LogXLogYDomain) {
84             topLeft = topLeftPoint(category, barWidth, domain()->minX());
85             bottomRight = bottomRightPoint(category, barWidth, domain()->minX());
86         } else {
87             topLeft = topLeftPoint(category, barWidth, 0.0);
88             bottomRight = bottomRightPoint(category, barWidth, 0.0);
89         }
90 
91         if (m_validData) {
92             rect.setTopLeft(topLeft);
93             rect.setBottomRight(bottomRight);
94         }
95     }
96     m_layout[layoutIndex] = rect.normalized();
97 }
98 
topLeftPoint(int category,qreal barWidth,qreal value)99 QPointF HorizontalStackedBarChartItem::topLeftPoint(int category, qreal barWidth, qreal value)
100 {
101     return domain()->calculateGeometryPoint(
102                 QPointF(value, m_seriesPosAdjustment + category - (barWidth / 2)), m_validData);
103 }
104 
bottomRightPoint(int category,qreal barWidth,qreal value)105 QPointF HorizontalStackedBarChartItem::bottomRightPoint(int category, qreal barWidth, qreal value)
106 {
107     return domain()->calculateGeometryPoint(
108                 QPointF(value, m_seriesPosAdjustment + category + (barWidth / 2)), m_validData);
109 }
110 
calculateLayout()111 QVector<QRectF> HorizontalStackedBarChartItem::calculateLayout()
112 {
113     QVector<QRectF> layout;
114     layout.resize(m_layout.size());
115 
116     const int setCount = m_series->count();
117     const qreal barWidth = m_series->d_func()->barWidth() * m_seriesWidth;
118 
119     QVector<qreal> positiveSums(m_categoryCount, 0.0);
120     QVector<qreal> negativeSums(m_categoryCount, 0.0);
121 
122     for (int set = 0; set < setCount; set++) {
123         QBarSet *barSet = m_series->barSets().at(set);
124         const QList<Bar *> bars = m_barMap.value(barSet);
125         for (int i = 0; i < m_categoryCount; i++) {
126             Bar *bar = bars.at(i);
127             const int category = bar->index();
128             qreal &positiveSum = positiveSums[category - m_firstCategory];
129             qreal &negativeSum = negativeSums[category - m_firstCategory];
130             qreal value = barSet->at(category);
131             QRectF rect;
132             QPointF topLeft;
133             QPointF bottomRight;
134             if (value < 0) {
135                 bottomRight = bottomRightPoint(category, barWidth, value + negativeSum);
136                 if (domain()->type() == AbstractDomain::XLogYDomain
137                         || domain()->type() == AbstractDomain::LogXLogYDomain) {
138                     topLeft = topLeftPoint(category, barWidth,
139                                            set ? negativeSum : domain()->minX());
140                 } else {
141                     topLeft = topLeftPoint(category, barWidth, set ? negativeSum : 0.0);
142                 }
143                 negativeSum += value;
144             } else {
145                 bottomRight = bottomRightPoint(category, barWidth, value + positiveSum);
146                 if (domain()->type() == AbstractDomain::XLogYDomain
147                         || domain()->type() == AbstractDomain::LogXLogYDomain) {
148                     topLeft = topLeftPoint(category, barWidth,
149                                            set ? positiveSum : domain()->minX());
150                 } else {
151                     topLeft = topLeftPoint(category, barWidth,
152                                            set ? positiveSum : 0.0);
153                 }
154                 positiveSum += value;
155             }
156 
157             rect.setTopLeft(topLeft);
158             rect.setBottomRight(bottomRight);
159             rect = rect.normalized();
160             layout[bar->layoutIndex()] = rect;
161 
162             // If animating, we need to reinitialize ~zero size bars with non-zero values
163             // so the bar growth animation starts at correct spot. We shouldn't reset if rect
164             // is already at correct position horizontally, so we check for that.
165             if (m_animation && value != 0.0) {
166                 const QRectF &checkRect = m_layout.at(bar->layoutIndex());
167                 if (checkRect.isEmpty() &&
168                         ((value < 0.0 && !qFuzzyCompare(checkRect.right(), rect.right()))
169                          || (value > 0.0 && !qFuzzyCompare(checkRect.left(), rect.left())))) {
170                     initializeLayout(set, category, bar->layoutIndex(), true);
171                 }
172             }
173         }
174     }
175     return layout;
176 }
177 
178 QT_CHARTS_END_NAMESPACE
179 
180 #include "moc_horizontalstackedbarchartitem_p.cpp"
181