File indexing completed on 2024-05-19 03:59:18
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 = (6 + 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 QList<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 QList<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::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), // in_position 0078 QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute), // in_uv 0079 0080 QSGGeometry::Attribute::createWithAttributeType(2, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_lineColor 0081 QSGGeometry::Attribute::createWithAttributeType(3, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_fillColor 0082 0083 QSGGeometry::Attribute::createWithAttributeType(4, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_bounds 0084 0085 QSGGeometry::Attribute::createWithAttributeType(5, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_count 0086 0087 QSGGeometry::Attribute::createWithAttributeType(8, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_0 0088 QSGGeometry::Attribute::createWithAttributeType(9, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_1 0089 QSGGeometry::Attribute::createWithAttributeType(10, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_2 0090 QSGGeometry::Attribute::createWithAttributeType(11, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_3 0091 QSGGeometry::Attribute::createWithAttributeType(12, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_4 0092 QSGGeometry::Attribute::createWithAttributeType(13, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_5 0093 QSGGeometry::Attribute::createWithAttributeType(14, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), // in_points_6 0094 }; 0095 /* clang-format on */ 0096 0097 QSGGeometry::AttributeSet LineAttributeSet = {13, sizeof(LineVertex), LineAttributes}; 0098 0099 void updateLineGeometry(QSGGeometry *geometry, 0100 const QRectF &rect, 0101 const QRectF &uvRect, 0102 const QList<QVector2D> &points, 0103 const QColor &lineColor, 0104 const QColor &fillColor, 0105 const QVector2D &bounds) 0106 { 0107 auto vertices = static_cast<LineVertex *>(geometry->vertexData()); 0108 vertices[0].set(rect.topLeft(), uvRect.topLeft(), points, lineColor, fillColor, bounds); 0109 vertices[1].set(rect.bottomLeft(), uvRect.bottomLeft(), points, lineColor, fillColor, bounds); 0110 vertices[2].set(rect.topRight(), uvRect.topRight(), points, lineColor, fillColor, bounds); 0111 vertices[3].set(rect.bottomRight(), uvRect.bottomRight(), points, lineColor, fillColor, bounds); 0112 geometry->markVertexDataDirty(); 0113 } 0114 0115 LineSegmentNode::LineSegmentNode() 0116 : LineSegmentNode(QRectF{}) 0117 { 0118 } 0119 0120 LineSegmentNode::LineSegmentNode(const QRectF &rect) 0121 { 0122 m_geometry = new QSGGeometry{LineAttributeSet, 4}; 0123 m_geometry->setVertexDataPattern(QSGGeometry::DynamicPattern); 0124 0125 setGeometry(m_geometry); 0126 0127 m_rect = rect; 0128 0129 m_material = new LineChartMaterial{}; 0130 setMaterial(m_material); 0131 0132 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial); 0133 } 0134 0135 LineSegmentNode::~LineSegmentNode() 0136 { 0137 } 0138 0139 void LineSegmentNode::setRect(const QRectF &rect) 0140 { 0141 m_rect = rect; 0142 } 0143 0144 void LineSegmentNode::setAspect(float xAspect, float yAspect) 0145 { 0146 if (qFuzzyCompare(xAspect, m_xAspect) && qFuzzyCompare(yAspect, m_yAspect)) { 0147 return; 0148 } 0149 0150 m_yAspect = yAspect; 0151 m_material->aspect = m_yAspect; 0152 markDirty(QSGNode::DirtyMaterial); 0153 0154 m_xAspect = xAspect; 0155 } 0156 0157 void LineSegmentNode::setSmoothing(float smoothing) 0158 { 0159 if (qFuzzyCompare(smoothing, m_smoothing)) { 0160 return; 0161 } 0162 0163 m_smoothing = smoothing; 0164 m_material->smoothing = m_smoothing; 0165 markDirty(QSGNode::DirtyMaterial); 0166 } 0167 0168 void LineSegmentNode::setLineWidth(float width) 0169 { 0170 if (qFuzzyCompare(width, m_lineWidth)) { 0171 return; 0172 } 0173 0174 m_lineWidth = width; 0175 m_material->lineWidth = m_lineWidth; 0176 markDirty(QSGNode::DirtyMaterial); 0177 } 0178 0179 void LineSegmentNode::setLineColor(const QColor &color) 0180 { 0181 m_lineColor = color; 0182 } 0183 0184 void LineSegmentNode::setFillColor(const QColor &color) 0185 { 0186 m_fillColor = color; 0187 } 0188 0189 void LineSegmentNode::setValues(const QList<QVector2D> &values) 0190 { 0191 m_values = values; 0192 } 0193 0194 void LineSegmentNode::setFarLeft(const QVector2D &value) 0195 { 0196 m_farLeft = value; 0197 } 0198 0199 void LineSegmentNode::setFarRight(const QVector2D &value) 0200 { 0201 m_farRight = value; 0202 } 0203 0204 void LineSegmentNode::update() 0205 { 0206 if (m_values.isEmpty() || !m_rect.isValid()) { 0207 updateLineGeometry(m_geometry, QRectF{}, QRectF{}, QList<QVector2D>{}, m_lineColor, m_fillColor, QVector2D{}); 0208 markDirty(QSGNode::DirtyGeometry); 0209 return; 0210 } 0211 0212 QList<QVector2D> points; 0213 points.reserve(m_values.size() + 8); 0214 0215 points << QVector2D{0.0, -0.5}; 0216 points << QVector2D{-0.5, -0.5}; 0217 0218 auto min = std::numeric_limits<float>::max(); 0219 auto max = std::numeric_limits<float>::min(); 0220 0221 if (!m_farLeft.isNull()) { 0222 points << QVector2D(-0.5, m_farLeft.y() * m_yAspect); 0223 points << QVector2D(((m_farLeft.x() - m_rect.left()) / m_rect.width()) * m_xAspect, m_farLeft.y() * m_yAspect); 0224 min = std::min(m_farLeft.y() * m_yAspect, min); 0225 max = std::max(m_farLeft.y() * m_yAspect, max); 0226 } else { 0227 points << QVector2D(-0.5, m_values[0].y() * m_yAspect); 0228 } 0229 0230 for (auto value : std::as_const(m_values)) { 0231 auto x = ((value.x() - m_rect.left()) / m_rect.width()) * m_xAspect; 0232 points << QVector2D(x, value.y() * m_yAspect); 0233 min = std::min(value.y() * m_yAspect, min); 0234 max = std::max(value.y() * m_yAspect, max); 0235 } 0236 0237 if (!m_farRight.isNull()) { 0238 points << QVector2D(((m_farRight.x() - m_rect.left()) / m_rect.width()) * m_xAspect, m_farRight.y() * m_yAspect); 0239 points << QVector2D(1.5, m_farRight.y() * m_yAspect); 0240 min = std::min(m_farRight.y() * m_yAspect, min); 0241 max = std::max(m_farRight.y() * m_yAspect, max); 0242 } else { 0243 points << QVector2D(1.5, points.last().y()); 0244 } 0245 0246 points << QVector2D{1.5, -0.5}; 0247 points << QVector2D{0.0, -0.5}; 0248 0249 updateLineGeometry(m_geometry, m_rect, {0.0, 0.0, m_xAspect, 1.0}, points, m_lineColor, m_fillColor, QVector2D{min, max}); 0250 markDirty(QSGNode::DirtyGeometry); 0251 }