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