aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/scenegraph/qsgcurveprocessor.cpp12
-rw-r--r--src/quick/scenegraph/qsgcurveprocessor_p.h4
-rw-r--r--src/quick/scenegraph/util/qquadpath.cpp33
-rw-r--r--src/quick/scenegraph/util/qquadpath_p.h46
-rw-r--r--src/quickshapes/qquickshapecurverenderer.cpp11
-rw-r--r--src/quickshapes/qquickshapecurverenderer_p.h1
6 files changed, 87 insertions, 20 deletions
diff --git a/src/quick/scenegraph/qsgcurveprocessor.cpp b/src/quick/scenegraph/qsgcurveprocessor.cpp
index 29e223d49f..a58c087fa3 100644
--- a/src/quick/scenegraph/qsgcurveprocessor.cpp
+++ b/src/quick/scenegraph/qsgcurveprocessor.cpp
@@ -884,8 +884,10 @@ static void handleOverlap(QQuadPath &path, int e1, const QVector2D vertex, int r
}
-void QSGCurveProcessor::solveOverlaps(QQuadPath &path)
+void QSGCurveProcessor::solveOverlaps(QQuadPath &path, OverlapSolveMode mode)
{
+ if (path.testHint(QQuadPath::PathNonOverlappingControlPointTriangles))
+ return;
for (int i = 0; i < path.elementCount(); i++) {
auto &element = path.elementAt(i);
// only concave curve overlap is problematic, as long as we don't allow self-intersecting curves
@@ -902,7 +904,7 @@ void QSGCurveProcessor::solveOverlaps(QQuadPath &path)
}
}
- static const int handleConcaveJoint = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_CONCAVE_JOINT");
+ const bool handleConcaveJoint = mode == FullOverlapSolve;
if (handleConcaveJoint) {
// Note that the joint between two non-concave elements can also be concave, so we have to
// test all convex elements to see if there is a vertex in any of them. We could do it the other way
@@ -926,6 +928,7 @@ void QSGCurveProcessor::solveOverlaps(QQuadPath &path)
}
}
}
+ path.setHint(QQuadPath::PathNonOverlappingControlPointTriangles);
}
// A fast algorithm to find path elements that might overlap. We will only check the overlap of the
@@ -996,6 +999,9 @@ QList<QPair<int, int>> QSGCurveProcessor::findOverlappingCandidates(const QQuadP
// Returns true if the path was changed
bool QSGCurveProcessor::solveIntersections(QQuadPath &path, bool alwaysReorder)
{
+ if (path.testHint(QQuadPath::PathNonIntersecting))
+ return false;
+
struct IntersectionData { int e1; int e2; float t1; float t2; bool in1 = false, in2 = false, out1 = false, out2 = false; };
QList<IntersectionData> intersections;
@@ -1330,6 +1336,7 @@ bool QSGCurveProcessor::solveIntersections(QQuadPath &path, bool alwaysReorder)
qCDebug(lcSGCurveIntersectionSolver) << "All subpaths handled. Looking for unhandled intersections.";
if (intersections.isEmpty()) {
qCDebug(lcSGCurveIntersectionSolver) << "All intersections handled. I am done.";
+ fixedPath.setPathHints(path.pathHints() | QQuadPath::PathNonIntersecting);
path = fixedPath;
return true;
}
@@ -1396,6 +1403,7 @@ bool QSGCurveProcessor::solveIntersections(QQuadPath &path, bool alwaysReorder)
// Check the totalIterations as a sanity check. Should never be triggered.
qWarning() << "Could not solve intersections of path. This should not happen. Returning the path unchanged.";
+
return false;
}
diff --git a/src/quick/scenegraph/qsgcurveprocessor_p.h b/src/quick/scenegraph/qsgcurveprocessor_p.h
index d9ca9da5bb..18e08dcbae 100644
--- a/src/quick/scenegraph/qsgcurveprocessor_p.h
+++ b/src/quick/scenegraph/qsgcurveprocessor_p.h
@@ -32,6 +32,7 @@ public:
const std::array<QVector2D, 3> &,
bool)> addStrokeTriangleCallback;
+ enum OverlapSolveMode { FullOverlapSolve, SkipConcaveJoinsSolve };
static void processFill(const QQuadPath &path,
Qt::FillRule fillRule,
@@ -43,10 +44,9 @@ public:
Qt::PenCapStyle capStyle,
addStrokeTriangleCallback addTriangle,
int subdivisions = 3);
- static void solveOverlaps(QQuadPath &path);
+ static void solveOverlaps(QQuadPath &path, OverlapSolveMode mode = SkipConcaveJoinsSolve);
static QList<QPair<int, int>> findOverlappingCandidates(const QQuadPath &path);
static bool solveIntersections(QQuadPath &path, bool alwaysReorder = true);
-
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qquadpath.cpp b/src/quick/scenegraph/util/qquadpath.cpp
index 70fca33e1c..188d53cd6a 100644
--- a/src/quick/scenegraph/util/qquadpath.cpp
+++ b/src/quick/scenegraph/util/qquadpath.cpp
@@ -474,12 +474,14 @@ QQuadPath::Element::CurvatureFlags QQuadPath::coordinateOrderOfElement(const QQu
return pathContainsPoint ? Element::FillOnRight : Element::CurvatureFlags(0);
}
-QQuadPath QQuadPath::fromPainterPath(const QPainterPath &path)
+QQuadPath QQuadPath::fromPainterPath(const QPainterPath &path, PathHints hints)
{
QQuadPath res;
res.reserve(path.elementCount());
res.setFillRule(path.fillRule());
+ const bool isQuadratic = hints & PathQuadratic;
+
QPolygonF quads;
QPointF sp;
for (int i = 0; i < path.elementCount(); ++i) {
@@ -497,12 +499,18 @@ QQuadPath QQuadPath::fromPainterPath(const QPainterPath &path)
QPointF cp1 = ep;
QPointF cp2(path.elementAt(++i));
ep = path.elementAt(++i);
- QBezier b = QBezier::fromPoints(sp, cp1, cp2, ep);
- qt_toQuadratics(b, &quads);
- for (int i = 1; i < quads.size(); i += 2) {
- QVector2D cp(quads[i]);
- QVector2D ep(quads[i + 1]);
- res.quadTo(cp, ep);
+ if (isQuadratic) {
+ const qreal f = 3.0 / 2.0;
+ const QPointF cp = sp + f * (cp1 - sp);
+ res.quadTo(QVector2D(cp), QVector2D(ep));
+ } else {
+ QBezier b = QBezier::fromPoints(sp, cp1, cp2, ep);
+ qt_toQuadratics(b, &quads);
+ for (int i = 1; i < quads.size(); i += 2) {
+ QVector2D cp(quads[i]);
+ QVector2D ep(quads[i + 1]);
+ res.quadTo(cp, ep);
+ }
}
break;
}
@@ -513,6 +521,7 @@ QQuadPath QQuadPath::fromPainterPath(const QPainterPath &path)
sp = ep;
}
+ res.setPathHints(hints | PathQuadratic);
return res;
}
@@ -527,12 +536,16 @@ void QQuadPath::addCurvatureData()
// can easily detect curvature of all subsequent elements in the subpath.
static bool checkAnomaly = qEnvironmentVariableIntValue("QT_QUICKSHAPES_CHECK_ALL_CURVATURE") != 0;
+ const bool pathHasFillOnRight = testHint(PathFillOnRight);
Element::CurvatureFlags flags = Element::CurvatureUndetermined;
for (QQuadPath::Element &element : m_elements) {
Q_ASSERT(element.childCount() == 0);
if (element.isSubpathStart()) {
- flags = coordinateOrderOfElement(element);
+ if (pathHasFillOnRight && !checkAnomaly)
+ flags = Element::FillOnRight;
+ else
+ flags = coordinateOrderOfElement(element);
} else if (checkAnomaly) {
Element::CurvatureFlags newFlags = coordinateOrderOfElement(element);
if (flags != newFlags) {
@@ -676,6 +689,8 @@ QQuadPath QQuadPath::flattened() const
QQuadPath res;
res.reserve(elementCountRecursive());
iterateElements([&](const QQuadPath::Element &element) { res.m_elements.append(element); });
+ res.setPathHints(pathHints());
+ res.setFillRule(fillRule());
return res;
}
@@ -843,6 +858,8 @@ QQuadPath QQuadPath::dashed(qreal lineWidth, const QList<qreal> &dashPattern, qr
}
}
}
+ res.setFillRule(fillRule());
+ res.setPathHints(pathHints());
return res;
}
diff --git a/src/quick/scenegraph/util/qquadpath_p.h b/src/quick/scenegraph/util/qquadpath_p.h
index eb27e7b4fe..82c571eff5 100644
--- a/src/quick/scenegraph/util/qquadpath_p.h
+++ b/src/quick/scenegraph/util/qquadpath_p.h
@@ -27,6 +27,18 @@ QT_BEGIN_NAMESPACE
class Q_QUICK_EXPORT QQuadPath
{
public:
+ // This is a copy of the flags in QQuickShapePath ### TODO: use a common definition
+ enum PathHint : quint8 {
+ PathLinear = 0x1,
+ PathQuadratic = 0x2,
+ PathConvex = 0x4,
+ PathFillOnRight = 0x8,
+ PathSolid = 0x10,
+ PathNonIntersecting = 0x20,
+ PathNonOverlappingControlPointTriangles = 0x40
+ };
+ Q_DECLARE_FLAGS(PathHints, PathHint)
+
class Element
{
public:
@@ -168,7 +180,7 @@ public:
quint8 m_isSubpathEnd : 1;
quint8 m_isLine : 1;
friend class QQuadPath;
- friend QDebug operator<<(QDebug, const QQuadPath::Element &);
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
};
void moveTo(const QVector2D &to)
@@ -212,7 +224,7 @@ public:
bool isEmpty() const { return m_elements.size() == 0; }
int elementCountRecursive() const;
- static QQuadPath fromPainterPath(const QPainterPath &path);
+ static QQuadPath fromPainterPath(const QPainterPath &path, PathHints hints = {});
QPainterPath toPainterPath() const;
QString asSvgString() const;
@@ -277,11 +289,33 @@ public:
static bool isPointOnLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
static bool isPointNearLine(const QVector2D &p, const QVector2D &sp, const QVector2D &ep);
+ bool testHint(PathHint hint) const
+ {
+ return m_hints.testFlag(hint);
+ }
+
+ void setHint(PathHint hint, bool on = true)
+ {
+ m_hints.setFlag(hint, on);
+ }
+
+ PathHints pathHints() const
+ {
+ return m_hints;
+ }
+
+ void setPathHints(PathHints newHints)
+ {
+ m_hints = newHints;
+ }
+
private:
void addElement(const QVector2D &control, const QVector2D &to, bool isLine = false);
Element::CurvatureFlags coordinateOrderOfElement(const Element &element) const;
- friend QDebug operator<<(QDebug, const QQuadPath &);
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
+
+ PathHints m_hints;
bool subPathToStart = true;
Qt::FillRule m_fillRule = Qt::OddEvenFill;
QVector2D currentPoint;
@@ -289,8 +323,10 @@ private:
QList<Element> m_childElements;
};
-QDebug operator<<(QDebug, const QQuadPath::Element &);
-QDebug operator<<(QDebug, const QQuadPath &);
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuadPath::PathHints);
+
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath::Element &);
+Q_QUICK_EXPORT QDebug operator<<(QDebug, const QQuadPath &);
QT_END_NAMESPACE
diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp
index df24d6df9a..93ccca4596 100644
--- a/src/quickshapes/qquickshapecurverenderer.cpp
+++ b/src/quickshapes/qquickshapecurverenderer.cpp
@@ -148,6 +148,9 @@ void QQuickShapeCurveRenderer::setPath(int index, const QQuickPath *path)
auto &pathData = m_paths[index];
pathData.originalPath = path->path();
pathData.m_dirty |= PathDirty;
+ const auto *shapePath = qobject_cast<const QQuickShapePath *>(path);
+ if (shapePath)
+ pathData.pathHints = shapePath->pathHints();
}
void QQuickShapeCurveRenderer::setStrokeColor(int index, const QColor &color)
@@ -419,14 +422,16 @@ void QQuickShapeCurveRenderer::processPath(PathData *pathData)
static const bool doIntersetionSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_INTERSECTION_SOLVER");
static const bool useTriangulatingStroker = qEnvironmentVariableIntValue("QT_QUICKSHAPES_TRIANGULATING_STROKER");
static const bool simplifyPath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SIMPLIFY_PATHS");
+ static const QSGCurveProcessor::OverlapSolveMode overlapMode = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_CONCAVE_JOINT")
+ ? QSGCurveProcessor::FullOverlapSolve : QSGCurveProcessor::SkipConcaveJoinsSolve;
int &dirtyFlags = pathData->m_dirty;
if (dirtyFlags & PathDirty) {
if (simplifyPath)
- pathData->path = QQuadPath::fromPainterPath(pathData->originalPath.simplified());
+ pathData->path = QQuadPath::fromPainterPath(pathData->originalPath.simplified(), QQuadPath::PathLinear | QQuadPath::PathNonIntersecting | QQuadPath::PathNonOverlappingControlPointTriangles);
else
- pathData->path = QQuadPath::fromPainterPath(pathData->originalPath);
+ pathData->path = QQuadPath::fromPainterPath(pathData->originalPath, QQuadPath::PathHints(int(pathData->pathHints)));
pathData->path.setFillRule(pathData->fillRule);
pathData->fillPath = {};
dirtyFlags |= (FillDirty | StrokeDirty);
@@ -440,7 +445,7 @@ void QQuickShapeCurveRenderer::processPath(PathData *pathData)
QSGCurveProcessor::solveIntersections(pathData->fillPath);
pathData->fillPath.addCurvatureData();
if (doOverlapSolving)
- QSGCurveProcessor::solveOverlaps(pathData->fillPath);
+ QSGCurveProcessor::solveOverlaps(pathData->fillPath, overlapMode);
}
pathData->fillNodes = addFillNodes(*pathData);
dirtyFlags |= StrokeDirty;
diff --git a/src/quickshapes/qquickshapecurverenderer_p.h b/src/quickshapes/qquickshapecurverenderer_p.h
index 4b192a9218..f46521d4f7 100644
--- a/src/quickshapes/qquickshapecurverenderer_p.h
+++ b/src/quickshapes/qquickshapecurverenderer_p.h
@@ -98,6 +98,7 @@ private:
QPen pen;
bool validPenWidth = true;
int m_dirty = 0;
+ QQuickShapePath::PathHints pathHints;
QPainterPath originalPath;
QQuadPath path;