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 }