KQuickCharts

BarChart.cpp
1/*
2 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7#include "BarChart.h"
8
9#include <QDebug>
10#include <QSGNode>
11
12#include "RangeGroup.h"
13#include "datasource/ChartDataSource.h"
14#include "scenegraph/BarChartNode.h"
15
16BarChart::BarChart(QQuickItem *parent)
17 : XYChart(parent)
18{
19}
20
21qreal BarChart::spacing() const
22{
23 return m_spacing;
24}
25
26void BarChart::setSpacing(qreal newSpacing)
27{
28 if (newSpacing == m_spacing) {
29 return;
30 }
31
32 m_spacing = newSpacing;
33 update();
34 Q_EMIT spacingChanged();
35}
36
37qreal BarChart::barWidth() const
38{
39 return m_barWidth;
40}
41
42void BarChart::setBarWidth(qreal newBarWidth)
43{
44 if (newBarWidth == m_barWidth) {
45 return;
46 }
47
48 m_barWidth = newBarWidth;
49 update();
50 Q_EMIT barWidthChanged();
51}
52
53qreal BarChart::radius() const
54{
55 return m_radius;
56}
57
58void BarChart::setRadius(qreal newRadius)
59{
60 if (newRadius == m_radius) {
61 return;
62 }
63
64 m_radius = newRadius;
65 update();
66 Q_EMIT radiusChanged();
67}
68
70{
71 return m_orientation;
72}
73
74void BarChart::setOrientation(BarChart::Orientation newOrientation)
75{
76 if (newOrientation == m_orientation) {
77 return;
78 }
79
80 m_orientation = newOrientation;
81 m_orientationChanged = true;
82 update();
83 Q_EMIT orientationChanged();
84}
85
87{
88 return m_backgroundColor;
89}
90
91void BarChart::setBackgroundColor(const QColor &newBackgroundColor)
92{
93 if (newBackgroundColor == m_backgroundColor) {
94 return;
95 }
96
97 m_backgroundColor = newBackgroundColor;
98 update();
99 Q_EMIT backgroundColorChanged();
100}
101
102QSGNode *BarChart::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
103{
104 BarChartNode *barNode = nullptr;
105
106 if (m_orientationChanged) {
107 delete node;
108 node = nullptr;
109 m_orientationChanged = false;
110 }
111
112 if (!node) {
113 barNode = new BarChartNode{};
114 if (m_orientation == VerticalOrientation) {
115 node = barNode;
116 } else {
117 auto transformNode = new QSGTransformNode{};
118 transformNode->appendChildNode(barNode);
119 QMatrix4x4 matrix;
120 matrix.translate(width(), 0.0);
121 matrix.rotate(90.0, 0.0, 0.0, 1.0);
122 transformNode->setMatrix(matrix);
123 node = transformNode;
124 }
125 } else {
126 if (m_orientation == VerticalOrientation) {
127 barNode = static_cast<BarChartNode *>(node);
128 } else {
129 barNode = static_cast<BarChartNode *>(node->childAtIndex(0));
130 }
131 }
132
133 if (m_orientation == VerticalOrientation) {
134 barNode->setRect(boundingRect());
135 } else {
136 QMatrix4x4 matrix;
137 matrix.translate(width(), 0.0);
138 matrix.rotate(90.0, 0.0, 0.0, 1.0);
139 static_cast<QSGTransformNode *>(node)->setMatrix(matrix);
140 barNode->setRect(QRectF{boundingRect().topLeft(), QSizeF{height(), width()}});
141 }
142 barNode->setBars(calculateBars());
143 barNode->setRadius(m_radius);
144 barNode->setBackgroundColor(m_backgroundColor);
145
146 barNode->update();
147
148 return node;
149}
150
152{
153 if (valueSources().size() == 0 || !colorSource()) {
154 return;
155 }
156
157 m_barDataItems.clear();
158
160
161 const auto range = computedRange();
162 const auto sources = valueSources();
163 auto colors = colorSource();
164 auto indexMode = indexingMode();
165 auto colorIndex = 0;
166
167 m_barDataItems.fill(QList<BarData>{}, range.distanceX);
168
169 auto generator = [&, this, i = range.startX]() mutable -> QList<BarData> {
170 QList<BarData> colorInfos;
171
172 for (int j = 0; j < sources.count(); ++j) {
173 auto value = (sources.at(j)->item(i).toReal() - range.startY) / range.distanceY;
174 colorInfos << BarData{value, colors->item(colorIndex).value<QColor>()};
175
176 if (indexMode != Chart::IndexSourceValues) {
177 colorIndex++;
178 }
179 }
180
181 if (stacked()) {
182 auto previous = 0.0;
183 for (auto &[colorVal, _] : colorInfos) {
184 colorVal += previous;
185 previous = colorVal;
186 }
187 }
188
189 if (indexMode == Chart::IndexSourceValues) {
190 colorIndex++;
191 } else if (indexMode == Chart::IndexEachSource) {
192 colorIndex = 0;
193 }
194
195 i++;
196 return colorInfos;
197 };
198
200 std::generate_n(m_barDataItems.begin(), range.distanceX, generator);
201 } else {
202 std::generate_n(m_barDataItems.rbegin(), range.distanceX, generator);
203 }
204
205 update();
206}
207
208QList<Bar> BarChart::calculateBars()
209{
210 QList<Bar> result;
211
212 // TODO: Find some way to clean this up and simplify it, since this is pretty ugly.
213
214 auto targetWidth = m_orientation == VerticalOrientation ? width() : height();
215
216 float w = m_barWidth;
217 if (w < 0.0) {
218 const auto totalItemCount = stacked() ? m_barDataItems.size() : m_barDataItems.size() * valueSources().count();
219
220 w = targetWidth / totalItemCount - m_spacing;
221
222 auto x = float(m_spacing / 2);
223 const auto itemSpacing = w + m_spacing;
224
225 for (const auto &items : std::as_const(m_barDataItems)) {
226 result.reserve(result.size() + items.size());
227 if (stacked()) {
228 std::transform(items.crbegin(), items.crend(), std::back_inserter(result), [x, w](const BarData &entry) {
229 return Bar{x, w, float(entry.value), entry.color};
230 });
231 x += itemSpacing;
232 } else {
233 std::transform(items.cbegin(), items.cend(), std::back_inserter(result), [&x, itemSpacing, w](const BarData &entry) {
234 Bar bar{x, w, float(entry.value), entry.color};
235 x += itemSpacing;
236 return bar;
237 });
238 }
239 }
240 } else {
241 const auto itemSpacing = targetWidth / m_barDataItems.size();
242 if (stacked()) {
243 auto x = float(itemSpacing / 2 - m_barWidth / 2);
244
245 for (const auto &items : std::as_const(m_barDataItems)) {
246 result.reserve(result.size() + items.size());
247 std::transform(items.crbegin(), items.crend(), std::back_inserter(result), [x, w](const BarData &entry) {
248 return Bar{x, w, float(entry.value), entry.color};
249 });
250
251 x += itemSpacing;
252 }
253 } else {
254 const auto totalWidth = m_barWidth * valueSources().count() + m_spacing * (valueSources().count() - 1);
255
256 auto x = float(itemSpacing / 2 - totalWidth / 2);
257
258 for (const auto &items : std::as_const(m_barDataItems)) {
259 result.reserve(result.size() + items.size());
260 for (int i = 0; i < items.count(); ++i) {
261 auto entry = items.at(i);
262 result << Bar{float(x + i * (m_barWidth + m_spacing)), w, float(entry.value), entry.color};
263 }
264 x += itemSpacing;
265 }
266 }
267 }
268
269 return result;
270}
271
272#include "moc_BarChart.cpp"
Orientation orientation
The orientation of bars in the chart.
Definition BarChart.h:84
void onDataChanged() override
Reimplemented from Chart.
Definition BarChart.cpp:151
QColor backgroundColor
The background color of bars in the chart.
Definition BarChart.h:96
QSGNode * updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) override
Reimplemented from QQuickItem.
Definition BarChart.cpp:102
qreal spacing
The spacing between bars for each value source.
Definition BarChart.h:53
qreal barWidth
The width of individual bars in the chart.
Definition BarChart.h:64
@ VerticalOrientation
Bars are oriented vertically, with low values at the bottom and high values at the top.
Definition BarChart.h:41
qreal radius
The radius of the ends of bars in the chart in pixels.
Definition BarChart.h:74
QQmlListProperty< ChartDataSource > valueSources
The data sources providing the data this chart needs to render.
Definition Chart.h:70
ChartDataSource * colorSource
The data source to use for colors of chart items.
Definition Chart.h:62
@ IndexSourceValues
Index each value, restart indexing for each value source.
Definition Chart.h:34
@ IndexEachSource
Index each value source, never index individual values.
Definition Chart.h:35
IndexingMode indexingMode
The indexing mode used for indexing colors and names.
Definition Chart.h:81
A base class for Charts that are based on an X/Y grid.
Definition XYChart.h:33
virtual void updateComputedRange()
Re-calculate the chart's range.
Definition XYChart.cpp:74
Direction direction
Which direction this chart's X axis runs.
Definition XYChart.h:77
bool stacked
Whether the values of each value source should be stacked.
Definition XYChart.h:88
ComputedRange computedRange() const
Get the complete, calculated range for this chart.
Definition XYChart.cpp:69
@ ZeroAtStart
Zero is at the beginning of the chart, values run from begin to end.
iterator begin()
void clear()
QList< T > & fill(parameter_type value, qsizetype size)
reverse_iterator rbegin()
void reserve(qsizetype size)
qsizetype size() const const
T value(qsizetype i) const const
void rotate(const QQuaternion &quaternion)
void translate(const QVector3D &vector)
Q_EMITQ_EMIT
virtual QRectF boundingRect() const const
QSizeF size() const const
void update()
QPointF topLeft() const const
void appendChildNode(QSGNode *node)
QSGNode * childAtIndex(int i) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:57 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.