7#include <private/qbezier_p.h>
8#include <private/qdatabuffer_p.h>
9#include <private/qnumeric_p.h>
14
15
16
17
18
19
20
21
22
23
24
25
26
27
35 if (
sizeof(qreal) ==
sizeof(
double))
36 return qAbs(d) <= 1e-12;
38 return qAbs(d) <= 1e-5f;
43 return fuzzyIsNull(a.x() - b.x())
44 && fuzzyIsNull(a.y() - b.y());
48static qreal dot(
const QPointF &a,
const QPointF &b)
50 return a.x() * b.x() + a.y() * b.y();
55 double reciprocal = 1 / qSqrt(x * x + y * y);
76 bool linesIntersect(
const QLineF &a,
const QLineF &b)
const;
81 const QPointF p1 = a.p1();
82 const QPointF p2 = a.p2();
84 const QPointF q1 = b.p1();
85 const QPointF q2 = b.p2();
87 if (comparePoints(p1, p2) || comparePoints(q1, q2))
90 const bool p1_equals_q1 = comparePoints(p1, q1);
91 const bool p2_equals_q2 = comparePoints(p2, q2);
93 if (p1_equals_q1 && p2_equals_q2)
96 const bool p1_equals_q2 = comparePoints(p1, q2);
97 const bool p2_equals_q1 = comparePoints(p2, q1);
99 if (p1_equals_q2 && p2_equals_q1)
102 const QPointF pDelta = p2 - p1;
103 const QPointF qDelta = q2 - q1;
105 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
107 if (qFuzzyIsNull(par)) {
108 const QPointF normal(-pDelta.y(), pDelta.x());
111 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
112 const qreal dp = dot(pDelta, pDelta);
114 const qreal tq1 = dot(pDelta, q1 - p1);
115 const qreal tq2 = dot(pDelta, q2 - p1);
117 if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
120 const qreal dq = dot(qDelta, qDelta);
122 const qreal tp1 = dot(qDelta, p1 - q1);
123 const qreal tp2 = dot(qDelta, p2 - q1);
125 if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
132 const qreal invPar = 1 / par;
134 const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
135 qDelta.x() * (q1.y() - p1.y())) * invPar;
137 if (tp < 0 || tp > 1)
140 const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
141 pDelta.x() * (q1.y() - p1.y())) * invPar;
143 return tq >= 0 && tq <= 1;
151 const QRectF &rb0 = b.elementBounds(0);
153 qreal minX = rb0.left();
154 qreal minY = rb0.top();
155 qreal maxX = rb0.right();
156 qreal maxY = rb0.bottom();
159 const QRectF &r = b.elementBounds(i);
160 minX = qMin(minX, r.left());
161 minY = qMin(minY, r.top());
162 maxX = qMax(maxX, r.right());
163 maxY = qMax(maxY, r.bottom());
166 QRectF rb(minX, minY, maxX - minX, maxY - minY);
169 const QRectF &r1 = a.elementBounds(i);
171 if (r1.left() > rb.right() || rb.left() > r1.right())
173 if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
177 const QRectF &r2 = b.elementBounds(j);
179 if (r1.left() > r2.right() || r2.left() > r1.right())
181 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
184 if (linesIntersect(a.lineAt(i), b.lineAt(j)))
200 int lowestRightIndex;
227 void produceIntersections(
int segment);
230 TreeNode buildTree(
int first,
int last,
int depth,
const RectF &bounds);
232 void produceIntersectionsLeaf(
const TreeNode &node,
int segment);
233 void produceIntersections(
const TreeNode &node,
int segment,
const RectF &segmentBounds,
const RectF &nodeBounds,
int axis);
234 void intersectLines(
const QLineF &a,
const QLineF &b, QDataBuffer<QIntersection> &intersections);
241 QList<TreeNode> m_tree;
242 QDataBuffer<QIntersection> m_intersections;
246 : m_segments(segments),
249 m_bounds.x1 = qt_inf();
250 m_bounds.y1 = qt_inf();
251 m_bounds.x2 = -qt_inf();
252 m_bounds.y2 = -qt_inf();
254 m_index.resize(m_segments.segments());
256 for (
int i = 0; i < m_index.size(); ++i) {
259 const QRectF &segmentBounds = m_segments.elementBounds(i);
261 if (segmentBounds.left() < m_bounds.x1)
262 m_bounds.x1 = segmentBounds.left();
263 if (segmentBounds.top() < m_bounds.y1)
264 m_bounds.y1 = segmentBounds.top();
265 if (segmentBounds.right() > m_bounds.x2)
266 m_bounds.x2 = segmentBounds.right();
267 if (segmentBounds.bottom() > m_bounds.y2)
268 m_bounds.y2 = segmentBounds.bottom();
273 TreeNode root = buildTree(0, m_index.size(), 0, m_bounds);
277static inline qreal coordinate(
const QPointF &pos,
int axis)
279 return axis == 0 ? pos.x() : pos.y();
282TreeNode SegmentTree::buildTree(
int first,
int last,
int depth,
const RectF &bounds)
284 if (depth >= 24 || (last - first) <= 10) {
287 node.index.interval.first = first;
288 node.index.interval.last = last;
293 int splitAxis = (depth & 1);
298 qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]);
300 node.splitLeft = (&bounds.x1)[splitAxis];
301 node.splitRight = (&bounds.x2)[splitAxis];
303 node.lowestLeftIndex = INT_MAX;
304 node.lowestRightIndex = INT_MAX;
306 const int treeSize = m_tree.size();
308 node.index.children.left = treeSize;
309 node.index.children.right = treeSize + 1;
311 m_tree.resize(treeSize + 2);
318 const int index = m_index.at(l);
319 const QRectF &segmentBounds = m_segments.elementBounds(index);
321 qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis);
323 if (coordinate(segmentBounds.center(), splitAxis) < split) {
324 qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis);
325 if (highCoordinate > node.splitLeft)
326 node.splitLeft = highCoordinate;
327 if (index < node.lowestLeftIndex)
328 node.lowestLeftIndex = index;
331 if (lowCoordinate < node.splitRight)
332 node.splitRight = lowCoordinate;
333 if (index < node.lowestRightIndex)
334 node.lowestRightIndex = index;
335 qSwap(m_index[l], m_index[r]);
340 RectF lbounds = bounds;
341 (&lbounds.x2)[splitAxis] = node.splitLeft;
343 RectF rbounds = bounds;
344 (&rbounds.x1)[splitAxis] = node.splitRight;
346 TreeNode left = buildTree(first, l, depth + 1, lbounds);
347 m_tree[node.index.children.left] = left;
349 TreeNode right = buildTree(l, last, depth + 1, rbounds);
350 m_tree[node.index.children.right] = right;
355void SegmentTree::intersectLines(
const QLineF &a,
const QLineF &b, QDataBuffer<QIntersection> &intersections)
357 const QPointF p1 = a.p1();
358 const QPointF p2 = a.p2();
360 const QPointF q1 = b.p1();
361 const QPointF q2 = b.p2();
363 if (comparePoints(p1, p2) || comparePoints(q1, q2))
366 const bool p1_equals_q1 = comparePoints(p1, q1);
367 const bool p2_equals_q2 = comparePoints(p2, q2);
369 if (p1_equals_q1 && p2_equals_q2)
372 const bool p1_equals_q2 = comparePoints(p1, q2);
373 const bool p2_equals_q1 = comparePoints(p2, q1);
375 if (p1_equals_q2 && p2_equals_q1)
378 const QPointF pDelta = p2 - p1;
379 const QPointF qDelta = q2 - q1;
381 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
383 if (qFuzzyIsNull(par)) {
384 const QPointF normal(-pDelta.y(), pDelta.x());
387 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
388 const qreal invDp = 1 / dot(pDelta, pDelta);
390 const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
391 const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
393 if (tq1 > 0 && tq1 < 1) {
395 intersection.alphaA = tq1;
396 intersection.alphaB = 0;
397 intersection.pos = q1;
398 intersections.add(intersection);
401 if (tq2 > 0 && tq2 < 1) {
403 intersection.alphaA = tq2;
404 intersection.alphaB = 1;
405 intersection.pos = q2;
406 intersections.add(intersection);
409 const qreal invDq = 1 / dot(qDelta, qDelta);
411 const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
412 const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
414 if (tp1 > 0 && tp1 < 1) {
416 intersection.alphaA = 0;
417 intersection.alphaB = tp1;
418 intersection.pos = p1;
419 intersections.add(intersection);
422 if (tp2 > 0 && tp2 < 1) {
424 intersection.alphaA = 1;
425 intersection.alphaB = tp2;
426 intersection.pos = p2;
427 intersections.add(intersection);
436 if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
440 const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
441 qDelta.x() * (q1.y() - p1.y())) / par;
442 const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
443 pDelta.x() * (q1.y() - p1.y())) / par;
445 if (tp<0 || tp>1 || tq<0 || tq>1)
448 const bool p_zero = qFuzzyIsNull(tp);
449 const bool p_one = qFuzzyIsNull(tp - 1);
451 const bool q_zero = qFuzzyIsNull(tq);
452 const bool q_one = qFuzzyIsNull(tq - 1);
454 if ((q_zero || q_one) && (p_zero || p_one))
467 pt = q1 + (q2 - q1) * tq;
471 intersection.alphaA = tp;
472 intersection.alphaB = tq;
473 intersection.pos = pt;
474 intersections.add(intersection);
477void SegmentTree::produceIntersections(
int segment)
479 const QRectF &segmentBounds = m_segments.elementBounds(segment);
482 sbounds.x1 = segmentBounds.left();
483 sbounds.y1 = segmentBounds.top();
484 sbounds.x2 = segmentBounds.right();
485 sbounds.y2 = segmentBounds.bottom();
487 produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0);
490void SegmentTree::produceIntersectionsLeaf(
const TreeNode &node,
int segment)
492 const QRectF &r1 = m_segments.elementBounds(segment);
493 const QLineF lineA = m_segments.lineAt(segment);
495 for (
int i = node.index.interval.first; i < node.index.interval.last; ++i) {
496 const int other = m_index.at(i);
497 if (other >= segment)
500 const QRectF &r2 = m_segments.elementBounds(other);
502 if (r1.left() > r2.right() || r2.left() > r1.right())
504 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
507 m_intersections.reset();
509 const QLineF lineB = m_segments.lineAt(other);
511 intersectLines(lineA, lineB, m_intersections);
513 for (
int k = 0; k < m_intersections.size(); ++k) {
515 i_isect.t = m_intersections.at(k).alphaA;
516 j_isect.t = m_intersections.at(k).alphaB;
518 i_isect
.vertex = j_isect
.vertex = m_segments.addPoint(m_intersections.at(k).pos);
529void SegmentTree::produceIntersections(
const TreeNode &node,
int segment,
const RectF &segmentBounds,
const RectF &nodeBounds,
int axis)
532 produceIntersectionsLeaf(node, segment);
536 RectF lbounds = nodeBounds;
537 (&lbounds.x2)[axis] = node.splitLeft;
539 RectF rbounds = nodeBounds;
540 (&rbounds.x1)[axis] = node.splitRight;
542 if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft)
543 produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis);
545 if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight)
546 produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis);
553 SegmentTree tree(segments);
556 tree.produceIntersections(i);
578 : m_segments(&segments)
582 m_nodes.resize(m_segments->points());
584 for (
int i = 0; i < m_nodes.size(); ++i) {
585 m_nodes.at(i).point = i;
586 m_nodes.at(i).id = -1;
589 m_rootNode =
build(0, m_nodes.size());
592 int build(
int begin,
int end,
int depth = 0);
596 return &m_nodes.at(m_rootNode);
606 QDataBuffer<Node> m_nodes;
620 if (traverseLeft && node
.left)
621 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node
.left, t, depth + 1);
623 if (traverseRight && node
.right)
624 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node
.right, t, depth + 1);
630 const qreal components[] = { point.x(), point.y() };
631 return components[i];
636 Q_ASSERT(end > begin);
638 const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
640 int first = begin + 1;
643 while (first <= last) {
644 const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
649 qSwap(m_nodes.at(first), m_nodes.at(last));
655 qSwap(m_nodes.at(last), m_nodes.at(begin));
658 m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
660 m_nodes.at(last).left =
nullptr;
663 m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
665 m_nodes.at(last).right =
nullptr;
675 , m_segments(&segments)
678 pointComponents[0] = segments.pointAt(point).x();
679 pointComponents[1] = segments.pointAt(point).y();
687 const QPointF &nodePoint = m_segments->pointAt(node
.point);
689 const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
691 const qreal pivot = pivotComponents[depth & 1];
692 const qreal value = pointComponents[depth & 1];
694 if (fuzzyIsNull(pivot - value)) {
695 const qreal pivot2 = pivotComponents[(depth + 1) & 1];
696 const qreal value2 = pointComponents[(depth + 1) & 1];
698 if (fuzzyIsNull(pivot2 - value2)) {
706 }
else if (value < pivot) {
719 qreal pointComponents[2];
731 QDataBuffer<QPointF> mergedPoints(points());
732 QDataBuffer<
int> pointIndices(points());
734 for (
int i = 0; i <
points(); ++i) {
740 if (finder.result() >= mergedPoints.size())
741 mergedPoints << m_points.at(i);
743 pointIndices << finder.result();
746 for (
int i = 0; i < m_segments.size(); ++i) {
747 m_segments.at(i).va = pointIndices.at(m_segments.at(i).va);
748 m_segments.at(i).vb = pointIndices.at(m_segments.at(i).vb);
751 for (
int i = 0; i < m_intersections.size(); ++i)
752 m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
754 m_points.swap(mergedPoints);
758void QWingedEdge::intersectAndAdd()
760 QIntersectionFinder finder;
761 finder.produceIntersections(m_segments);
763 m_segments.mergePoints();
765 for (
int i = 0; i < m_segments.points(); ++i)
766 addVertex(m_segments.pointAt(i));
768 QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments());
769 for (
int i = 0; i < m_segments.segments(); ++i) {
770 intersections.reset();
772 int pathId = m_segments.pathId(i);
774 const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
776 intersections << *isect;
779 isect += isect->next;
785 std::sort(intersections.data(), intersections.data() + intersections.size());
787 int first = m_segments.segmentAt(i).va;
788 int second = m_segments.segmentAt(i).vb;
791 for (
int j = 0; j < intersections.size(); ++j) {
792 const QPathSegments::Intersection &isect = intersections.at(j);
794 QPathEdge *ep = edge(addEdge(last, isect.vertex));
797 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
807 QPathEdge *ep = edge(addEdge(last, second));
810 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
819QWingedEdge::QWingedEdge() :
826QWingedEdge::QWingedEdge(
const QPainterPath &subject,
const QPainterPath &clip) :
827 m_edges(subject.elementCount()),
828 m_vertices(subject.elementCount()),
829 m_segments(subject.elementCount())
831 m_segments.setPath(subject);
832 m_segments.addPath(clip);
837QWingedEdge::TraversalStatus QWingedEdge::next(
const QWingedEdge::TraversalStatus &status)
const
839 const QPathEdge *sp = edge(status.edge);
842 TraversalStatus result;
843 result.edge = sp->next(status.traversal, status.direction);
844 result.traversal = status.traversal;
845 result.direction = status.direction;
847 const QPathEdge *rp = edge(result.edge);
850 if (sp->vertex(status.direction) == rp->vertex(status.direction))
858 const bool equal_1_2 = comparePoints(bezier.pt1(), bezier.pt2());
859 const bool equal_2_3 = comparePoints(bezier.pt2(), bezier.pt3());
860 const bool equal_3_4 = comparePoints(bezier.pt3(), bezier.pt4());
863 if (equal_1_2 && equal_2_3 && equal_3_4)
866 if (comparePoints(bezier.pt1(), bezier.pt4()))
867 return equal_1_2 || equal_3_4;
869 return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
875 m_intersections.reset();
885 int firstSegment = m_segments.size();
887 bool hasMoveTo =
false;
890 for (
int i = 0; i < path.elementCount(); ++i) {
891 int current = m_points.size();
893 QPointF currentPoint;
894 if (path.elementAt(i).type == QPainterPath::CurveToElement)
895 currentPoint = path.elementAt(i+2);
897 currentPoint = path.elementAt(i);
899 if (i > 0 && comparePoints(m_points.at(lastMoveTo), currentPoint))
900 current = lastMoveTo;
902 m_points << currentPoint;
904 switch (path.elementAt(i).type) {
905 case QPainterPath::MoveToElement:
906 if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
907 m_segments << Segment(m_pathId, last, lastMoveTo);
909 last = lastMoveTo = current;
911 case QPainterPath::LineToElement:
912 m_segments << Segment(m_pathId, last, current);
915 case QPainterPath::CurveToElement:
917 QBezier bezier = QBezier::fromPoints(m_points.at(last), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2));
918 if (isLine(bezier)) {
919 m_segments << Segment(m_pathId, last, current);
921 QRectF bounds = bezier.bounds();
924 int threshold = qMin<
float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6));
926 if (threshold < 3) threshold = 3;
927 qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
929 for (
int t = 1; t < threshold - 1; ++t) {
930 currentPoint = bezier.pointAt(t * one_over_threshold_minus_1);
932 int index = m_points.size();
933 m_segments << Segment(m_pathId, last, index);
936 m_points << currentPoint;
939 m_segments << Segment(m_pathId, last, current);
951 if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
952 m_segments << Segment(m_pathId, last, lastMoveTo);
954 for (
int i = firstSegment; i < m_segments.size(); ++i) {
955 const QLineF line = lineAt(i);
957 qreal x1 = line.p1().x();
958 qreal y1 = line.p1().y();
959 qreal x2 = line.p2().x();
960 qreal y2 = line.p2().y();
967 m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
973qreal QWingedEdge::delta(
int vertex,
int a,
int b)
const
975 const QPathEdge *ap = edge(a);
976 const QPathEdge *bp = edge(b);
978 double a_angle = ap->angle;
979 double b_angle = bp->angle;
981 if (vertex == ap->second)
982 a_angle = ap->invAngle;
984 if (vertex == bp->second)
985 b_angle = bp->invAngle;
987 double result = b_angle - a_angle;
990 return result - 128.;
992 return result + 128.;
997QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(
int vi,
int ei)
const
999 const QPathVertex *vp = vertex(vi);
1003 Q_ASSERT(vp->edge >= 0);
1005 int position = vp->edge;
1008 TraversalStatus status;
1009 status.direction = edge(vp->edge)->directionTo(vi);
1010 status.traversal = QPathEdge::RightTraversal;
1011 status.edge = vp->edge;
1013#ifdef QDEBUG_CLIPPER
1014 const QPathEdge *ep = edge(ei);
1015 qDebug() <<
"Finding insert status for edge" << ei <<
"at vertex" << QPointF(*vp) <<
", angles: " << ep->angle << ep->invAngle;
1019 status = next(status);
1022 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1023 qreal d2 = delta(vi, ei, status.edge);
1025#ifdef QDEBUG_CLIPPER
1026 const QPathEdge *op = edge(status.edge);
1027 qDebug() <<
"Delta to edge" << status.edge << d2 <<
", angles: " << op->angle << op->invAngle;
1031 position = status.edge;
1034 }
while (status.edge != vp->edge);
1036 status.traversal = QPathEdge::LeftTraversal;
1037 status.direction = QPathEdge::Forward;
1038 status.edge = position;
1040 if (edge(status.edge)->vertex(status.direction) != vi)
1043#ifdef QDEBUG_CLIPPER
1044 qDebug() <<
"Inserting edge" << ei <<
"to" << (status.traversal == QPathEdge::LeftTraversal ?
"left" :
"right") <<
"of edge" << status.edge;
1047 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1052void QWingedEdge::removeEdge(
int ei)
1054 QPathEdge *ep = edge(ei);
1056 TraversalStatus status;
1057 status.direction = QPathEdge::Forward;
1058 status.traversal = QPathEdge::RightTraversal;
1061 TraversalStatus forwardRight = next(status);
1062 forwardRight.flipDirection();
1064 status.traversal = QPathEdge::LeftTraversal;
1065 TraversalStatus forwardLeft = next(status);
1066 forwardLeft.flipDirection();
1068 status.direction = QPathEdge::Backward;
1069 TraversalStatus backwardLeft = next(status);
1070 backwardLeft.flipDirection();
1072 status.traversal = QPathEdge::RightTraversal;
1073 TraversalStatus backwardRight = next(status);
1074 backwardRight.flipDirection();
1076 edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
1077 edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
1079 edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
1080 edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
1082 ep->setNext(QPathEdge::Forward, ei);
1083 ep->setNext(QPathEdge::Backward, ei);
1085 QPathVertex *a = vertex(ep->first);
1086 QPathVertex *b = vertex(ep->second);
1088 a->edge = backwardRight.edge;
1089 b->edge = forwardRight.edge;
1103 QWingedEdge::TraversalStatus status;
1105 status.direction = list.edge(status.edge)->directionTo(a);
1109 const QPathEdge *ep = list.edge(status.edge);
1115 status = list.next(status);
1117 }
while (status.edge != ap
->edge);
1126 return v.y() <= 0 ? 0 : 64.;
1127 }
else if (v.y() == 0) {
1128 return v.x() <= 0 ? 32. : 96.;
1138 return 128. - 32. * vx;
1141 return 64. + 32. * vx;
1145 return qAtan2(v.x(), v.y()) + Q_PI;
1149int QWingedEdge::addEdge(
const QPointF &a,
const QPointF &b)
1154 return addEdge(fi, si);
1157int QWingedEdge::addEdge(
int fi,
int si)
1162 int common = commonEdge(*
this, fi, si);
1166 m_edges << QPathEdge(fi, si);
1168 int ei = m_edges.size() - 1;
1170 QPathVertex *fp = vertex(fi);
1171 QPathVertex *sp = vertex(si);
1173 QPathEdge *ep = edge(ei);
1175 const QPointF tangent = QPointF(*sp) - QPointF(*fp);
1176 ep->angle = computeAngle(tangent);
1177 ep->invAngle = ep->angle + 64;
1178 if (ep->invAngle >= 128)
1179 ep->invAngle -= 128;
1181 QPathVertex *vertices[2] = { fp, sp };
1182 QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
1184#ifdef QDEBUG_CLIPPER
1185 printf(
"** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
1188 for (
int i = 0; i < 2; ++i) {
1189 QPathVertex *vp = vertices[i];
1192 ep->setNext(dirs[i], ei);
1194 int vi = ep->vertex(dirs[i]);
1195 Q_ASSERT(vertex(vi) == vertices[i]);
1197 TraversalStatus os = findInsertStatus(vi, ei);
1198 QPathEdge *op = edge(os.edge);
1200 Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
1202 TraversalStatus ns = next(os);
1204 QPathEdge *np = edge(ns.edge);
1206 op->setNext(os.traversal, os.direction, ei);
1207 np->setNext(ns.traversal, ns.direction, ei);
1218 Q_ASSERT(os.edge == ei);
1219 Q_ASSERT(ns.edge == ei);
1221 ep->setNext(os.traversal, os.direction, oe);
1222 ep->setNext(ns.traversal, ns.direction, ne);
1226 Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Forward) >= 0);
1227 Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Backward) >= 0);
1228 Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Forward) >= 0);
1229 Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Backward) >= 0);
1234int QWingedEdge::insert(
const QPathVertex &vertex)
1236 if (!m_vertices.isEmpty()) {
1237 const QPathVertex &last = m_vertices.last();
1238 if (vertex.x == last.x && vertex.y == last.y)
1239 return m_vertices.size() - 1;
1241 for (
int i = 0; i < m_vertices.size(); ++i) {
1242 const QPathVertex &v = m_vertices.at(i);
1243 if (qFuzzyCompare(v.x, vertex.x) && qFuzzyCompare(v.y, vertex.y)) {
1249 m_vertices << vertex;
1250 return m_vertices.size() - 1;
1253static void addLineTo(QPainterPath &path,
const QPointF &point)
1255 const int elementCount = path.elementCount();
1256 if (elementCount >= 2) {
1257 const QPainterPath::Element &middle = path.elementAt(elementCount - 1);
1258 if (middle.type == QPainterPath::LineToElement) {
1259 const QPointF first = path.elementAt(elementCount - 2);
1260 const QPointF d1 = point - first;
1261 const QPointF d2 = middle - first;
1263 const QPointF p(-d1.y(), d1.x());
1265 if (qFuzzyIsNull(dot(p, d2))) {
1266 path.setElementPositionAt(elementCount - 1, point.x(), point.y());
1277 QWingedEdge::TraversalStatus status;
1279 status.traversal = traversal;
1282 path.moveTo(*list.vertex(list.edge(edge)->first));
1285 const QPathEdge *ep = list.edge(status.edge);
1287 addLineTo(path, *list.vertex(ep
->vertex(status.direction
)));
1294 status = list.next(status);
1295 }
while (status.edge != edge);
1298void QWingedEdge::simplify()
1300 for (
int i = 0; i < edgeCount(); ++i) {
1301 const QPathEdge *ep = edge(i);
1304 int flag = 0x3 << 4;
1305 if ((ep->flag & flag) == flag) {
1313QPainterPath QWingedEdge::toPath()
const
1317 for (
int i = 0; i < edgeCount(); ++i) {
1318 const QPathEdge *ep = edge(i);
1320 if (ep->flag & 16) {
1321 add(path, *
this, i, QPathEdge::LeftTraversal);
1325 add(path, *
this, i, QPathEdge::RightTraversal);
1331bool QPathClipper::intersect()
1333 if (subjectPath == clipPath)
1336 QRectF r1 = subjectPath.controlPointRect();
1337 QRectF r2 = clipPath.controlPointRect();
1338 if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1339 qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1344 bool subjectIsRect = pathToRect(subjectPath);
1345 bool clipIsRect = pathToRect(clipPath);
1347 if (subjectIsRect && clipIsRect)
1349 else if (subjectIsRect)
1350 return clipPath.intersects(r1);
1351 else if (clipIsRect)
1352 return subjectPath.intersects(r2);
1354 QPathSegments a(subjectPath.elementCount());
1355 a.setPath(subjectPath);
1356 QPathSegments b(clipPath.elementCount());
1357 b.setPath(clipPath);
1359 QIntersectionFinder finder;
1360 if (finder.hasIntersections(a, b))
1363 for (
int i = 0; i < clipPath.elementCount(); ++i) {
1364 if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1365 const QPointF point = clipPath.elementAt(i);
1366 if (r1.contains(point) && subjectPath.contains(point))
1371 for (
int i = 0; i < subjectPath.elementCount(); ++i) {
1372 if (subjectPath.elementAt(i).type == QPainterPath::MoveToElement) {
1373 const QPointF point = subjectPath.elementAt(i);
1374 if (r2.contains(point) && clipPath.contains(point))
1382bool QPathClipper::contains()
1384 if (subjectPath == clipPath)
1387 QRectF r1 = subjectPath.controlPointRect();
1388 QRectF r2 = clipPath.controlPointRect();
1389 if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1390 qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1395 bool clipIsRect = pathToRect(clipPath);
1397 return subjectPath.contains(r2);
1399 QPathSegments a(subjectPath.elementCount());
1400 a.setPath(subjectPath);
1401 QPathSegments b(clipPath.elementCount());
1402 b.setPath(clipPath);
1404 QIntersectionFinder finder;
1405 if (finder.hasIntersections(a, b))
1408 for (
int i = 0; i < clipPath.elementCount(); ++i) {
1409 if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1410 const QPointF point = clipPath.elementAt(i);
1411 if (!r1.contains(point) || !subjectPath.contains(point))
1419QPathClipper::QPathClipper(
const QPainterPath &subject,
1420 const QPainterPath &clip)
1421 : subjectPath(subject)
1424 aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1425 bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1430 QWingedEdge::TraversalStatus status;
1432 status.traversal = traversal;
1437 list.edge(status.edge)->flag |= 1;
1439 list.edge(status.edge)->flag |= 2;
1441 status = list.next(status);
1442 }
while (status.edge != edge);
1445template <
typename InputIterator>
1446InputIterator
qFuzzyFind(InputIterator first, InputIterator last, qreal val)
1448 while (first != last && !QT_PREPEND_NAMESPACE(qFuzzyCompare)(qreal(*first), qreal(val)))
1455 return qFuzzyCompare(a, b);
1458bool QPathClipper::pathToRect(
const QPainterPath &path, QRectF *rect)
1460 if (path.elementCount() != 5)
1463 const bool mightBeRect = path.elementAt(0).isMoveTo()
1464 && path.elementAt(1).isLineTo()
1465 && path.elementAt(2).isLineTo()
1466 && path.elementAt(3).isLineTo()
1467 && path.elementAt(4).isLineTo();
1472 const qreal x1 = path.elementAt(0).x;
1473 const qreal y1 = path.elementAt(0).y;
1475 const qreal x2 = path.elementAt(1).x;
1476 const qreal y2 = path.elementAt(2).y;
1478 if (path.elementAt(1).y != y1)
1481 if (path.elementAt(2).x != x2)
1484 if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
1487 if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
1491 rect->setCoords(x1, y1, x2, y2);
1497QPainterPath QPathClipper::clip(Operation operation)
1501 if (op != Simplify) {
1502 if (subjectPath == clipPath)
1503 return op == BoolSub ? QPainterPath() : subjectPath;
1505 bool subjectIsRect = pathToRect(subjectPath,
nullptr);
1506 bool clipIsRect = pathToRect(clipPath,
nullptr);
1508 const QRectF clipBounds = clipPath.boundingRect();
1509 const QRectF subjectBounds = subjectPath.boundingRect();
1511 if (!clipBounds.intersects(subjectBounds)) {
1516 return QPainterPath();
1518 QPainterPath result = subjectPath;
1519 if (result.fillRule() == clipPath.fillRule()) {
1520 result.addPath(clipPath);
1521 }
else if (result.fillRule() == Qt::WindingFill) {
1522 result = result.simplified();
1523 result.addPath(clipPath);
1525 result.addPath(clipPath.simplified());
1534 if (clipBounds.contains(subjectBounds)) {
1538 return QPainterPath();
1547 }
else if (subjectBounds.contains(clipBounds)) {
1548 if (subjectIsRect) {
1551 if (clipPath.fillRule() == Qt::OddEvenFill) {
1552 QPainterPath result = clipPath;
1553 result.addRect(subjectBounds);
1556 QPainterPath result = clipPath.simplified();
1557 result.addRect(subjectBounds);
1570 if (op == BoolAnd) {
1572 return intersect(clipPath, subjectBounds);
1573 else if (clipIsRect)
1574 return intersect(subjectPath, clipBounds);
1578 QWingedEdge list(subjectPath, clipPath);
1580 doClip(list, ClipMode);
1582 QPainterPath path = list.toPath();
1586bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
1588 QList<qreal> y_coords;
1589 y_coords.reserve(list.vertexCount());
1590 for (
int i = 0; i < list.vertexCount(); ++i)
1591 y_coords << list.vertex(i)->y;
1593 std::sort(y_coords.begin(), y_coords.end());
1594 y_coords.erase(std::unique(y_coords.begin(), y_coords.end(), fuzzyCompare), y_coords.end());
1596#ifdef QDEBUG_CLIPPER
1597 printf(
"sorted y coords:\n");
1598 for (
int i = 0; i < y_coords.size(); ++i) {
1599 printf(
"%.9f\n", y_coords.at(i));
1607 qreal maxHeight = 0;
1608 for (
int i = 0; i < list.edgeCount(); ++i) {
1609 QPathEdge *edge = list.edge(i);
1612 if ((edge->flag & 0x3) == 0x3)
1615 QPathVertex *a = list.vertex(edge->first);
1616 QPathVertex *b = list.vertex(edge->second);
1618 if (qFuzzyCompare(a->y, b->y))
1623 qreal height = qAbs(a->y - b->y);
1624 if (height > maxHeight) {
1631 QPathEdge *edge = list.edge(index);
1633 QPathVertex *a = list.vertex(edge->first);
1634 QPathVertex *b = list.vertex(edge->second);
1637 const int first = qFuzzyFind(y_coords.cbegin(), y_coords.cend(), qMin(a->y, b->y)) - y_coords.cbegin();
1638 const int last = qFuzzyFind(y_coords.cbegin() + first, y_coords.cend(), qMax(a->y, b->y)) - y_coords.cbegin();
1640 Q_ASSERT(first < y_coords.size() - 1);
1641 Q_ASSERT(last < y_coords.size());
1643 qreal biggestGap = y_coords.at(first + 1) - y_coords.at(first);
1644 int bestIdx = first;
1645 for (
int i = first + 1; i < last; ++i) {
1646 qreal gap = y_coords.at(i + 1) - y_coords.at(i);
1648 if (gap > biggestGap) {
1653 const qreal bestY = 0.5 * (y_coords.at(bestIdx) + y_coords.at(bestIdx + 1));
1655#ifdef QDEBUG_CLIPPER
1656 printf(
"y: %.9f, gap: %.9f\n", bestY, biggestGap);
1659 if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
1666 if (mode == ClipMode)
1674 QWingedEdge::TraversalStatus status;
1676 status.traversal = traversal;
1684 ep
->flag |= (flag | (flag << 4));
1686#ifdef QDEBUG_CLIPPER
1687 qDebug() <<
"traverse: adding edge " << status.edge <<
", mask:" << (flag << 4) <<ep->flag;
1690 status = list.next(status);
1691 }
while (status.edge != edge);
1706static bool bool_op(
bool a,
bool b, QPathClipper::Operation op)
1709 case QPathClipper::BoolAnd:
1711 case QPathClipper::BoolOr:
1712 case QPathClipper::Simplify:
1714 case QPathClipper::BoolSub:
1722bool QWingedEdge::isInside(qreal x, qreal y)
const
1725 for (
int i = 0; i < edgeCount(); ++i) {
1726 const QPathEdge *ep = edge(i);
1729 int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
1734 QPointF a = *vertex(ep->first);
1735 QPointF b = *vertex(ep->second);
1737 if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1738 qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1740 if (intersectionX > x)
1750 QList<QCrossingEdge> crossings;
1751 for (
int i = 0; i < list.edgeCount(); ++i) {
1753 QPointF a = *list.vertex(edge
->first);
1754 QPointF b = *list.vertex(edge
->second);
1756 if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1757 const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1765bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
1767 QList<QCrossingEdge> crossings = findCrossings(list, y);
1769 Q_ASSERT(!crossings.isEmpty());
1770 std::sort(crossings.begin(), crossings.end());
1777#ifdef QDEBUG_CLIPPER
1778 qDebug() <<
"crossings:" << crossings.size();
1780 for (
int i = 0; i < crossings.size() - 1; ++i) {
1781 int ei = crossings.at(i).edge;
1782 const QPathEdge *edge = list.edge(ei);
1784 windingA += edge->windingA;
1785 windingB += edge->windingB;
1787 const bool hasLeft = (edge->flag >> 4) & 1;
1788 const bool hasRight = (edge->flag >> 4) & 2;
1790 windingD += hasLeft ^ hasRight;
1792 const bool inA = (windingA & aMask) != 0;
1793 const bool inB = (windingB & bMask) != 0;
1794 const bool inD = (windingD & 0x1) != 0;
1796 const bool inside = bool_op(inA, inB, op);
1797 const bool add = inD ^ inside;
1799#ifdef QDEBUG_CLIPPER
1800 printf(
"y %f, x %f, inA: %d, inB: %d, inD: %d, inside: %d, flag: %x, bezier: %p, edge: %d\n", y, crossings.at(i).x, inA, inB, inD, inside, edge->flag, edge->bezier, ei);
1804 if (mode == CheckMode)
1807 qreal y0 = list.vertex(edge->first)->y;
1808 qreal y1 = list.vertex(edge->second)->y;
1811 if (!(edge->flag & 1))
1812 traverse(list, ei, QPathEdge::LeftTraversal);
1814 if (!(edge->flag & 2))
1815 clear(list, ei, QPathEdge::RightTraversal);
1817 if (!(edge->flag & 1))
1818 clear(list, ei, QPathEdge::LeftTraversal);
1820 if (!(edge->flag & 2))
1821 traverse(list, ei, QPathEdge::RightTraversal);
1826 if (!(edge->flag & 1))
1827 clear(list, ei, QPathEdge::LeftTraversal);
1829 if (!(edge->flag & 2))
1830 clear(list, ei, QPathEdge::RightTraversal);
1839QList<QPainterPath> toSubpaths(
const QPainterPath &path)
1842 QList<QPainterPath> subpaths;
1846 QPainterPath current;
1847 for (
int i = 0; i < path.elementCount(); ++i) {
1848 const QPainterPath::Element &e = path.elementAt(i);
1850 case QPainterPath::MoveToElement:
1851 if (current.elementCount() > 1)
1852 subpaths += current;
1853 current = QPainterPath();
1856 case QPainterPath::LineToElement:
1859 case QPainterPath::CurveToElement: {
1860 current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2));
1864 case QPainterPath::CurveToDataElement:
1865 Q_ASSERT(!
"toSubpaths(), bad element type");
1870 if (current.elementCount() > 1)
1871 subpaths << current;
1878 Left, Top, Right, Bottom
1881static bool isVertical(Edge edge)
1883 return edge == Left || edge == Right;
1887bool compare(
const QPointF &p, qreal t)
1903QPointF intersectLine(
const QPointF &a,
const QPointF &b, qreal t)
1909 return line.pointAt((t - a.x()) / (b.x() - a.x()));
1911 return line.pointAt((t - a.y()) / (b.y() - a.y()));
1915void addLine(QPainterPath &path,
const QLineF &line)
1917 if (path.elementCount() > 0)
1918 path.lineTo(line.p1());
1920 path.moveTo(line.p1());
1922 path.lineTo(line.p2());
1926void clipLine(
const QPointF &a,
const QPointF &b, qreal t, QPainterPath &result)
1928 bool outA = compare<edge>(a, t);
1929 bool outB = compare<edge>(b, t);
1934 addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1936 addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1938 addLine(result, QLineF(a, b));
1941void addBezier(QPainterPath &path,
const QBezier &bezier)
1943 if (path.elementCount() > 0)
1944 path.lineTo(bezier.pt1());
1946 path.moveTo(bezier.pt1());
1948 path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4());
1952void clipBezier(
const QPointF &a,
const QPointF &b,
const QPointF &c,
const QPointF &d, qreal t, QPainterPath &result)
1954 QBezier bezier = QBezier::fromPoints(a, b, c, d);
1956 bool outA = compare<edge>(a, t);
1957 bool outB = compare<edge>(b, t);
1958 bool outC = compare<edge>(c, t);
1959 bool outD = compare<edge>(d, t);
1961 int outCount =
int(outA) +
int(outB) +
int(outC) +
int(outD);
1966 if (outCount == 0) {
1967 addBezier(result, bezier);
1971 QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform();
1972 QBezier unflipped = bezier;
1973 QBezier flipped = bezier.mapBy(flip);
1975 qreal t0 = 0, t1 = 1;
1976 int stationary = flipped.stationaryYPoints(t0, t1);
1980 points[0] = unflipped.pt1();
1983 int segmentCount = 0;
1984 if (stationary > 0) {
1986 segments[segmentCount] = t0;
1987 points[segmentCount] = unflipped.pointAt(t0);
1989 if (stationary > 1) {
1991 segments[segmentCount] = t1;
1992 points[segmentCount] = unflipped.pointAt(t1);
1995 segments[segmentCount] = 1;
1996 points[segmentCount] = unflipped.pt4();
1998 qreal lastIntersection = 0;
1999 for (
int i = 0; i < segmentCount; ++i) {
2000 outA = compare<edge>(points[i], t);
2001 outB = compare<edge>(points[i+1], t);
2004 qreal intersection = flipped.tForY(segments[i], segments[i+1], t);
2007 addBezier(result, unflipped.getSubRange(lastIntersection, intersection));
2009 lastIntersection = intersection;
2014 addBezier(result, unflipped.getSubRange(lastIntersection, 1));
2019QPainterPath clip(
const QPainterPath &path, qreal t)
2021 QPainterPath result;
2022 for (
int i = 1; i < path.elementCount(); ++i) {
2023 const QPainterPath::Element &element = path.elementAt(i);
2024 Q_ASSERT(!element.isMoveTo());
2025 if (element.isLineTo()) {
2026 clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2028 clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2033 int last = path.elementCount() - 1;
2034 if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2035 clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2040QPainterPath intersectPath(
const QPainterPath &path,
const QRectF &rect)
2042 QList<QPainterPath> subpaths = toSubpaths(path);
2044 QPainterPath result;
2045 result.setFillRule(path.fillRule());
2046 for (
int i = 0; i < subpaths.size(); ++i) {
2047 QPainterPath subPath = subpaths.at(i);
2048 QRectF bounds = subPath.boundingRect();
2049 if (bounds.intersects(rect)) {
2050 if (bounds.left() < rect.left())
2051 subPath = clip<Left>(subPath, rect.left());
2052 if (bounds.right() > rect.right())
2053 subPath = clip<Right>(subPath, rect.right());
2055 bounds = subPath.boundingRect();
2057 if (bounds.top() < rect.top())
2058 subPath = clip<Top>(subPath, rect.top());
2059 if (bounds.bottom() > rect.bottom())
2060 subPath = clip<Bottom>(subPath, rect.bottom());
2062 if (subPath.elementCount() > 1)
2063 result.addPath(subPath);
2068 if (result.boundingRect().isEmpty())
2069 return QPainterPath();
2076QPainterPath QPathClipper::intersect(
const QPainterPath &path,
const QRectF &rect)
2078 return intersectPath(path, rect);
bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const
void produceIntersections(QPathSegments &segments)
QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
QKdPointTree(const QPathSegments &segments)
int build(int begin, int end, int depth=0)
int vertex(Direction direction) const
void setPath(const QPainterPath &path)
void addPath(const QPainterPath &path)
void addIntersection(int index, const Intersection &intersection)
Combined button and popup list for selecting options.
Q_DECLARE_TYPEINFO(QCrossingEdge, Q_PRIMITIVE_TYPE)
static qreal dot(const QPointF &a, const QPointF &b)
Q_DECLARE_TYPEINFO(QIntersection, Q_PRIMITIVE_TYPE)
static QList< QCrossingEdge > findCrossings(const QWingedEdge &list, qreal y)
static bool bool_op(bool a, bool b, QPathClipper::Operation op)
static void normalize(double &x, double &y)
static QT_BEGIN_NAMESPACE bool fuzzyIsNull(qreal d)
static bool fuzzyCompare(qreal a, qreal b)
static bool comparePoints(const QPointF &a, const QPointF &b)
static void clear(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
void qTraverseKdPointTree(QKdPointTree::Node &node, T &t, int depth=0)
static qreal component(const QPointF &point, unsigned int i)
static void addLineTo(QPainterPath &path, const QPointF &point)
static int commonEdge(const QWingedEdge &list, int a, int b)
static bool isLine(const QBezier &bezier)
InputIterator qFuzzyFind(InputIterator first, InputIterator last, qreal val)
static double computeAngle(const QPointF &v)
static void traverse(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
bool operator<(const QCrossingEdge &edge) const