Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgcurvestrokenode.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7QT_BEGIN_NAMESPACE
8
9QSGCurveStrokeNode::QSGCurveStrokeNode()
10{
11 setFlag(OwnsGeometry, true);
12 qsgnode_set_description(this, QLatin1StringView("curve stroke"));
13 setGeometry(new QSGGeometry(attributes(), 0, 0));
14 // defer updateMaterial() until later
15}
16
17void QSGCurveStrokeNode::QSGCurveStrokeNode::updateMaterial()
18{
19 const bool expandingInVertexShader = m_cosmetic || expandingStrokeEnabled();
20 m_material.reset(new QSGCurveStrokeMaterial(this, expandingInVertexShader));
21 setMaterial(m_material.data());
22}
23
24// Take the start, control and end point of a curve and return the points A, B, C
25// representing the curve as Q(s) = A*s*s + B*s + C
26std::array<QVector2D, 3> QSGCurveStrokeNode::curveABC(const std::array<QVector2D, 3> &p)
27{
28 QVector2D a = p[0] - 2*p[1] + p[2];
29 QVector2D b = 2*p[1] - 2*p[0];
30 QVector2D c = p[0];
31
32 return {a, b, c};
33}
34
35/*!
36 Append a triangle with \a vtx corners within which the fragment shader will
37 draw the visible part of a quadratic curve from ctl[0] to ctl[2] with
38 control point ctl[1] (AKA a quadratic Bézier curve with 3 control points).
39 The \a normal vectors are used in the vertex shader to expand the triangle
40 according to its stroke width: therefore, it's ok for the triangle to be
41 degenerate, and get expanded to size in the vertex shader. Normals are
42 usually unit vectors, but it's also ok for some to have larger magnitudes,
43 to handle the case when miter corners need to be extended proportionally
44 farther as stroke width increases.
45*/
46void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &vtx,
47 const std::array<QVector2D, 3> &ctl,
48 const std::array<QVector2D, 3> &normal,
49 const std::array<float, 3> &extrusions)
50{
51 auto abc = curveABC(ctl);
52
53 int currentVertex = m_uncookedVertexes.count();
54
55 for (int i = 0; i < 3; ++i) {
56 m_uncookedVertexes.append( { vtx[i].x(), vtx[i].y(),
57 abc[0].x(), abc[0].y(), abc[1].x(), abc[1].y(), abc[2].x(), abc[2].y(),
58 normal[i].x(), normal[i].y(), extrusions[i] } );
59 }
60 m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
61}
62
63/*!
64 Append a triangle with \a vtx corners within which the fragment shader will
65 draw the visible part of a line from ctl[0] to ctl[2].
66 The \a normal vectors are used in the vertex shader to expand the triangle
67 according to its stroke width: therefore, it's ok for the triangle to be
68 degenerate, and get expanded to size in the vertex shader. Normals are
69 usually unit vectors, but it's also ok for some to have larger magnitudes,
70 to handle the case when miter corners need to be extended proportionally
71 farther as stroke width increases.
72*/
73void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &vtx,
74 const std::array<QVector2D, 2> &ctl,
75 const std::array<QVector2D, 3> &normal,
76 const std::array<float, 3> &extrusions)
77{
78 // We could reduce this to a linear equation by setting A to (0,0).
79 // However, then we cannot use the cubic solution and need an additional
80 // code path in the shader. The following formulation looks more complicated
81 // but allows to always use the cubic solution.
82 auto A = ctl[1] - ctl[0];
83 auto B = QVector2D(0., 0.);
84 auto C = ctl[0];
85
86 int currentVertex = m_uncookedVertexes.count();
87
88 for (int i = 0; i < 3; ++i) {
89 m_uncookedVertexes.append( { vtx[i].x(), vtx[i].y(),
90 A.x(), A.y(), B.x(), B.y(), C.x(), C.y(),
91 normal[i].x(), normal[i].y(), extrusions[i] } );
92 }
93 m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
94}
95
96void QSGCurveStrokeNode::cookGeometry()
97{
98 updateMaterial(); // by now, setCosmeticStroke has been called if necessary
99 QSGGeometry *g = geometry();
100 if (g->indexType() != QSGGeometry::UnsignedIntType) {
101 g = new QSGGeometry(attributes(),
102 m_uncookedVertexes.size(),
103 m_uncookedIndexes.size(),
104 QSGGeometry::UnsignedIntType);
105 setGeometry(g);
106 } else {
107 g->allocate(m_uncookedVertexes.size(), m_uncookedIndexes.size());
108 }
109
110 g->setDrawingMode(QSGGeometry::DrawTriangles);
111 memcpy(g->vertexData(),
112 m_uncookedVertexes.constData(),
113 g->vertexCount() * g->sizeOfVertex());
114 memcpy(g->indexData(),
115 m_uncookedIndexes.constData(),
116 g->indexCount() * g->sizeOfIndex());
117
118 m_uncookedIndexes.clear();
119 m_uncookedIndexes.squeeze();
120 m_uncookedVertexes.clear();
121 m_uncookedVertexes.squeeze();
122}
123
124const QSGGeometry::AttributeSet &QSGCurveStrokeNode::attributes()
125{
126 static QSGGeometry::Attribute data[] = {
127 QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), //vertexCoord
128 QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //A
129 QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //B
130 QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //C
131 QSGGeometry::Attribute::createWithAttributeType(4, 3, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //normalExt
132 };
133 static QSGGeometry::AttributeSet attrs = { 5, sizeof(StrokeVertex), data };
134 return attrs;
135}
136
137// TODO remove when we consider the expanding-stroke shader to be stable for full-time use
138bool QSGCurveStrokeNode::expandingStrokeEnabled()
139{
140 static const bool ret = qEnvironmentVariableIntValue("QT_QUICKSHAPES_STROKE_EXPANDING");
141 return ret;
142}
143
144QT_END_NAMESPACE