diff options
-rw-r--r-- | src/quick/scenegraph/qsgcurveprocessor.cpp | 12 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgcurveprocessor_p.h | 4 | ||||
-rw-r--r-- | src/quick/scenegraph/util/qquadpath.cpp | 33 | ||||
-rw-r--r-- | src/quick/scenegraph/util/qquadpath_p.h | 46 | ||||
-rw-r--r-- | src/quickshapes/qquickshapecurverenderer.cpp | 11 | ||||
-rw-r--r-- | src/quickshapes/qquickshapecurverenderer_p.h | 1 |
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; |