1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsgcurvestrokenode_p.h"
#include "qsgcurvestrokenode_p_p.h"
QT_BEGIN_NAMESPACE
QSGCurveStrokeNode::QSGCurveStrokeNode()
{
setFlag(OwnsGeometry, true);
setGeometry(new QSGGeometry(attributes(), 0, 0));
updateMaterial();
}
void QSGCurveStrokeNode::QSGCurveStrokeNode::updateMaterial()
{
m_material.reset(new QSGCurveStrokeMaterial(this));
setMaterial(m_material.data());
}
// Take the start, control and end point of a curve and return the points A, B, C
// representing the curve as Q(s) = A*s*s + B*s + C
std::array<QVector2D, 3> QSGCurveStrokeNode::curveABC(const std::array<QVector2D, 3> &p)
{
QVector2D a = p[0] - 2*p[1] + p[2];
QVector2D b = 2*p[1] - 2*p[0];
QVector2D c = p[0];
return {a, b, c};
}
// Curve from p[0] to p[2] with control point p[1]
void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &v,
const std::array<QVector2D, 3> &p,
const std::array<QVector2D, 3> &n)
{
auto abc = curveABC(p);
int currentVertex = m_uncookedVertexes.count();
for (int i = 0; i < 3; ++i) {
m_uncookedVertexes.append( { v[i].x(), v[i].y(),
abc[0].x(), abc[0].y(), abc[1].x(), abc[1].y(), abc[2].x(), abc[2].y(),
n[i].x(), n[i].y() } );
}
m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
}
// Straight line from p0 to p1
void QSGCurveStrokeNode::appendTriangle(const std::array<QVector2D, 3> &v,
const std::array<QVector2D, 2> &p,
const std::array<QVector2D, 3> &n)
{
// We could reduce this to a linear equation by setting A to (0,0).
// However, then we cannot use the cubic solution and need an additional
// code path in the shader. The following formulation looks more complicated
// but allows to always use the cubic solution.
auto A = p[1] - p[0];
auto B = QVector2D(0., 0.);
auto C = p[0];
int currentVertex = m_uncookedVertexes.count();
// for (auto v : QList<QPair<QVector2D, QVector2D>>({{v0, n0}, {v1, n1}, {v2, n2}})) {
for (int i = 0; i < 3; ++i) {
m_uncookedVertexes.append( { v[i].x(), v[i].y(),
A.x(), A.y(), B.x(), B.y(), C.x(), C.y(),
n[i].x(), n[i].y() } );
}
m_uncookedIndexes << currentVertex << currentVertex + 1 << currentVertex + 2;
}
void QSGCurveStrokeNode::cookGeometry()
{
QSGGeometry *g = geometry();
if (g->indexType() != QSGGeometry::UnsignedIntType) {
g = new QSGGeometry(attributes(),
m_uncookedVertexes.size(),
m_uncookedIndexes.size(),
QSGGeometry::UnsignedIntType);
setGeometry(g);
} else {
g->allocate(m_uncookedVertexes.size(), m_uncookedIndexes.size());
}
g->setDrawingMode(QSGGeometry::DrawTriangles);
memcpy(g->vertexData(),
m_uncookedVertexes.constData(),
g->vertexCount() * g->sizeOfVertex());
memcpy(g->indexData(),
m_uncookedIndexes.constData(),
g->indexCount() * g->sizeOfIndex());
m_uncookedIndexes.clear();
m_uncookedVertexes.clear();
}
const QSGGeometry::AttributeSet &QSGCurveStrokeNode::attributes()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute), //vertexCoord
QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //A
QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //B
QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //C
QSGGeometry::Attribute::createWithAttributeType(4, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute), //normalVector
};
static QSGGeometry::AttributeSet attrs = { 5, sizeof(StrokeVertex), data };
return attrs;
}
QT_END_NAMESPACE
|