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 "BarChartNode.h"
8
9#include <QColor>
10#include <QDebug>
11
12#include "BarChartMaterial.h"
13
14struct BarVertex {
15 float x;
16 float y;
17
18 float u;
19 float v;
20
21 float r;
22 float g;
23 float b;
24 float a;
25
26 float value;
27
28 void set(const QPointF &position, const QVector2D &uv, const QColor &color, float newValue)
29 {
30 x = position.x();
31 y = position.y();
32 u = uv.x();
33 v = uv.y();
34 r = color.redF();
35 g = color.greenF();
36 b = color.blueF();
37 a = color.alphaF();
38 value = newValue;
39 }
40};
41
42/* clang-format off */
43QSGGeometry::Attribute BarAttributes[] = {
44 QSGGeometry::Attribute::create(pos: 0, tupleSize: 2, primitiveType: QSGGeometry::FloatType, isPosition: true),
45 QSGGeometry::Attribute::create(pos: 1, tupleSize: 2, primitiveType: QSGGeometry::FloatType, isPosition: false),
46 QSGGeometry::Attribute::create(pos: 2, tupleSize: 4, primitiveType: QSGGeometry::FloatType, isPosition: false),
47 QSGGeometry::Attribute::create(pos: 3, tupleSize: 1, primitiveType: QSGGeometry::FloatType, isPosition: false)
48};
49/* clang-format on */
50
51QSGGeometry::AttributeSet BarAttributeSet = {.count: 4, .stride: sizeof(BarVertex), .attributes: BarAttributes};
52
53void updateBarGeometry(QSGGeometry *geometry, const QRectF &rect, const QColor &color, float value)
54{
55 auto vertices = static_cast<BarVertex *>(geometry->vertexData());
56 vertices[0].set(position: rect.topLeft(), uv: {0.0, 0.0}, color, newValue: value);
57 vertices[1].set(position: rect.bottomLeft(), uv: {0.0, 1.0}, color, newValue: value);
58 vertices[2].set(position: rect.topRight(), uv: {1.0, 0.0}, color, newValue: value);
59 vertices[3].set(position: rect.bottomRight(), uv: {1.0, 1.0}, color, newValue: value);
60 geometry->markVertexDataDirty();
61}
62
63class BarNode : public QSGGeometryNode
64{
65public:
66 BarNode(const QRectF &r)
67 {
68 geometry = new QSGGeometry(BarAttributeSet, 4);
69 geometry->setVertexDataPattern(QSGGeometry::DynamicPattern);
70 updateBarGeometry(geometry, rect: r, color: Qt::transparent, value: 0.0);
71 setGeometry(geometry);
72
73 rect = r;
74
75 material = new BarChartMaterial{};
76 setMaterial(material);
77
78 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
79 }
80
81 void update()
82 {
83 auto minSize = std::min(a: rect.width(), b: rect.height());
84 auto aspect = rect.height() / minSize;
85 updateBarGeometry(geometry, rect, color, value: value * aspect);
86
87 markDirty(bits: QSGNode::DirtyGeometry);
88 }
89
90 QSGGeometry *geometry;
91 BarChartMaterial *material;
92 QRectF rect;
93 QColor color;
94 float value;
95};
96
97BarChartNode::BarChartNode()
98{
99}
100
101void BarChartNode::setRect(const QRectF &rect)
102{
103 m_rect = rect;
104}
105
106void BarChartNode::setBars(const QList<Bar> &bars)
107{
108 m_bars = bars;
109}
110
111void BarChartNode::setRadius(qreal radius)
112{
113 m_radius = radius;
114}
115
116void BarChartNode::setBackgroundColor(const QColor &color)
117{
118 m_backgroundColor = color;
119}
120
121void BarChartNode::update()
122{
123 if (!m_rect.isValid() || m_bars.isEmpty()) {
124 return;
125 }
126
127 for (auto index = 0; index < m_bars.count(); ++index) {
128 auto entry = m_bars.at(i: index);
129
130 auto rect = QRectF{QPointF{entry.x, m_rect.top()}, QSizeF{entry.width, m_rect.height()}};
131
132 if (childCount() <= index) {
133 appendChildNode(node: new BarNode{rect});
134 }
135
136 auto child = static_cast<BarNode *>(childAtIndex(i: index));
137
138 auto minSize = std::min(a: rect.width(), b: rect.height());
139 auto aspect = QVector2D{float(rect.width() / minSize), float(rect.height() / minSize)};
140
141 if (aspect != child->material->aspect) {
142 child->material->aspect = aspect;
143 child->markDirty(bits: QSGNode::DirtyMaterial);
144 }
145
146 float correctedRadius = (std::min(a: m_radius, b: entry.width / 2.0) / minSize) * 2.0;
147 if (!qFuzzyCompare(p1: correctedRadius, p2: child->material->radius)) {
148 child->material->radius = correctedRadius;
149 child->markDirty(bits: QSGNode::DirtyMaterial);
150 }
151
152 if (m_backgroundColor != child->material->backgroundColor) {
153 child->material->backgroundColor = m_backgroundColor;
154 child->markDirty(bits: QSGNode::DirtyMaterial);
155 }
156
157 if (child->rect != rect || !qFuzzyCompare(p1: child->value, p2: entry.value) || child->color != entry.color) {
158 child->rect = rect;
159 child->value = entry.value;
160 child->color = entry.color;
161 child->update();
162 }
163 }
164
165 while (childCount() > m_bars.count()) {
166 auto child = childAtIndex(i: childCount() - 1);
167 removeChildNode(node: child);
168 delete child;
169 }
170}
171

source code of kquickcharts/src/scenegraph/BarChartNode.cpp