File indexing completed on 2024-05-05 16:16:43
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "BarChartNode.h" 0008 0009 #include <QColor> 0010 #include <QDebug> 0011 0012 #include "BarChartMaterial.h" 0013 0014 struct BarVertex { 0015 float x; 0016 float y; 0017 0018 float u; 0019 float v; 0020 0021 float r; 0022 float g; 0023 float b; 0024 float a; 0025 0026 float value; 0027 0028 void set(const QPointF &position, const QVector2D &uv, const QColor &color, float newValue) 0029 { 0030 x = position.x(); 0031 y = position.y(); 0032 u = uv.x(); 0033 v = uv.y(); 0034 r = color.redF(); 0035 g = color.greenF(); 0036 b = color.blueF(); 0037 a = color.alphaF(); 0038 value = newValue; 0039 } 0040 }; 0041 0042 /* clang-format off */ 0043 QSGGeometry::Attribute BarAttributes[] = { 0044 QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), 0045 QSGGeometry::Attribute::create(1, 2, QSGGeometry::FloatType, false), 0046 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType, false), 0047 QSGGeometry::Attribute::create(3, 1, QSGGeometry::FloatType, false) 0048 }; 0049 /* clang-format on */ 0050 0051 QSGGeometry::AttributeSet BarAttributeSet = {4, sizeof(BarVertex), BarAttributes}; 0052 0053 void updateBarGeometry(QSGGeometry *geometry, const QRectF &rect, const QColor &color, float value) 0054 { 0055 auto vertices = static_cast<BarVertex *>(geometry->vertexData()); 0056 vertices[0].set(rect.topLeft(), {0.0, 0.0}, color, value); 0057 vertices[1].set(rect.bottomLeft(), {0.0, 1.0}, color, value); 0058 vertices[2].set(rect.topRight(), {1.0, 0.0}, color, value); 0059 vertices[3].set(rect.bottomRight(), {1.0, 1.0}, color, value); 0060 geometry->markVertexDataDirty(); 0061 } 0062 0063 class BarNode : public QSGGeometryNode 0064 { 0065 public: 0066 BarNode(const QRectF &r) 0067 { 0068 geometry = new QSGGeometry(BarAttributeSet, 4); 0069 geometry->setVertexDataPattern(QSGGeometry::DynamicPattern); 0070 updateBarGeometry(geometry, r, Qt::transparent, 0.0); 0071 setGeometry(geometry); 0072 0073 rect = r; 0074 0075 material = new BarChartMaterial{}; 0076 setMaterial(material); 0077 0078 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial); 0079 } 0080 0081 void update() 0082 { 0083 auto minSize = std::min(rect.width(), rect.height()); 0084 auto aspect = rect.height() / minSize; 0085 updateBarGeometry(geometry, rect, color, value * aspect); 0086 0087 markDirty(QSGNode::DirtyGeometry); 0088 } 0089 0090 QSGGeometry *geometry; 0091 BarChartMaterial *material; 0092 QRectF rect; 0093 QColor color; 0094 float value; 0095 }; 0096 0097 BarChartNode::BarChartNode() 0098 { 0099 } 0100 0101 void BarChartNode::setRect(const QRectF &rect) 0102 { 0103 m_rect = rect; 0104 } 0105 0106 void BarChartNode::setBars(const QVector<Bar> &bars) 0107 { 0108 m_bars = bars; 0109 } 0110 0111 void BarChartNode::setRadius(qreal radius) 0112 { 0113 m_radius = radius; 0114 } 0115 0116 void BarChartNode::setBackgroundColor(const QColor &color) 0117 { 0118 m_backgroundColor = color; 0119 } 0120 0121 void BarChartNode::update() 0122 { 0123 if (!m_rect.isValid() || m_bars.isEmpty()) { 0124 return; 0125 } 0126 0127 for (auto index = 0; index < m_bars.count(); ++index) { 0128 auto entry = m_bars.at(index); 0129 0130 auto rect = QRectF{QPointF{entry.x, m_rect.top()}, QSizeF{entry.width, m_rect.height()}}; 0131 0132 if (childCount() <= index) { 0133 appendChildNode(new BarNode{rect}); 0134 } 0135 0136 auto child = static_cast<BarNode *>(childAtIndex(index)); 0137 0138 auto minSize = std::min(rect.width(), rect.height()); 0139 auto aspect = QVector2D{float(rect.width() / minSize), float(rect.height() / minSize)}; 0140 0141 if (aspect != child->material->aspect) { 0142 child->material->aspect = aspect; 0143 child->markDirty(QSGNode::DirtyMaterial); 0144 } 0145 0146 float correctedRadius = (std::min(m_radius, entry.width / 2.0) / minSize) * 2.0; 0147 if (!qFuzzyCompare(correctedRadius, child->material->radius)) { 0148 child->material->radius = correctedRadius; 0149 child->markDirty(QSGNode::DirtyMaterial); 0150 } 0151 0152 if (m_backgroundColor != child->material->backgroundColor) { 0153 child->material->backgroundColor = m_backgroundColor; 0154 child->markDirty(QSGNode::DirtyMaterial); 0155 } 0156 0157 if (child->rect != rect || !qFuzzyCompare(child->value, entry.value) || child->color != entry.color) { 0158 child->rect = rect; 0159 child->value = entry.value; 0160 child->color = entry.color; 0161 child->update(); 0162 } 0163 } 0164 0165 while (childCount() > m_bars.count()) { 0166 auto child = childAtIndex(childCount() - 1); 0167 removeChildNode(child); 0168 delete child; 0169 } 0170 }