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 const auto highlightIndex = highlight();
170
171 auto generator = [&, this, i = range.startX]() mutable -> QList<BarData> {
172 QList<BarData> colorInfos;
173
174 for (int j = 0; j < sources.count(); ++j) {
175 auto value = (sources.at(j)->item(i).toReal() - range.startY) / range.distanceY;
176 auto color = colors->item(colorIndex).value<QColor>();
177
178 if (highlightIndex >= 0 && highlightIndex != colorIndex) {
179 color = desaturate(color);
180 }
181
182 colorInfos << BarData{value, color};
183
184 if (indexMode != Chart::IndexSourceValues) {
185 colorIndex++;
186 }
187 }
188
189 if (stacked()) {
190 auto previous = 0.0;
191 for (auto &[colorVal, _] : colorInfos) {
192 colorVal += previous;
193 previous = colorVal;
194 }
195 }
196
197 if (indexMode == Chart::IndexSourceValues) {
198 colorIndex++;
199 } else if (indexMode == Chart::IndexEachSource) {
200 colorIndex = 0;
201 }
202
203 i++;
204 return colorInfos;
205 };
206
208 std::generate_n(m_barDataItems.begin(), range.distanceX, generator);
209 } else {
210 std::generate_n(m_barDataItems.rbegin(), range.distanceX, generator);
211 }
212
213 update();
214}
215
216QList<Bar> BarChart::calculateBars()
217{
218 QList<Bar> result;
219
220 // TODO: Find some way to clean this up and simplify it, since this is pretty ugly.
221
222 auto targetWidth = m_orientation == VerticalOrientation ? width() : height();
223
224 float w = m_barWidth;
225 if (w < 0.0) {
226 const auto totalItemCount = stacked() ? m_barDataItems.size() : m_barDataItems.size() * valueSources().count();
227
228 w = targetWidth / totalItemCount - m_spacing;
229
230 auto x = float(m_spacing / 2);
231 const auto itemSpacing = w + m_spacing;
232
233 for (const auto &items : std::as_const(m_barDataItems)) {
234 result.reserve(result.size() + items.size());
235 if (stacked()) {
236 std::transform(items.crbegin(), items.crend(), std::back_inserter(result), [x, w](const BarData &entry) {
237 return Bar{x, w, float(entry.value), entry.color};
238 });
239 x += itemSpacing;
240 } else {
241 std::transform(items.cbegin(), items.cend(), std::back_inserter(result), [&x, itemSpacing, w](const BarData &entry) {
242 Bar bar{x, w, float(entry.value), entry.color};
243 x += itemSpacing;
244 return bar;
245 });
246 }
247 }
248 } else {
249 const auto itemSpacing = targetWidth / m_barDataItems.size();
250 if (stacked()) {
251 auto x = float(itemSpacing / 2 - m_barWidth / 2);
252
253 for (const auto &items : std::as_const(m_barDataItems)) {
254 result.reserve(result.size() + items.size());
255 std::transform(items.crbegin(), items.crend(), std::back_inserter(result), [x, w](const BarData &entry) {
256 return Bar{x, w, float(entry.value), entry.color};
257 });
258
259 x += itemSpacing;
260 }
261 } else {
262 const auto totalWidth = m_barWidth * valueSources().count() + m_spacing * (valueSources().count() - 1);
263
264 auto x = float(itemSpacing / 2 - totalWidth / 2);
265
266 for (const auto &items : std::as_const(m_barDataItems)) {
267 result.reserve(result.size() + items.size());
268 for (int i = 0; i < items.count(); ++i) {
269 auto entry = items.at(i);
270 result << Bar{float(x + i * (m_barWidth + m_spacing)), w, float(entry.value), entry.color};
271 }
272 x += itemSpacing;
273 }
274 }
275 }
276
277 return result;
278}
279
280#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
QColor desaturate(const QColor &input)
Desaturate and de-emphasise a color.
Definition Chart.cpp:179
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
int highlight
The index of a value source to highlight.
Definition Chart.h:94
@ 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
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 Mon Nov 18 2024 12:08:53 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.