File indexing completed on 2024-05-05 16:16:43
0001 /* 0002 * This file is part of KQuickCharts 0003 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "LineSegmentNode.h" 0009 0010 #include <QSGGeometry> 0011 0012 #include "LineChartMaterial.h" 0013 0014 constexpr int MaxPointsSize = (10 + 8) * 2; 0015 0016 struct LineVertex { 0017 float position[2]; 0018 0019 float uv[2]; 0020 0021 float lineColor[4]; 0022 float fillColor[4]; 0023 0024 float bounds[2]; 0025 0026 float pointCount; 0027 float points[MaxPointsSize]; 0028 0029 void set(const QPointF &newPosition, 0030 const QPointF &newUv, 0031 const QVector<QVector2D> &newPoints, 0032 const QColor &newLineColor, 0033 const QColor &newFillColor, 0034 const QVector2D &newBounds) 0035 { 0036 position[0] = newPosition.x(); 0037 position[1] = newPosition.y(); 0038 0039 uv[0] = newUv.x(); 0040 uv[1] = newUv.y(); 0041 0042 lineColor[0] = newLineColor.redF(); 0043 lineColor[1] = newLineColor.greenF(); 0044 lineColor[2] = newLineColor.blueF(); 0045 lineColor[3] = newLineColor.alphaF(); 0046 0047 fillColor[0] = newFillColor.redF(); 0048 fillColor[1] = newFillColor.greenF(); 0049 fillColor[2] = newFillColor.blueF(); 0050 fillColor[3] = newFillColor.alphaF(); 0051 0052 bounds[0] = newBounds.x(); 0053 bounds[1] = newBounds.y(); 0054 0055 setPoints(newPoints); 0056 } 0057 0058 void setPoints(const QVector<QVector2D> &newPoints) 0059 { 0060 memset(points, 0, MaxPointsSize * sizeof(float)); 0061 0062 Q_ASSERT_X(newPoints.size() <= (MaxPointsSize / 2), 0063 "LineVertex::setPoints", 0064 qPrintable(QStringLiteral("Too many points in new points array: %1").arg(newPoints.size()))); 0065 0066 for (int i = 0; i < newPoints.size(); ++i) { 0067 points[i * 2 + 0] = newPoints[i].x(); 0068 points[i * 2 + 1] = newPoints[i].y(); 0069 } 0070 0071 pointCount = newPoints.size(); 0072 } 0073 }; 0074 0075 /* clang-format off */ 0076 QSGGeometry::Attribute LineAttributes[] = { 0077 QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // in_position 0078 QSGGeometry::Attribute::create(1, 2, QSGGeometry::FloatType, false), // in_uv 0079 0080 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType, false), // in_lineColor 0081 QSGGeometry::Attribute::create(3, 4, QSGGeometry::FloatType, false), // in_fillColor 0082 0083 QSGGeometry::Attribute::create(4, 2, QSGGeometry::FloatType, false), // in_bounds 0084 0085 QSGGeometry::Attribute::create(5, 1, QSGGeometry::FloatType, false), // in_count 0086 0087 QSGGeometry::Attribute::create(6, 4, QSGGeometry::FloatType, false), // in_points_0 0088 QSGGeometry::Attribute::create(7, 4, QSGGeometry::FloatType, false), // in_points_1 0089 QSGGeometry::Attribute::create(8, 4, QSGGeometry::FloatType, false), // in_points_2 0090 QSGGeometry::Attribute::create(9, 4, QSGGeometry::FloatType, false), // in_points_3 0091 QSGGeometry::Attribute::create(10, 4, QSGGeometry::FloatType, false), // in_points_4 0092 QSGGeometry::Attribute::create(11, 4, QSGGeometry::FloatType, false), // in_points_5 0093 QSGGeometry::Attribute::create(12, 4, QSGGeometry::FloatType, false), // in_points_6 0094 QSGGeometry::Attribute::create(13, 4, QSGGeometry::FloatType, false), // in_points_7 0095 QSGGeometry::Attribute::create(14, 4, QSGGeometry::FloatType, false), // in_points_8 0096 }; 0097 /* clang-format on */ 0098 0099 QSGGeometry::AttributeSet LineAttributeSet = {15, sizeof(LineVertex), LineAttributes}; 0100 0101 void updateLineGeometry(QSGGeometry *geometry, 0102 const QRectF &rect, 0103 const QRectF &uvRect, 0104 const QVector<QVector2D> &points, 0105 const QColor &lineColor, 0106 const QColor &fillColor, 0107 const QVector2D &bounds) 0108 { 0109 auto vertices = static_cast<LineVertex *>(geometry->vertexData()); 0110 vertices[0].set(rect.topLeft(), uvRect.topLeft(), points, lineColor, fillColor, bounds); 0111 vertices[1].set(rect.bottomLeft(), uvRect.bottomLeft(), points, lineColor, fillColor, bounds); 0112 vertices[2].set(rect.topRight(), uvRect.topRight(), points, lineColor, fillColor, bounds); 0113 vertices[3].set(rect.bottomRight(), uvRect.bottomRight(), points, lineColor, fillColor, bounds); 0114 geometry->markVertexDataDirty(); 0115 } 0116 0117 LineSegmentNode::LineSegmentNode() 0118 : LineSegmentNode(QRectF{}) 0119 { 0120 } 0121 0122 LineSegmentNode::LineSegmentNode(const QRectF &rect) 0123 { 0124 m_geometry = new QSGGeometry{LineAttributeSet, 4}; 0125 m_geometry->setVertexDataPattern(QSGGeometry::DynamicPattern); 0126 0127 setGeometry(m_geometry); 0128 0129 m_rect = rect; 0130 0131 m_material = new LineChartMaterial{}; 0132 setMaterial(m_material); 0133 0134 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial); 0135 } 0136 0137 LineSegmentNode::~LineSegmentNode() 0138 { 0139 } 0140 0141 void LineSegmentNode::setRect(const QRectF &rect) 0142 { 0143 m_rect = rect; 0144 } 0145 0146 void LineSegmentNode::setAspect(float xAspect, float yAspect) 0147 { 0148 if (qFuzzyCompare(xAspect, m_xAspect) && qFuzzyCompare(yAspect, m_yAspect)) { 0149 return; 0150 } 0151 0152 m_yAspect = yAspect; 0153 m_material->aspect = m_yAspect; 0154 markDirty(QSGNode::DirtyMaterial); 0155 0156 m_xAspect = xAspect; 0157 } 0158 0159 void LineSegmentNode::setSmoothing(float smoothing) 0160 { 0161 if (qFuzzyCompare(smoothing, m_smoothing)) { 0162 return; 0163 } 0164 0165 m_smoothing = smoothing; 0166 m_material->smoothing = m_smoothing; 0167 markDirty(QSGNode::DirtyMaterial); 0168 } 0169 0170 void LineSegmentNode::setLineWidth(float width) 0171 { 0172 if (qFuzzyCompare(width, m_lineWidth)) { 0173 return; 0174 } 0175 0176 m_lineWidth = width; 0177 m_material->lineWidth = m_lineWidth; 0178 markDirty(QSGNode::DirtyMaterial); 0179 } 0180 0181 void LineSegmentNode::setLineColor(const QColor &color) 0182 { 0183 m_lineColor = color; 0184 } 0185 0186 void LineSegmentNode::setFillColor(const QColor &color) 0187 { 0188 m_fillColor = color; 0189 } 0190 0191 void LineSegmentNode::setValues(const QVector<QVector2D> &values) 0192 { 0193 m_values = values; 0194 } 0195 0196 void LineSegmentNode::setFarLeft(const QVector2D &value) 0197 { 0198 m_farLeft = value; 0199 } 0200 0201 void LineSegmentNode::setFarRight(const QVector2D &value) 0202 { 0203 m_farRight = value; 0204 } 0205 0206 void LineSegmentNode::update() 0207 { 0208 if (m_values.isEmpty() || !m_rect.isValid()) { 0209 updateLineGeometry(m_geometry, QRectF{}, QRectF{}, QVector<QVector2D>{}, m_lineColor, m_fillColor, QVector2D{}); 0210 markDirty(QSGNode::DirtyGeometry); 0211 return; 0212 } 0213 0214 QVector<QVector2D> points; 0215 points.reserve(m_values.size() + 8); 0216 0217 points << QVector2D{0.0, -0.5}; 0218 points << QVector2D{-0.5, -0.5}; 0219 0220 auto min = std::numeric_limits<float>::max(); 0221 auto max = std::numeric_limits<float>::min(); 0222 0223 if (!m_farLeft.isNull()) { 0224 points << QVector2D(-0.5, m_farLeft.y() * m_yAspect); 0225 points << QVector2D(((m_farLeft.x() - m_rect.left()) / m_rect.width()) * m_xAspect, m_farLeft.y() * m_yAspect); 0226 min = std::min(m_farLeft.y() * m_yAspect, min); 0227 max = std::max(m_farLeft.y() * m_yAspect, max); 0228 } else { 0229 points << QVector2D(-0.5, m_values[0].y() * m_yAspect); 0230 } 0231 0232 for (auto value : std::as_const(m_values)) { 0233 auto x = ((value.x() - m_rect.left()) / m_rect.width()) * m_xAspect; 0234 points << QVector2D(x, value.y() * m_yAspect); 0235 min = std::min(value.y() * m_yAspect, min); 0236 max = std::max(value.y() * m_yAspect, max); 0237 } 0238 0239 if (!m_farRight.isNull()) { 0240 points << QVector2D(((m_farRight.x() - m_rect.left()) / m_rect.width()) * m_xAspect, m_farRight.y() * m_yAspect); 0241 points << QVector2D(1.5, m_farRight.y() * m_yAspect); 0242 min = std::min(m_farRight.y() * m_yAspect, min); 0243 max = std::max(m_farRight.y() * m_yAspect, max); 0244 } else { 0245 points << QVector2D(1.5, points.last().y()); 0246 } 0247 0248 points << QVector2D{1.5, -0.5}; 0249 points << QVector2D{0.0, -0.5}; 0250 0251 updateLineGeometry(m_geometry, m_rect, {0.0, 0.0, m_xAspect, 1.0}, points, m_lineColor, m_fillColor, QVector2D{min, max}); 0252 markDirty(QSGNode::DirtyGeometry); 0253 }