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

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