9#include <QtGui/qvector2d.h>
10#include <QtGui/qvector4d.h>
11#include <QtGui/private/qtriangulator_p.h>
12#include <QtGui/private/qtriangulatingstroker_p.h>
13#include <QtGui/private/qrhi_p.h>
15#include <QtQuick/qsgmaterial.h>
23#if !defined(QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN)
24# define QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN (1.0f / 32.0f)
34 QQuickShapeWireFrameMaterialShader()
37 QStringLiteral(
":/qt-project.org/shapes/shaders_ng/wireframe.vert.qsb"));
39 QStringLiteral(
":/qt-project.org/shapes/shaders_ng/wireframe.frag.qsb"));
48 if (
state.isMatrixDirty()) {
51 memcpy(
buf->data(),
m.constData(), 64);
59class QQuickShapeWireFrameMaterial :
public QSGMaterial
62 QQuickShapeWireFrameMaterial()
80 return new QQuickShapeWireFrameMaterialShader;
88 struct WireFrameVertex
93 QQuickShapeWireFrameNode()
100 void activateMaterial()
102 m_material.reset(
new QQuickShapeWireFrameMaterial);
124 return sp +
t * (ep - sp);
126 const float r = 1 -
t;
127 return (
r *
r * sp) + (2 *
t *
r * cp) + (
t *
t * ep);
140 return (max - min).length();
145int QuadPath::Element::intersectionsAtY(
float y,
float *fractions)
const
147 const float y0 = startPoint().y() -
y;
148 const float y1 = controlPoint().y() -
y;
149 const float y2 = endPoint().y() -
y;
152 const float a =
y0 - (2 *
y1) +
y2;
156 const float sqr =
qSqrt(
b);
157 const float root1 = -(-
y0 +
y1 + sqr) /
a;
158 if (
qIsFinite(root1) && root1 >= 0 && root1 <= 1)
159 fractions[numRoots++] = root1;
160 const float root2 = (
y0 -
y1 + sqr) /
a;
161 if (
qIsFinite(root2) && root2 != root1 && root2 >= 0 && root2 <= 1)
162 fractions[numRoots++] = root2;
164 }
else if (
y1 !=
y2) {
165 const float root1 = (
y2 - (2 *
y1)) / (2 * (
y2 -
y1));
166 if (
qIsFinite(root1) && root1 >= 0 && root1 <= 1)
167 fractions[numRoots++] = root1;
177 return (
v2.x() *
v1.y()) - (
v2.y() *
v1.x());
196 constexpr float epsilon = 0.01f;
206 return isPointOnLeft(element.cp, element.sp, element.ep);
215 int winding_number = 0;
216 for (
const Element &
e : m_elements) {
218 float y1 =
e.startPoint().y();
219 float y2 =
e.endPoint().y();
227 const float t = (pt.
y() -
e.startPoint().y()) / (
e.endPoint().y() -
e.startPoint().y());
228 const float x =
e.startPoint().x() +
t * (
e.endPoint().x() -
e.startPoint().x());
230 winding_number +=
dir;
234 if (pt.
y() <
y1 || pt.
y() >=
y2)
237 const int numRoots =
e.intersectionsAtY(pt.
y(), ts);
241 for (
int i = 0;
i < numRoots;
i++) {
242 if (
e.pointAtFraction(ts[
i]).x() <= pt.
x()) {
248 dir =
e.tangentAtFraction(tForHit).y() < 0 ? -1 : 1;
249 winding_number +=
dir;
262 isLine =
isLine || isPointNearLine(control, currentPoint, endPoint);
264 m_elements.resize(m_elements.size() + 1);
265 Element &elem = m_elements.last();
266 elem.sp = currentPoint;
267 elem.cp =
isLine ? (0.5f * (currentPoint + endPoint)) : control;
270 elem.m_isSubpathStart = subPathToStart;
271 subPathToStart =
false;
272 currentPoint = endPoint;
275QuadPath::Element::CurvatureFlags QuadPath::coordinateOrderOfElement(
const QuadPath::Element &element)
const
282 QVector2D justRightOfMid = midPoint + (normal * delta);
283 bool pathContainsPoint =
contains(justRightOfMid);
284 return pathContainsPoint ? Element::FillOnRight : Element::CurvatureFlags(0);
299 res.reserve(
path.elementCount());
300 res.setFillRule(
path.fillRule());
304 for (
int i = 0;
i <
path.elementCount(); ++
i) {
308 switch (element.
type) {
318 ep =
path.elementAt(++
i);
320#ifndef USE_TOQUADRATICS_IN_QBEZIER
323 quads =
b.toQuadratics();
325 for (
int i = 1;
i < quads.
size();
i += 2) {
354 Element::CurvatureFlags
flags = Element::CurvatureUndetermined;
358 flags = coordinateOrderOfElement(element);
359 }
else if (checkAnomaly) {
360 Element::CurvatureFlags newFlags = coordinateOrderOfElement(element);
361 if (
flags != newFlags) {
362 qDebug() <<
"Curvature anomaly detected:" << element
363 <<
"Subpath fill on right:" << (
flags & Element::FillOnRight)
364 <<
"Element fill on right:" << (newFlags & Element::FillOnRight);
370 element.m_curvatureFlags =
flags;
380 bool controlPointOnLeft = isControlPointOnLeft(element);
381 bool isFillOnRight =
flags & Element::FillOnRight;
382 bool isConvex = controlPointOnLeft == isFillOnRight;
385 element.m_curvatureFlags = Element::CurvatureFlags(
flags | Element::Convex);
387 element.m_curvatureFlags =
flags;
397 min = max = m_elements.constFirst().sp;
400 min.setX(std::min({ min.x(),
e.sp.x(),
e.cp.x(),
e.ep.x() }));
401 min.setY(std::min({ min.y(),
e.sp.y(),
e.cp.y(),
e.ep.y() }));
402 max.
setX(std::max({ max.
x(),
e.sp.x(),
e.cp.x(),
e.ep.x() }));
403 max.
setY(std::max({ max.
y(),
e.sp.y(),
e.cp.y(),
e.ep.y() }));
424 for (
const Element &element : m_elements) {
425 if (element.m_isSubpathStart)
427 if (element.m_isLine)
447 const auto &element = m_elements.at(
i);
448 if (element.m_isSubpathStart) {
449 if (subStart >= 0 && m_elements[
i - 1].ep != m_elements[subStart].
sp) {
450 res.currentPoint = m_elements[
i - 1].ep;
451 res.lineTo(m_elements[subStart].
sp);
452 auto &endElement =
res.m_elements.last();
453 endElement.m_isSubpathEnd =
true;
457 endElement.ep = m_elements[subStart].sp;
458 }
else if (prevElement >= 0) {
459 res.m_elements[prevElement].m_isSubpathEnd =
true;
463 res.m_elements.append(element);
464 prevElement =
res.m_elements.size() - 1;
467 if (subStart >= 0 && m_elements.last().ep != m_elements[subStart].sp) {
468 res.currentPoint = m_elements.last().ep;
469 res.lineTo(m_elements[subStart].
sp);
471 if (!
res.m_elements.isEmpty()) {
472 auto &endElement =
res.m_elements.last();
473 endElement.m_isSubpathEnd =
true;
474 endElement.ep = m_elements[subStart].sp;
478 if (
res.elementCount() == 3) {
479 res.splitElementAt(2);
501 m_currentPoint = m_element.startPoint();
502 if (m_element.isLine())
503 m_lineLength = (m_element.endPoint() - m_element.startPoint()).
length();
510 m_lastT = m_currentT;
511 m_lastPoint = m_currentPoint;
512 float nextBreak = m_consumed +
length;
513 float breakT = m_element.isLine() ? nextBreak / m_lineLength : tForLength(nextBreak);
516 m_currentPoint = m_element.pointAtFraction(m_currentT);
517 m_consumed = nextBreak;
521 m_currentPoint = m_element.endPoint();
528 return m_currentPoint;
535 QVector2D rcp = (1 - m_lastT) * m_element.controlPoint() + m_lastT * m_element.endPoint();
537 float segmentT = (m_currentT - m_lastT) / (1 - m_lastT);
538 QVector2D lcp = (1 - segmentT) * m_lastPoint + segmentT * rcp;
544 float elemLength = m_element.isLine() ? m_lineLength : m_lut.last();
545 return elemLength - m_consumed;
552 QVector2D ap = m_element.startPoint() - 2 * m_element.controlPoint() + m_element.endPoint();
553 QVector2D bp = 2 * m_element.controlPoint() - 2 * m_element.startPoint();
557 float b =
B / (2 *
A);
559 float k =
c - (
b *
b);
560 float l2 =
b * std::sqrt(
b *
b + k);
561 float lnom =
b + std::sqrt(
b *
b + k);
562 float l0 = 0.5f * std::sqrt(
A);
564 m_lut.resize(LUTSize, 0);
565 for (
int i = 1;
i < LUTSize;
i++) {
566 float t = float(
i) / (LUTSize - 1);
568 float w = std::sqrt(u * u + k);
571 float l3 = k * std::log(std::fabs(lden / lnom));
572 float res = l0 * (l1 - l2 + l3);
577 float tForLength(
float length)
583 auto it = std::upper_bound(m_lut.cbegin(), m_lut.cend(),
length);
585 float nextLength = *
it--;
586 float prevLength = *
it;
587 int prevIndex = std::distance(m_lut.cbegin(),
it);
588 float fraction = (
length - prevLength) / (nextLength - prevLength);
589 res = (prevIndex + fraction) / (LUTSize - 1);
596 float m_currentT = 0;
599 float m_consumed = 0;
603 static constexpr int LUTSize = 21;
610 float patternLength = 0;
611 for (
int i = 0;
i < 2 * (dashPattern.
length() / 2);
i++) {
612 float dashLength =
qMax(lineWidth * dashPattern[
i],
qreal(0));
614 patternLength += dashLength;
616 if (patternLength == 0)
620 float startOffset = std::fmod(lineWidth * dashOffset, patternLength);
622 startOffset += patternLength;
623 for (
float dashLength :
pattern) {
624 if (dashLength > startOffset)
627 startOffset -= dashLength;
630 int dashIndex = startIndex;
631 float offset = startOffset;
637 dashIndex = startIndex;
645 res.moveTo(nextPoint);
646 else if (element.
isLine())
647 res.lineTo(nextPoint);
652 dashIndex = (dashIndex + 1) %
pattern.size();
664 const qsizetype newChildIndex = m_childElements.size();
665 m_childElements.resize(newChildIndex + 2);
668 parent.m_firstChildIndex = newChildIndex;
670 Element &quad1 = m_childElements[newChildIndex];
675 quad1.m_isSubpathStart =
parent.m_isSubpathStart;
676 quad1.m_isSubpathEnd =
false;
677 quad1.m_curvatureFlags =
parent.m_curvatureFlags;
678 quad1.m_isLine =
parent.m_isLine;
680 Element &quad2 = m_childElements[newChildIndex + 1];
684 quad2.m_isSubpathStart =
false;
685 quad2.m_isSubpathEnd =
parent.m_isSubpathEnd;
686 quad2.m_curvatureFlags =
parent.m_curvatureFlags;
687 quad2.m_isLine =
parent.m_isLine;
691 qDebug() <<
"###FIXME: quad splitting has yielded ~null quad.";
697 auto printPoint = [&](
QVector2D p) {
stream <<
"(" <<
p.x() <<
", " <<
p.y() <<
") "; };
710 stream <<
"QuadPath::Element( ";
720 stream <<
"QuadPath(" <<
path.elementCount() <<
" main elements, "
721 <<
path.elementCountRecursive() <<
" leaf elements, "
725 stream <<
" " <<
count++ << (
e.isSubpathStart() ?
" >" :
" ");
737 if (countChanged !=
nullptr && totalCount != m_paths.size())
738 *countChanged =
true;
739 m_paths.resize(totalCount);
744 auto &pathData = m_paths[
index];
745 pathData.originalPath =
path->path();
746 pathData.m_dirty |= PathDirty;
751 auto &pathData = m_paths[
index];
752 const bool wasVisible = pathData.isStrokeVisible();
753 pathData.pen.setColor(
color);
754 if (pathData.isStrokeVisible() != wasVisible)
755 pathData.m_dirty |= StrokeDirty;
757 pathData.m_dirty |= UniformsDirty;
762 auto &pathData = m_paths[
index];
764 pathData.validPenWidth =
true;
765 pathData.pen.setWidthF(
w);
767 pathData.validPenWidth =
false;
769 pathData.m_dirty |= StrokeDirty;
774 auto &pathData = m_paths[
index];
775 const bool wasVisible = pathData.isFillVisible();
776 pathData.fillColor =
color;
777 if (pathData.isFillVisible() != wasVisible)
778 pathData.m_dirty |= FillDirty;
780 pathData.m_dirty |= UniformsDirty;
785 auto &pathData = m_paths[
index];
787 pathData.m_dirty |= PathDirty;
794 auto &pathData = m_paths[
index];
796 pathData.pen.setMiterLimit(miterLimit);
797 pathData.m_dirty |= StrokeDirty;
802 auto &pathData = m_paths[
index];
804 pathData.m_dirty |= StrokeDirty;
812 auto &pathData = m_paths[
index];
815 pathData.pen.setDashPattern(dashPattern);
816 pathData.pen.setDashOffset(dashOffset);
818 pathData.m_dirty |= StrokeDirty;
823 PathData &pd(m_paths[
index]);
824 pd.gradientType = NoGradient;
826 pd.gradientType = LinearGradient;
828 pd.gradient.spread = gradient->
spread();
829 pd.gradient.a =
QPointF(
g->x1(),
g->y1());
830 pd.gradient.b =
QPointF(
g->x2(),
g->y2());
832 pd.gradientType = RadialGradient;
833 pd.gradient.a =
QPointF(
g->centerX(),
g->centerY());
834 pd.gradient.b =
QPointF(
g->focalX(),
g->focalY());
835 pd.gradient.v0 =
g->centerRadius();
836 pd.gradient.v1 =
g->focalRadius();
838 pd.gradientType = ConicalGradient;
839 pd.gradient.a =
QPointF(
g->centerX(),
g->centerY());
840 pd.gradient.v0 =
g->angle();
842 if (gradient !=
nullptr) {
843 static bool warned =
false;
846 qCWarning(lcShapeCurveRenderer) <<
"Unsupported gradient fill";
850 if (pd.gradientType != NoGradient) {
852 pd.gradient.spread = gradient->
spread();
855 pd.m_dirty |= FillDirty;
860 qCWarning(lcShapeCurveRenderer) <<
"Asynchronous creation not supported by CurveRenderer";
878 for (PathData &pathData : m_paths) {
879 int dirtyFlags = pathData.m_dirty;
881 if (dirtyFlags & PathDirty) {
886 pathData.path.setFillRule(pathData.fillRule);
887 pathData.fillPath = {};
888 dirtyFlags |= (FillDirty | StrokeDirty);
891 if (dirtyFlags & FillDirty) {
892 deleteAndClear(&pathData.fillNodes);
893 deleteAndClear(&pathData.fillDebugNodes);
894 if (pathData.isFillVisible()) {
895 if (pathData.fillPath.isEmpty()) {
896 pathData.fillPath = pathData.path.subPathsClosed();
897 pathData.fillPath.addCurvatureData();
898 if (doOverlapSolving)
899 solveOverlaps(pathData.fillPath);
901 pathData.fillNodes = addFillNodes(pathData, &pathData.fillDebugNodes);
902 dirtyFlags |= StrokeDirty;
906 if (dirtyFlags & StrokeDirty) {
907 deleteAndClear(&pathData.strokeNodes);
908 deleteAndClear(&pathData.strokeDebugNodes);
909 if (pathData.isStrokeVisible()) {
910 const QPen &pen = pathData.pen;
912 pathData.strokePath = pathData.path;
916 if (useTriangulatingStroker)
917 pathData.strokeNodes = addTriangulatingStrokerNodes(pathData, &pathData.strokeDebugNodes);
919 pathData.strokeNodes = addCurveStrokeNodes(pathData, &pathData.strokeDebugNodes);
923 if (dirtyFlags & UniformsDirty) {
924 if (!(dirtyFlags & FillDirty)) {
925 for (
auto &pathNode : std::as_const(pathData.fillNodes))
928 if (!(dirtyFlags & StrokeDirty)) {
929 for (
auto &strokeNode : std::as_const(pathData.strokeNodes))
934 pathData.m_dirty &= ~(PathDirty | FillDirty | StrokeDirty | UniformsDirty);
963 return { uv.x(), uv.y(), 0.0f };
965 return { uv.x(), uv.y(), (m_curvatureFlags & Convex) ? -1.0f : 1.0f };
971 return {
v.y(), -
v.x()};
981template<
typename Func>
984 const auto &element =
path.elementAt(
index);
986 lambda(element,
index);
1001 internalHull.
setFillRule(pathData.fillPath.fillRule());
1004 bool visualizeDebug = debugVisualization() & DebugCurves;
1005 const float dbg = visualizeDebug ? 0.5f : 0.0f;
1018 return {
qRound(
p.x() * 32.0f) / 32.0f,
qRound(
p.y() * 32.0f) / 32.0f };
1022 return {
qRound(
p.x() * 32.0f) / 32.0f,
qRound(
p.y() * 32.0f) / 32.0f };
1026 float r = 0.0f,
g = 0.0f,
b = 0.0f;
1037 node->appendTriangle(
sp, cp, ep,
1039 dbgColor, dbgColor, dbgColor);
1041 wfVertices.
append({
sp.x(),
sp.y(), 1.0f, 0.0f, 0.0f});
1042 wfVertices.
append({cp.x(), cp.y(), 0.0f, 1.0f, 0.0f});
1043 wfVertices.
append({ep.x(), ep.y(), 0.0f, 0.0f, 1.0f});
1049 QVector2D baseLine = endPoint - startPoint;
1050 QVector2D insideVector = referencePoint - startPoint;
1056 return swap ? startPoint + normal : startPoint - normal;
1060 addCurveTriangle(element,
sp, ep, cp);
1064 addCurveTriangle(element,
sp, op, ep);
1068 addCurveTriangle(element,
sp, ep, cp);
1074 addCurveTriangle(element,
sp, cp, op);
1080 addCurveTriangle(element, op, cp, ep);
1090 return startPoint + 2 * (endPoint - controlPoint);
1095 addCurveTriangle(element,
sp, ep, cp);
1099 addCurveTriangle(element,
sp, op, ep);
1104 QVector4D dbgColor(0.0f, 1.0f, 0.0f, dbg);
1105 node->appendTriangle(
p1,
p2, p3,
1107 dbgColor, dbgColor, dbgColor);
1109 wfVertices.
append({
p1.x(),
p1.y(), 1.0f, 0.0f, 0.0f});
1110 wfVertices.
append({p3.x(), p3.y(), 0.0f, 1.0f, 0.0f});
1111 wfVertices.
append({
p2.x(),
p2.y(), 0.0f, 0.0f, 1.0f});
1116 for (
int i = 0;
i < pathData.fillPath.elementCount(); ++
i)
1118 QPointF sp(element.startPoint().toPointF());
1119 QPointF cp(element.controlPoint().toPointF());
1120 QPointF ep(element.endPoint().toPointF());
1121 if (element.isSubpathStart())
1122 internalHull.moveTo(sp);
1123 if (element.isLine()) {
1124 internalHull.lineTo(ep);
1126 linePointHash.insert(toRoundedPair(sp), index);
1128 if (element.isConvex()) {
1129 internalHull.lineTo(ep);
1130 addConvexTriangle(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
1131 convexPointHash.insert(toRoundedPair(sp), index);
1133 internalHull.lineTo(cp);
1134 internalHull.lineTo(ep);
1135 addConcaveTriangle(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
1136 concaveControlPointHash.insert(toRoundedPair(cp), index);
1153 return side1 * side2 >= 0;
1166 bool safeSideOf1 = onSameSideOfLine(
p,
c,
a, n1);
1167 bool safeSideOf2 = onSameSideOfLine(
p,
c,
b, n2);
1168 return safeSideOf1 && safeSideOf2;
1171 auto handleTriangle = [&](
const QVector2D (&
p)[3]) ->
bool {
1172 int lineElementIndex = -1;
1173 int concaveElementIndex = -1;
1174 int convexElementIndex = -1;
1176 bool foundElement =
false;
1179 for (
int i = 0;
i < 3; ++
i) {
1180 if (
auto found = linePointHash.
constFind(makeHashable(
p[
i])); found != linePointHash.
constEnd()) {
1182 const auto &element = pathData.fillPath.elementAt(*found);
1184 for (
int j = 0;
j < 3; ++
j) {
1185 if (
i !=
j && roundVec2D(element.
endPoint()) ==
p[
j]) {
1188 lineElementIndex = *found;
1192 foundElement =
true;
1195 }
else if (
auto found = concaveControlPointHash.
constFind(makeHashable(
p[
i])); found != concaveControlPointHash.
constEnd()) {
1199 const auto &element = pathData.fillPath.elementAt(*found);
1200 for (
int j = 0;
j < 3; ++
j) {
1206 concaveElementIndex = *found;
1211 foundElement =
true;
1214 }
else if (
auto found = convexPointHash.
constFind(makeHashable(
p[
i])); found != convexPointHash.
constEnd()) {
1216 const auto &element = pathData.fillPath.elementAt(*found);
1217 for (
int j = 0;
j < 3; ++
j) {
1218 if (
i !=
j && roundVec2D(element.
endPoint()) ==
p[
j]) {
1221 convexElementIndex = *found;
1225 foundElement =
true;
1230 if (lineElementIndex != -1) {
1231 int ci = (6 - si - ei) % 3;
1232 addLineTriangle(pathData.fillPath.elementAt(lineElementIndex),
p[si],
p[ei],
p[ci]);
1233 }
else if (concaveElementIndex != -1) {
1234 addCurveTriangle(pathData.fillPath.elementAt(concaveElementIndex),
p[0],
p[1],
p[2]);
1235 }
else if (convexElementIndex != -1) {
1236 int oi = (6 - si - ei) % 3;
1237 const auto &otherPoint =
p[oi];
1238 const auto &element = pathData.fillPath.elementAt(convexElementIndex);
1240 bool safeSpace = pointInSafeSpace(otherPoint, element);
1242 addCurveTriangle(element,
p[0],
p[1],
p[2]);
1248 for (
int i = 0;
i < 7; ++
i) {
1249 safeSpace = pointInSafeSpace(newPoint, element);
1253 newPoint = (
p[si] +
p[ei] + newPoint) / 3;
1258 addCurveTriangle(element,
p[si],
p[ei], newPoint);
1260 addFillTriangle(
p[si],
p[oi], newPoint);
1261 addFillTriangle(
p[ei],
p[oi], newPoint);
1264 addFillTriangle(
p[0],
p[1],
p[2]);
1269 addFillTriangle(
p[0],
p[1],
p[2]);
1277 for (
int triangle = 0; triangle <
triangles.indices.size() / 3; ++triangle) {
1278 const quint32 *idx = &idxTable[triangle * 3];
1281 for (
int i = 0;
i < 3; ++
i) {
1284 bool needsSplit = !handleTriangle(
p);
1288 for (
int i = 0;
i < 3; ++
i) {
1299 node->setColor(
color);
1300 node->setFillGradient(pathData.gradient);
1302 node->cookGeometry();
1303 m_rootNode->appendChildNode(node);
1308 const bool wireFrame = debugVisualization() & DebugWireframe;
1310 QQuickShapeWireFrameNode *wfNode =
new QQuickShapeWireFrameNode;
1318 wfNode->setGeometry(wfg);
1328 m_rootNode->appendChildNode(wfNode);
1329 debugNodes->append(wfNode);
1335QVector<QSGGeometryNode *> QQuickShapeCurveRenderer::addTriangulatingStrokerNodes(
const PathData &pathData, NodeList *debugNodes)
1340 bool visualizeDebug = debugVisualization() & DebugCurves;
1341 const float dbg = visualizeDebug ? 0.5f : 0.0f;
1345 const auto painterPath = pathData.strokePath.toPainterPath();
1347 QPen pen = pathData.pen;
1348 stroker.
process(vp, pen, {}, {});
1355 QVector2D baseLine = endPoint - startPoint;
1356 QVector2D insideVector = referencePoint - startPoint;
1362 return swap ? startPoint + normal : startPoint - normal;
1371 if (
p1 ==
p2 ||
p2 == p3) {
1381 node->appendTriangle(
p1,
p2, p3,
1388 wfVertices.
append({
p1.x(),
p1.y(), 1.0f, 0.0f, 0.0f});
1389 wfVertices.
append({
p2.x(),
p2.y(), 0.0f, 0.1f, 0.0f});
1390 wfVertices.
append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f});
1392 if (!disableExtraTriangles) {
1396 node->appendTriangle(
p1, op, p3,
1402 wfVertices.
append({
p1.x(),
p1.y(), 1.0f, 0.0f, 0.0f});
1403 wfVertices.
append({op.
x(), op.
y(), 0.0f, 1.0f, 0.0f});
1404 wfVertices.
append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f});
1409 const float *verts = stroker.
vertices();
1410 for (
int i = 0;
i < vertCount - 2; ++
i) {
1412 for (
int j = 0;
j < 3; ++
j) {
1416 addStrokeTriangle(
p[0],
p[1],
p[2], isOdd);
1421 node->setColor(
color);
1422 node->setFillGradient(pathData.gradient);
1424 node->cookGeometry();
1425 m_rootNode->appendChildNode(node);
1428 const bool wireFrame = debugVisualization() & DebugWireframe;
1430 QQuickShapeWireFrameNode *wfNode =
new QQuickShapeWireFrameNode;
1438 wfNode->setGeometry(wfg);
1448 m_rootNode->appendChildNode(wfNode);
1449 debugNodes->append(wfNode);
1465 return debugVisualizationFlags | envFlags;
1470 if (debugVisualizationFlags == options)
1472 debugVisualizationFlags = options;
1475void QQuickShapeCurveRenderer::deleteAndClear(NodeList *nodeList)
1477 for (
QSGNode *node :
std::as_const(*nodeList))
1497 return p1.x() * (
p2.y() - p3.
y())
1498 +
p2.x() * (p3.
y() -
p1.y())
1499 + p3.
x() * (
p1.y() -
p2.y());
1505 double det = determinant(
p1,
p2, p3);
1522 fixWinding(triangle1[0], triangle1[1], triangle1[2]);
1523 for (
int i = 0;
i < 3;
i++) {
1524 int ni = (
i + 1) % 3;
1525 if (checkEdge(triangle1[
i], triangle1[ni], triangle2[0],
epsilon) &&
1526 checkEdge(triangle1[
i], triangle1[ni], triangle2[1],
epsilon) &&
1527 checkEdge(triangle1[
i], triangle1[ni], triangle2[2],
epsilon))
1532 fixWinding(triangle2[0], triangle2[1], triangle2[2]);
1533 for (
int i = 0;
i < 3;
i++) {
1534 int ni = (
i + 1) % 3;
1536 if (checkEdge(triangle2[
i], triangle2[ni], triangle1[0],
epsilon) &&
1537 checkEdge(triangle2[
i], triangle2[ni], triangle1[1],
epsilon) &&
1538 checkEdge(triangle2[
i], triangle2[ni], triangle1[2],
epsilon))
1548 bool s1 = determinant(
line[0],
line[1], triangle[0]) < 0;
1549 auto s2 = determinant(
line[0],
line[1], triangle[1]) < 0;
1550 auto s3 = determinant(
line[0],
line[1], triangle[2]) < 0;
1556 fixWinding(triangle[0], triangle[1], triangle[2]);
1557 for (
int i = 0;
i < 3;
i++) {
1558 int ni = (
i + 1) % 3;
1559 if (checkEdge(triangle[
i], triangle[ni],
line[0],
epsilon) &&
1560 checkEdge(triangle[
i], triangle[ni],
line[1],
epsilon))
1571 d1 = determinant(pt,
v1,
v2);
1572 d2 = determinant(pt,
v2,
v3);
1573 d3 = determinant(pt,
v3,
v1);
1578 return allNegative || allPositive;
1591 return checkLineTriangleOverlap(
t1,
line);
1594 return checkTriangleOverlap(
t1,
t2);
1609 int pathElementIndex;
1611 bool specialDebug =
false;
1653 bool *
ok =
nullptr,
bool *pointingRight =
nullptr)
1663 const auto bisector =
v1 +
v2;
1673 *pointingRight =
true;
1674 return (n2 - n1).normalized() * strokeMargin;
1682 cos2x =
qMin(1.0f, cos2x);
1683 float sine = sqrt((1.0f - cos2x) / 2);
1685 *
ok = sine >= inverseMiterLimit / 2.0f;
1687 *pointingRight = determinant(
p1,
p2, p3) > 0;
1688 sine =
qMax(sine, 0.01f);
1689 return bisector.normalized() * strokeMargin / sine;
1700 ret.append({{pts[0], pts[1], pts[2]}, elementIndex, {1, 0, 0}});
1704 float det1 = determinant(pts[0], pts[1], pts[2]);
1706 hull << pts[0] << pts[1] << pts[2];
1708 hull << pts[2] << pts[1] << pts[0];
1711 const int n = hull.
size();
1712 for (
int i = 0;
i <
n; ++
i) {
1713 const auto &
p1 = hull.
at(
i);
1714 const auto &
p2 = hull.
at((
i+1) %
n);
1715 if (determinant(
p1,
p2, pt) < 0.0f)
1721 const auto &
p = pts[
i];
1722 auto visible = connectableInHull(
p);
1723 if (visible.isEmpty())
1725 int visCount = visible.count();
1726 int hullCount = hull.
count();
1729 int boundaryStart = visible[0];
1730 for (
int j = 0;
j < visCount - 1; ++
j) {
1731 if ((visible[
j] + 1) % hullCount != visible[
j+1]) {
1732 boundaryStart = visible[
j + 1];
1737 for (
int j = 0;
j < visCount; ++
j) {
1738 const auto &
p1 = hull.
at((boundaryStart +
j) % hullCount);
1739 const auto &
p2 = hull.
at((boundaryStart +
j+1) % hullCount);
1740 ret.append({{
p1,
p2,
p}, elementIndex, {1,1,0}});
1745 int pointsToKeep = hullCount - visCount + 1;
1748 for (
int j = 0;
j < pointsToKeep; ++
j) {
1749 newHull << hull.
at((
j + boundaryStart + visCount) % hullCount);
1761 const bool miterJoin = !bevelJoin && !roundJoin;
1766 Q_ASSERT(miterLimit > 0 || !miterJoin);
1767 float inverseMiterLimit = miterJoin ? 1.0f / miterLimit : 1.0;
1772 const float extraFactor = roundJoin && additionalSpace ? (penWidth + additionalSpace) / penWidth : 2.0;
1777 while (subStart <
count) {
1778 int subEnd = subStart;
1779 for (
int i = subStart + 1;
i <
count; ++
i) {
1780 const auto &
e =
path.elementAt(
i);
1781 if (
e.isSubpathStart()) {
1790 bool closed =
path.elementAt(subStart).startPoint() ==
path.elementAt(subEnd).endPoint();
1791 const int subCount = subEnd - subStart + 1;
1794 int subIdx = idx - subStart;
1796 subIdx = (subIdx + subCount + delta) % subCount;
1797 return &
path.elementAt(subStart + subIdx);
1800 if (subIdx >= 0 && subIdx < subCount)
1801 return &
path.elementAt(subStart + subIdx);
1805 for (
int i = subStart;
i <= subEnd; ++
i) {
1806 const auto &element =
path.elementAt(
i);
1819 bool startBisectorPointsRight =
true;
1820 bool startBisectorWithinMiterLimit =
true;
1821 QVector2D startBisector = prevElement ? miterBisector(*prevElement, element, penWidth / 2, inverseMiterLimit, &startBisectorWithinMiterLimit, &startBisectorPointsRight) : startNormal;
1822 if (!startBisectorPointsRight)
1823 startBisector = -startBisector;
1825 bool endBisectorPointsRight =
true;
1826 bool endBisectorWithinMiterLimit =
true;
1827 QVector2D endBisector = nextElement ? miterBisector(element, *nextElement, penWidth / 2, inverseMiterLimit, &endBisectorWithinMiterLimit, &endBisectorPointsRight) : endNormal;
1828 if (!endBisectorPointsRight)
1829 endBisector = -endBisector;
1835 bool startMiter = simpleMiter && startBisectorWithinMiterLimit;
1836 bool endMiter = simpleMiter && endBisectorWithinMiterLimit;
1841 p1 =
s + startBisector * extraFactor;
1842 p2 =
s - startBisector * extraFactor;
1848 p1 =
s + startNormal * extraFactor;
1849 p2 =
s - startNormal * extraFactor;
1853 p3 =
e + endBisector * extraFactor;
1854 p4 =
e - endBisector * extraFactor;
1856 p3 =
e + endNormal * extraFactor;
1857 p4 =
e - endNormal * extraFactor;
1867 }
else if (squareCap) {
1870 ret.append({{
p1,
s, c1}, -1, {1, 1, 0},
true});
1871 ret.append({{c1,
s,
c2}, -1, {1, 1, 0},
true});
1872 ret.append({{
p2,
s,
c2}, -1, {1, 1, 0},
true});
1880 }
else if (squareCap) {
1883 ret.append({{p3,
e, c3}, -1, {1, 1, 0},
true});
1884 ret.append({{c3,
e, c4}, -1, {1, 1, 0},
true});
1885 ret.append({{p4,
e, c4}, -1, {1, 1, 0},
true});
1890 ret.append({{
p1,
p2, p3},
i, {0,1,0},
false});
1891 ret.append({{
p2, p3, p4},
i, {0.5,1,0},
false});
1893 bool controlPointOnRight = determinant(
s,
c,
e) > 0;
1895 QVector2D p5 = controlPointOnRight ?
c - controlPointOffset :
c + controlPointOffset;
1896 ret.append(simplePointTriangulator({
p1,
p2, p5, p3, p4},
i));
1899 if (!endMiter && nextElement) {
1903 const auto &np = nextElement->isLine() ? nextElement->endPoint() : nextElement->controlPoint();
1904 bool innerOnRight = endBisectorPointsRight;
1908 outer1 =
e - 2 * endNormal;
1909 outer2 =
e + 2 * nextNormal;
1911 outer1 =
e + 2 * endNormal;
1912 outer2 =
e - 2 * nextNormal;
1915 if (bevelJoin || (miterJoin && !endBisectorWithinMiterLimit)) {
1916 ret.append({{outer1,
e, outer2}, -1, {1,1,0},
false});
1917 }
else if (roundJoin) {
1918 ret.append({{outer1,
e, outer2},
i, {1,1,0},
false});
1922 ret.append({{outer1, outer1 + nn, outer2},
i, {1,1,0},
false});
1923 ret.append({{outer1 + nn, outer2, outer2 + nn},
i, {1,1,0},
false});
1925 }
else if (miterJoin) {
1926 QVector2D outer = innerOnRight ?
e - endBisector * 2 :
e + endBisector * 2;
1927 ret.append({{outer1,
e, outer}, -2, {1,1,0},
false});
1928 ret.append({{outer,
e, outer2}, -2, {1,1,0},
false});
1932 subStart = subEnd + 1;
1948 float miterLimit = pathData.pen.miterLimit();
1949 float penWidth = pathData.pen.widthF();
1951 auto thePath = pathData.strokePath;
1952 auto triangles = customTriangulator2(thePath, penWidth, pathData.pen.joinStyle(), pathData.pen.capStyle(), miterLimit);
1957 node->appendTriangle(p0,
p1,
p2,
1960 node->appendTriangle(p0,
p1,
p2,
1963 wfVertices.
append({p0.x(), p0.y(), 1.0f, 0.0f, 0.0f});
1964 wfVertices.
append({
p1.x(),
p1.y(), 0.0f, 1.0f, 0.0f});
1965 wfVertices.
append({
p2.x(),
p2.y(), 0.0f, 0.0f, 1.0f});
1979 if (determinant(p0,
p1,
p2) < 0)
1981 float delta = penWidth / 2;
1988 node->appendTriangle(p0,
p1,
p2, fp1, fp2);
1990 wfVertices.
append({p0.
x(), p0.
y(), 1.0f, 0.0f, 0.0f});
1991 wfVertices.
append({
p1.x(),
p1.y(), 0.0f, 1.0f, 0.0f});
1992 wfVertices.
append({
p2.x(),
p2.y(), 0.0f, 0.0f, 1.0f});
1996 for (
const auto &triangle :
triangles) {
1997 if (triangle.pathElementIndex < 0) {
1999 addBevelTriangle(triangle.points[0], triangle.points[1], triangle.points[2]);
2002 const auto &element = thePath.elementAt(triangle.pathElementIndex);
2003 addCurveTriangle(element, triangle.points[0], triangle.points[1], triangle.points[2]);
2006 auto indexCopy = node->uncookedIndexes();
2008 node->setColor(
color);
2009 node->setStrokeWidth(pathData.pen.widthF());
2010 node->cookGeometry();
2011 m_rootNode->appendChildNode(node);
2015 const bool wireFrame = debugVisualization() & DebugWireframe;
2017 QQuickShapeWireFrameNode *wfNode =
new QQuickShapeWireFrameNode;
2023 wfNode->setGeometry(wfg);
2033 m_rootNode->appendChildNode(wfNode);
2034 debugNodes->append(wfNode);
2045 if (!isOverlap(
path, e1, e2)) {
2049 if (recursionLevel > 8) {
2050 qDebug() <<
"Triangle overlap: recursion level" << recursionLevel <<
"aborting!";
2054 if (
path.elementAt(e1).childCount() > 1) {
2056 auto e11 =
path.indexOfChildAt(e1, 0);
2057 auto e12 =
path.indexOfChildAt(e1, 1);
2060 }
else if (
path.elementAt(e2).childCount() > 1) {
2062 auto e21 =
path.indexOfChildAt(e2, 0);
2063 auto e22 =
path.indexOfChildAt(e2, 1);
2067 path.splitElementAt(e1);
2068 auto e11 =
path.indexOfChildAt(e1, 0);
2069 auto e12 =
path.indexOfChildAt(e1, 1);
2070 bool overlap1 = isOverlap(
path, e11, e2);
2071 bool overlap2 = isOverlap(
path, e12, e2);
2075 if (!overlap1 && !overlap2)
2079 if (
path.elementAt(e2).isLine()) {
2087 path.splitElementAt(e2);
2088 auto e21 =
path.indexOfChildAt(e2, 0);
2089 auto e22 =
path.indexOfChildAt(e2, 1);
2104 int recursionLevel = 0)
2107 if (vertex ==
path.elementAt(e1).endPoint() || !isOverlap(
path, e1, vertex))
2109 if (recursionLevel > 8) {
2110 qDebug() <<
"Vertex overlap: recursion level" << recursionLevel <<
"aborting!";
2115 if (
path.elementAt(e1).childCount() == 0)
2116 path.splitElementAt(e1);
2122void QQuickShapeCurveRenderer::solveOverlaps(
QuadPath &
path)
2125 auto &element =
path.elementAt(
i);
2141 if (handleConcaveJoint) {
2150 auto &element =
path.elementAt(
i);
bool consume(float length)
QVector2D currentControlPoint()
ElementCutter(const QuadPath::Element &element)
QVector2D currentCutPoint()
static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
The QColor class provides colors based on RGB, HSV or CMYK values.
const_iterator constFind(const Key &key) const noexcept
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
qsizetype size() const noexcept
qsizetype length() const noexcept
const_reference at(qsizetype i) const noexcept
qsizetype count() const noexcept
void append(parameter_type t)
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
qreal widthF() const
Returns the pen width with floating point precision.
QList< qreal > dashPattern() const
Returns the dash pattern of this pen.
qreal dashOffset() const
Returns the dash offset for the pen.
Qt::PenStyle style() const
Returns the pen style.
\inmodule QtCore\reentrant
The QPolygonF class provides a list of points using floating point precision.
QGradientStops gradientStops() const
void setColor(QColor col)
void setGradientType(QQuickAbstractPathRenderer::FillGradientType type)
void beginSync(int totalCount, bool *countChanged) override
static Q_QUICKSHAPES_PRIVATE_EXPORT int debugVisualization()
void setRootNode(QSGNode *node)
static Q_QUICKSHAPES_PRIVATE_EXPORT void setDebugVisualization(int options)
void setFillGradient(int index, QQuickShapeGradient *gradient) override
void endSync(bool async) override
void setStrokeWidth(int index, qreal w) override
void updateNode() override
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override
~QQuickShapeCurveRenderer() override
void setStrokeColor(int index, const QColor &color) override
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override
void setFillColor(int index, const QColor &color) override
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, qreal dashOffset, const QVector< qreal > &dashPattern) override
void setPath(int index, const QQuickPath *path) override
void setAsyncCallback(void(*)(void *), void *) override
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override
\inmodule QtCore\reentrant
void setGeometry(QSGGeometry *geometry)
Sets the geometry of this node to geometry.
The QSGGeometryNode class is used for all rendered content in the scene graph.
void setMaterial(QSGMaterial *material)
Sets the material of this geometry node to material.
The QSGGeometry class provides low-level storage for graphics primitives in the \l{Qt Quick Scene Gra...
void setDrawingMode(unsigned int mode)
Sets the mode to be used for drawing this geometry.
void * indexData()
Returns a pointer to the raw index data of this geometry object.
int sizeOfVertex() const
Returns the size in bytes of one vertex.
void * vertexData()
Returns a pointer to the raw vertex data of this geometry object.
int sizeOfIndex() const
Returns the byte size of the index type.
int vertexCount() const
Returns the number of vertices in this geometry object.
The QSGMaterialShader class represents a graphics API independent shader program.
virtual bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
This function is called by the scene graph to get the contents of the shader program's uniform buffer...
void setShaderFileName(Stage stage, const QString &filename)
Sets the filename for the shader for the specified stage.
The QSGMaterial class encapsulates rendering state for a shader program.
virtual QSGMaterialShader * createShader(QSGRendererInterface::RenderMode renderMode) const =0
This function returns a new instance of a the QSGMaterialShader implementation used to render geometr...
virtual int compare(const QSGMaterial *other) const
Compares this material to other and returns 0 if they are equal; -1 if this material should sort befo...
virtual QSGMaterialType * type() const =0
This function is called by the scene graph to query an identifier that is unique to the QSGMaterialSh...
void setFlag(Flags flags, bool on=true)
Sets the flags flags on this material if on is true; otherwise clears the attribute.
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
RenderMode
\value RenderMode2D Normal 2D rendering \value RenderMode2DNoDepthBuffer Normal 2D rendering with dep...
const_iterator cend() const noexcept
const float * vertices() const
void process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints)
The QVector2D class represents a vector or vertex in 2D space.
constexpr float y() const noexcept
Returns the y coordinate of this point.
QVector2D normalized() const noexcept
Returns the normalized unit vector form of this vector.
constexpr float x() const noexcept
Returns the x coordinate of this point.
static constexpr float dotProduct(QVector2D v1, QVector2D v2) noexcept
Returns the dot product of v1 and v2.
constexpr void setY(float y) noexcept
Sets the y coordinate of this point to the given finite y coordinate.
constexpr QPointF toPointF() const noexcept
Returns the QPointF form of this 2D vector.
constexpr void setX(float x) noexcept
Sets the x coordinate of this point to the given finite x coordinate.
The QVector3D class represents a vector or vertex in 3D space.
The QVector4D class represents a vector or vertex in 4D space.
QVector2D pointAtFraction(float t) const
bool isSubpathEnd() const
QVector2D startPoint() const
QVector2D midPoint() const
QVector2D controlPoint() const
qsizetype indexOfChild(qsizetype childNumber) const
qsizetype childCount() const
bool isSubpathStart() const
QVector2D endPoint() const
QVector3D uvForPoint(QVector2D p) const
qsizetype elementCount() const
static QuadPath fromPainterPath(const QPainterPath &path)
QuadPath flattened() const
void splitElementAt(qsizetype index)
qsizetype elementCountRecursive() const
QPainterPath toPainterPath() const
QuadPath subPathsClosed() const
Qt::FillRule fillRule() const
QuadPath dashed(qreal lineWidth, const QList< qreal > &dashPattern, qreal dashOffset=0) const
friend QDebug operator<<(QDebug, const QuadPath &)
QRectF controlPointRect() const
void iterateElements(Func &&lambda)
bool contains(const QVector2D &v) const
Element & elementAt(qsizetype i)
QSet< QString >::iterator it
Combined button and popup list for selecting options.
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
std::pair< T1, T2 > QPair
static struct AttrInfo attrs[]
bool qIsFinite(qfloat16 f) noexcept
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
bool qFuzzyIsNull(qfloat16 f) noexcept
qfloat16 qSqrt(qfloat16 f)
int qRound(qfloat16 d) noexcept
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qBound(const T &min, const T &val, const T &max)
constexpr const T & qMax(const T &a, const T &b)
GLint GLfloat GLfloat GLfloat v2
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLint GLfloat GLfloat GLfloat GLfloat v3
GLuint GLfloat GLfloat y0
GLsizei GLenum const void * indices
GLfixed GLfixed GLint GLint GLfixed points
GLfixed GLfixed GLfixed y2
GLsizei const GLchar *const * path
GLint GLenum GLboolean normalized
const QVectorPath & qtVectorPathForPath(const QPainterPath &path)
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
static qreal dot(const QPointF &a, const QPointF &b)
static bool isLine(const QBezier &bezier)
static const qreal epsilon
static void printElement(QDebug stream, const QuadPath::Element &element)
static QVector2D uvForPoint(QVector2D v1, QVector2D v2, QVector2D p)
static float crossProduct(const QVector2D &sp, const QVector2D &p, const QVector2D &ep)
static QVector2D calcNormalVector(QVector2D a, QVector2D b)
static void handleOverlap(QuadPath &path, qsizetype e1, qsizetype e2, int recursionLevel=0)
static QVector2D curveUv(QVector2D p0, QVector2D p1, QVector2D p2, QVector2D p)
#define QQUICKSHAPECURVERENDERER_CONVEX_CHECK_ERROR_MARGIN
static float testSideOfLineByNormal(QVector2D a, QVector2D n, QVector2D p)
void iteratePath(const QuadPath &path, int index, Func &&lambda)
Q_QUICKSHAPES_PRIVATE_EXPORT void qt_toQuadratics(const QBezier &b, QPolygonF *out, qreal errorLimit=0.01)
static const struct TessellationModeTab triangles[]
#define QStringLiteral(str)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
Q_GUI_EXPORT QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint, const QTransform &matrix, bool allowUintIndices)
The QSGGeometry::AttributeSet describes how the vertices in a QSGGeometry are built up.
The QSGGeometry::Attribute describes a single vertex attribute in a QSGGeometry.
static Attribute createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType)
Creates a new QSGGeometry::Attribute for attribute register pos with tupleSize.
The QSGMaterialType class is used as a unique type token in combination with QSGMaterial.
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent