6#include <private/qbezier_p.h>
7#include <private/qdatabuffer_p.h>
8#include <private/qnumeric_p.h>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
34 if (
sizeof(qreal) ==
sizeof(
double))
35 return qAbs(d) <= 1e-12;
37 return qAbs(d) <= 1e-5f;
42 return fuzzyIsNull(a.x() - b.x())
43 && fuzzyIsNull(a.y() - b.y());
47static qreal dot(
const QPointF &a,
const QPointF &b)
49 return a.x() * b.x() + a.y() * b.y();
54 double reciprocal = 1 / qSqrt(x * x + y * y);
75 bool linesIntersect(
const QLineF &a,
const QLineF &b)
const;
80 const QPointF p1 = a.p1();
81 const QPointF p2 = a.p2();
83 const QPointF q1 = b.p1();
84 const QPointF q2 = b.p2();
86 if (comparePoints(p1, p2) || comparePoints(q1, q2))
89 const bool p1_equals_q1 = comparePoints(p1, q1);
90 const bool p2_equals_q2 = comparePoints(p2, q2);
92 if (p1_equals_q1 && p2_equals_q2)
95 const bool p1_equals_q2 = comparePoints(p1, q2);
96 const bool p2_equals_q1 = comparePoints(p2, q1);
98 if (p1_equals_q2 && p2_equals_q1)
101 const QPointF pDelta = p2 - p1;
102 const QPointF qDelta = q2 - q1;
104 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
106 if (qFuzzyIsNull(par)) {
107 const QPointF normal(-pDelta.y(), pDelta.x());
110 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
111 const qreal dp = dot(pDelta, pDelta);
113 const qreal tq1 = dot(pDelta, q1 - p1);
114 const qreal tq2 = dot(pDelta, q2 - p1);
116 if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
119 const qreal dq = dot(qDelta, qDelta);
121 const qreal tp1 = dot(qDelta, p1 - q1);
122 const qreal tp2 = dot(qDelta, p2 - q1);
124 if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
131 const qreal invPar = 1 / par;
133 const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
134 qDelta.x() * (q1.y() - p1.y())) * invPar;
136 if (tp < 0 || tp > 1)
139 const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
140 pDelta.x() * (q1.y() - p1.y())) * invPar;
142 return tq >= 0 && tq <= 1;
150 const QRectF &rb0 = b.elementBounds(0);
152 qreal minX = rb0.left();
153 qreal minY = rb0.top();
154 qreal maxX = rb0.right();
155 qreal maxY = rb0.bottom();
158 const QRectF &r = b.elementBounds(i);
159 minX = qMin(minX, r.left());
160 minY = qMin(minY, r.top());
161 maxX = qMax(maxX, r.right());
162 maxY = qMax(maxY, r.bottom());
165 QRectF rb(minX, minY, maxX - minX, maxY - minY);
168 const QRectF &r1 = a.elementBounds(i);
170 if (r1.left() > rb.right() || rb.left() > r1.right())
172 if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
176 const QRectF &r2 = b.elementBounds(j);
178 if (r1.left() > r2.right() || r2.left() > r1.right())
180 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
183 if (linesIntersect(a.lineAt(i), b.lineAt(j)))
199 int lowestRightIndex;
226 void produceIntersections(
int segment);
229 TreeNode buildTree(
int first,
int last,
int depth,
const RectF &bounds);
231 void produceIntersectionsLeaf(
const TreeNode &node,
int segment);
232 void produceIntersections(
const TreeNode &node,
int segment,
const RectF &segmentBounds,
const RectF &nodeBounds,
int axis);
233 void intersectLines(
const QLineF &a,
const QLineF &b, QDataBuffer<QIntersection> &intersections);
240 QList<TreeNode> m_tree;
241 QDataBuffer<QIntersection> m_intersections;
245 : m_segments(segments),
248 m_bounds.x1 = qt_inf();
249 m_bounds.y1 = qt_inf();
250 m_bounds.x2 = -qt_inf();
251 m_bounds.y2 = -qt_inf();
253 m_index.resize(m_segments.segments());
255 for (
int i = 0; i < m_index.size(); ++i) {
258 const QRectF &segmentBounds = m_segments.elementBounds(i);
260 if (segmentBounds.left() < m_bounds.x1)
261 m_bounds.x1 = segmentBounds.left();
262 if (segmentBounds.top() < m_bounds.y1)
263 m_bounds.y1 = segmentBounds.top();
264 if (segmentBounds.right() > m_bounds.x2)
265 m_bounds.x2 = segmentBounds.right();
266 if (segmentBounds.bottom() > m_bounds.y2)
267 m_bounds.y2 = segmentBounds.bottom();
272 TreeNode root = buildTree(0, m_index.size(), 0, m_bounds);
276static inline qreal coordinate(
const QPointF &pos,
int axis)
278 return axis == 0 ? pos.x() : pos.y();
281TreeNode SegmentTree::buildTree(
int first,
int last,
int depth,
const RectF &bounds)
283 if (depth >= 24 || (last - first) <= 10) {
286 node.index.interval.first = first;
287 node.index.interval.last = last;
292 int splitAxis = (depth & 1);
297 qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]);
299 node.splitLeft = (&bounds.x1)[splitAxis];
300 node.splitRight = (&bounds.x2)[splitAxis];
302 node.lowestLeftIndex = INT_MAX;
303 node.lowestRightIndex = INT_MAX;
305 const int treeSize = m_tree.size();
307 node.index.children.left = treeSize;
308 node.index.children.right = treeSize + 1;
310 m_tree.resize(treeSize + 2);
317 const int index = m_index.at(l);
318 const QRectF &segmentBounds = m_segments.elementBounds(index);
320 qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis);
322 if (coordinate(segmentBounds.center(), splitAxis) < split) {
323 qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis);
324 if (highCoordinate > node.splitLeft)
325 node.splitLeft = highCoordinate;
326 if (index < node.lowestLeftIndex)
327 node.lowestLeftIndex = index;
330 if (lowCoordinate < node.splitRight)
331 node.splitRight = lowCoordinate;
332 if (index < node.lowestRightIndex)
333 node.lowestRightIndex = index;
334 qSwap(m_index[l], m_index[r]);
339 RectF lbounds = bounds;
340 (&lbounds.x2)[splitAxis] = node.splitLeft;
342 RectF rbounds = bounds;
343 (&rbounds.x1)[splitAxis] = node.splitRight;
345 TreeNode left = buildTree(first, l, depth + 1, lbounds);
346 m_tree[node.index.children.left] = left;
348 TreeNode right = buildTree(l, last, depth + 1, rbounds);
349 m_tree[node.index.children.right] = right;
354void SegmentTree::intersectLines(
const QLineF &a,
const QLineF &b, QDataBuffer<QIntersection> &intersections)
356 const QPointF p1 = a.p1();
357 const QPointF p2 = a.p2();
359 const QPointF q1 = b.p1();
360 const QPointF q2 = b.p2();
362 if (comparePoints(p1, p2) || comparePoints(q1, q2))
365 const bool p1_equals_q1 = comparePoints(p1, q1);
366 const bool p2_equals_q2 = comparePoints(p2, q2);
368 if (p1_equals_q1 && p2_equals_q2)
371 const bool p1_equals_q2 = comparePoints(p1, q2);
372 const bool p2_equals_q1 = comparePoints(p2, q1);
374 if (p1_equals_q2 && p2_equals_q1)
377 const QPointF pDelta = p2 - p1;
378 const QPointF qDelta = q2 - q1;
380 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
382 if (qFuzzyIsNull(par)) {
383 const QPointF normal(-pDelta.y(), pDelta.x());
386 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
387 const qreal invDp = 1 / dot(pDelta, pDelta);
389 const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
390 const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
392 if (tq1 > 0 && tq1 < 1) {
394 intersection.alphaA = tq1;
395 intersection.alphaB = 0;
396 intersection.pos = q1;
397 intersections.add(intersection);
400 if (tq2 > 0 && tq2 < 1) {
402 intersection.alphaA = tq2;
403 intersection.alphaB = 1;
404 intersection.pos = q2;
405 intersections.add(intersection);
408 const qreal invDq = 1 / dot(qDelta, qDelta);
410 const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
411 const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
413 if (tp1 > 0 && tp1 < 1) {
415 intersection.alphaA = 0;
416 intersection.alphaB = tp1;
417 intersection.pos = p1;
418 intersections.add(intersection);
421 if (tp2 > 0 && tp2 < 1) {
423 intersection.alphaA = 1;
424 intersection.alphaB = tp2;
425 intersection.pos = p2;
426 intersections.add(intersection);
435 if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
439 const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
440 qDelta.x() * (q1.y() - p1.y())) / par;
441 const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
442 pDelta.x() * (q1.y() - p1.y())) / par;
444 if (tp<0 || tp>1 || tq<0 || tq>1)
447 const bool p_zero = qFuzzyIsNull(tp);
448 const bool p_one = qFuzzyIsNull(tp - 1);
450 const bool q_zero = qFuzzyIsNull(tq);
451 const bool q_one = qFuzzyIsNull(tq - 1);
453 if ((q_zero || q_one) && (p_zero || p_one))
466 pt = q1 + (q2 - q1) * tq;
470 intersection.alphaA = tp;
471 intersection.alphaB = tq;
472 intersection.pos = pt;
473 intersections.add(intersection);
476void SegmentTree::produceIntersections(
int segment)
478 const QRectF &segmentBounds = m_segments.elementBounds(segment);
481 sbounds.x1 = segmentBounds.left();
482 sbounds.y1 = segmentBounds.top();
483 sbounds.x2 = segmentBounds.right();
484 sbounds.y2 = segmentBounds.bottom();
486 produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0);
489void SegmentTree::produceIntersectionsLeaf(
const TreeNode &node,
int segment)
491 const QRectF &r1 = m_segments.elementBounds(segment);
492 const QLineF lineA = m_segments.lineAt(segment);
494 for (
int i = node.index.interval.first; i < node.index.interval.last; ++i) {
495 const int other = m_index.at(i);
496 if (other >= segment)
499 const QRectF &r2 = m_segments.elementBounds(other);
501 if (r1.left() > r2.right() || r2.left() > r1.right())
503 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
506 m_intersections.reset();
508 const QLineF lineB = m_segments.lineAt(other);
510 intersectLines(lineA, lineB, m_intersections);
512 for (
int k = 0; k < m_intersections.size(); ++k) {
514 i_isect.t = m_intersections.at(k).alphaA;
515 j_isect.t = m_intersections.at(k).alphaB;
517 i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos);
528void SegmentTree::produceIntersections(
const TreeNode &node,
int segment,
const RectF &segmentBounds,
const RectF &nodeBounds,
int axis)
531 produceIntersectionsLeaf(node, segment);
535 RectF lbounds = nodeBounds;
536 (&lbounds.x2)[axis] = node.splitLeft;
538 RectF rbounds = nodeBounds;
539 (&rbounds.x1)[axis] = node.splitRight;
541 if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft)
542 produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis);
544 if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight)
545 produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis);
552 SegmentTree tree(segments);
555 tree.produceIntersections(i);
577 : m_segments(&segments)
581 m_nodes.resize(m_segments->points());
583 for (
int i = 0; i < m_nodes.size(); ++i) {
584 m_nodes.at(i).point = i;
585 m_nodes.at(i).id = -1;
588 m_rootNode = build(0, m_nodes.size());
591 int build(
int begin,
int end,
int depth = 0);
595 return &m_nodes.at(m_rootNode);
605 QDataBuffer<Node> m_nodes;
619 if (traverseLeft && node
.left)
620 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node
.left, t, depth + 1);
622 if (traverseRight && node
.right)
623 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node
.right, t, depth + 1);
629 const qreal components[] = { point.x(), point.y() };
630 return components[i];
635 Q_ASSERT(end > begin);
637 const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
639 int first = begin + 1;
642 while (first <= last) {
643 const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
648 qSwap(m_nodes.at(first), m_nodes.at(last));
654 qSwap(m_nodes.at(last), m_nodes.at(begin));
657 m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
659 m_nodes.at(last).left =
nullptr;
662 m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
664 m_nodes.at(last).right =
nullptr;
674 , m_segments(&segments)
677 pointComponents[0] = segments.pointAt(point).x();
678 pointComponents[1] = segments.pointAt(point).y();
686 const QPointF &nodePoint = m_segments->pointAt(node.point);
688 const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
690 const qreal pivot = pivotComponents[depth & 1];
691 const qreal value = pointComponents[depth & 1];
693 if (fuzzyIsNull(pivot - value)) {
694 const qreal pivot2 = pivotComponents[(depth + 1) & 1];
695 const qreal value2 = pointComponents[(depth + 1) & 1];
697 if (fuzzyIsNull(pivot2 - value2)) {
705 }
else if (value < pivot) {
718 qreal pointComponents[2];
730 QDataBuffer<QPointF> mergedPoints(points());
731 QDataBuffer<
int> pointIndices(points());
733 for (
int i = 0; i <
points(); ++i) {
739 if (finder.result() >= mergedPoints.size())
740 mergedPoints << m_points.at(i);
742 pointIndices << finder.result();
745 for (
int i = 0; i < m_segments.size(); ++i) {
746 m_segments.at(i).va = pointIndices.at(m_segments.at(i).va);
747 m_segments.at(i).vb = pointIndices.at(m_segments.at(i).vb);
750 for (
int i = 0; i < m_intersections.size(); ++i)
751 m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
753 m_points.swap(mergedPoints);
757void QWingedEdge::intersectAndAdd()
759 QIntersectionFinder finder;
760 finder.produceIntersections(m_segments);
762 m_segments.mergePoints();
764 for (
int i = 0; i < m_segments.points(); ++i)
765 addVertex(m_segments.pointAt(i));
767 QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments());
768 for (
int i = 0; i < m_segments.segments(); ++i) {
769 intersections.reset();
771 int pathId = m_segments.pathId(i);
773 const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
775 intersections << *isect;
778 isect += isect->next;
784 std::sort(intersections.data(), intersections.data() + intersections.size());
786 int first = m_segments.segmentAt(i).va;
787 int second = m_segments.segmentAt(i).vb;
790 for (
int j = 0; j < intersections.size(); ++j) {
791 const QPathSegments::Intersection &isect = intersections.at(j);
793 QPathEdge *ep = edge(addEdge(last, isect.vertex));
796 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
806 QPathEdge *ep = edge(addEdge(last, second));
809 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
818QWingedEdge::QWingedEdge() :
825QWingedEdge::QWingedEdge(
const QPainterPath &subject,
const QPainterPath &clip) :
826 m_edges(subject.elementCount()),
827 m_vertices(subject.elementCount()),
828 m_segments(subject.elementCount())
830 m_segments.setPath(subject);
831 m_segments.addPath(clip);
836QWingedEdge::TraversalStatus QWingedEdge::next(
const QWingedEdge::TraversalStatus &status)
const
838 const QPathEdge *sp = edge(status.edge);
841 TraversalStatus result;
842 result.edge = sp->next(status.traversal, status.direction);
843 result.traversal = status.traversal;
844 result.direction = status.direction;
846 const QPathEdge *rp = edge(result.edge);
849 if (sp->vertex(status.direction) == rp->vertex(status.direction))
857 const bool equal_1_2 = comparePoints(bezier.pt1(), bezier.pt2());
858 const bool equal_2_3 = comparePoints(bezier.pt2(), bezier.pt3());
859 const bool equal_3_4 = comparePoints(bezier.pt3(), bezier.pt4());
862 if (equal_1_2 && equal_2_3 && equal_3_4)
865 if (comparePoints(bezier.pt1(), bezier.pt4()))
866 return equal_1_2 || equal_3_4;
868 return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
874 m_intersections.reset();
884 int firstSegment = m_segments.size();
886 bool hasMoveTo =
false;
889 for (
int i = 0; i < path.elementCount(); ++i) {
890 int current = m_points.size();
892 QPointF currentPoint;
893 if (path.elementAt(i).type == QPainterPath::CurveToElement)
894 currentPoint = path.elementAt(i+2);
896 currentPoint = path.elementAt(i);
898 if (i > 0 && comparePoints(m_points.at(lastMoveTo), currentPoint))
899 current = lastMoveTo;
901 m_points << currentPoint;
903 switch (path.elementAt(i).type) {
904 case QPainterPath::MoveToElement:
905 if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
906 m_segments << Segment(m_pathId, last, lastMoveTo);
908 last = lastMoveTo = current;
910 case QPainterPath::LineToElement:
911 m_segments << Segment(m_pathId, last, current);
914 case QPainterPath::CurveToElement:
916 QBezier bezier = QBezier::fromPoints(m_points.at(last), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2));
917 if (isLine(bezier)) {
918 m_segments << Segment(m_pathId, last, current);
920 QRectF bounds = bezier.bounds();
923 int threshold = qMin<
float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6));
925 if (threshold < 3) threshold = 3;
926 qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
928 for (
int t = 1; t < threshold - 1; ++t) {
929 currentPoint = bezier.pointAt(t * one_over_threshold_minus_1);
931 int index = m_points.size();
932 m_segments << Segment(m_pathId, last, index);
935 m_points << currentPoint;
938 m_segments << Segment(m_pathId, last, current);
950 if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
951 m_segments << Segment(m_pathId, last, lastMoveTo);
953 for (
int i = firstSegment; i < m_segments.size(); ++i) {
954 const QLineF line = lineAt(i);
956 qreal x1 = line.p1().x();
957 qreal y1 = line.p1().y();
958 qreal x2 = line.p2().x();
959 qreal y2 = line.p2().y();
966 m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
972qreal QWingedEdge::delta(
int vertex,
int a,
int b)
const
974 const QPathEdge *ap = edge(a);
975 const QPathEdge *bp = edge(b);
977 double a_angle = ap->angle;
978 double b_angle = bp->angle;
980 if (vertex == ap->second)
981 a_angle = ap->invAngle;
983 if (vertex == bp->second)
984 b_angle = bp->invAngle;
986 double result = b_angle - a_angle;
989 return result - 128.;
991 return result + 128.;
996QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(
int vi,
int ei)
const
998 const QPathVertex *vp = vertex(vi);
1002 Q_ASSERT(vp->edge >= 0);
1004 int position = vp->edge;
1007 TraversalStatus status;
1008 status.direction = edge(vp->edge)->directionTo(vi);
1009 status.traversal = QPathEdge::RightTraversal;
1010 status.edge = vp->edge;
1012#ifdef QDEBUG_CLIPPER
1013 const QPathEdge *ep = edge(ei);
1014 qDebug() <<
"Finding insert status for edge" << ei <<
"at vertex" << QPointF(*vp) <<
", angles: " << ep->angle << ep->invAngle;
1018 status = next(status);
1021 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1022 qreal d2 = delta(vi, ei, status.edge);
1024#ifdef QDEBUG_CLIPPER
1025 const QPathEdge *op = edge(status.edge);
1026 qDebug() <<
"Delta to edge" << status.edge << d2 <<
", angles: " << op->angle << op->invAngle;
1030 position = status.edge;
1033 }
while (status.edge != vp->edge);
1035 status.traversal = QPathEdge::LeftTraversal;
1036 status.direction = QPathEdge::Forward;
1037 status.edge = position;
1039 if (edge(status.edge)->vertex(status.direction) != vi)
1042#ifdef QDEBUG_CLIPPER
1043 qDebug() <<
"Inserting edge" << ei <<
"to" << (status.traversal == QPathEdge::LeftTraversal ?
"left" :
"right") <<
"of edge" << status.edge;
1046 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1051void QWingedEdge::removeEdge(
int ei)
1053 QPathEdge *ep = edge(ei);
1055 TraversalStatus status;
1056 status.direction = QPathEdge::Forward;
1057 status.traversal = QPathEdge::RightTraversal;
1060 TraversalStatus forwardRight = next(status);
1061 forwardRight.flipDirection();
1063 status.traversal = QPathEdge::LeftTraversal;
1064 TraversalStatus forwardLeft = next(status);
1065 forwardLeft.flipDirection();
1067 status.direction = QPathEdge::Backward;
1068 TraversalStatus backwardLeft = next(status);
1069 backwardLeft.flipDirection();
1071 status.traversal = QPathEdge::RightTraversal;
1072 TraversalStatus backwardRight = next(status);
1073 backwardRight.flipDirection();
1075 edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
1076 edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
1078 edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
1079 edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
1081 ep->setNext(QPathEdge::Forward, ei);
1082 ep->setNext(QPathEdge::Backward, ei);
1084 QPathVertex *a = vertex(ep->first);
1085 QPathVertex *b = vertex(ep->second);
1087 a->edge = backwardRight.edge;
1088 b->edge = forwardRight.edge;
1102 QWingedEdge::TraversalStatus status;
1104 status.direction = list.edge(status.edge)->directionTo(a);
1108 const QPathEdge *ep = list.edge(status.edge);
1114 status = list.next(status);
1116 }
while (status.edge != ap
->edge);
1125 return v.y() <= 0 ? 0 : 64.;
1126 }
else if (v.y() == 0) {
1127 return v.x() <= 0 ? 32. : 96.;
1137 return 128. - 32. * vx;
1140 return 64. + 32. * vx;
1144 return qAtan2(v.x(), v.y()) + Q_PI;
1148int QWingedEdge::addEdge(
const QPointF &a,
const QPointF &b)
1153 return addEdge(fi, si);
1156int QWingedEdge::addEdge(
int fi,
int si)
1161 int common = commonEdge(*
this, fi, si);
1165 m_edges << QPathEdge(fi, si);
1167 int ei = m_edges.size() - 1;
1169 QPathVertex *fp = vertex(fi);
1170 QPathVertex *sp = vertex(si);
1172 QPathEdge *ep = edge(ei);
1174 const QPointF tangent = QPointF(*sp) - QPointF(*fp);
1175 ep->angle = computeAngle(tangent);
1176 ep->invAngle = ep->angle + 64;
1177 if (ep->invAngle >= 128)
1178 ep->invAngle -= 128;
1180 QPathVertex *vertices[2] = { fp, sp };
1181 QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
1183#ifdef QDEBUG_CLIPPER
1184 printf(
"** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
1187 for (
int i = 0; i < 2; ++i) {
1188 QPathVertex *vp = vertices[i];
1191 ep->setNext(dirs[i], ei);
1193 int vi = ep->vertex(dirs[i]);
1194 Q_ASSERT(vertex(vi) == vertices[i]);
1196 TraversalStatus os = findInsertStatus(vi, ei);
1197 QPathEdge *op = edge(os.edge);
1199 Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
1201 TraversalStatus ns = next(os);
1203 QPathEdge *np = edge(ns.edge);
1205 op->setNext(os.traversal, os.direction, ei);
1206 np->setNext(ns.traversal, ns.direction, ei);
1217 Q_ASSERT(os.edge == ei);
1218 Q_ASSERT(ns.edge == ei);
1220 ep->setNext(os.traversal, os.direction, oe);
1221 ep->setNext(ns.traversal, ns.direction, ne);
1225 Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Forward) >= 0);
1226 Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Backward) >= 0);
1227 Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Forward) >= 0);
1228 Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Backward) >= 0);
1233int QWingedEdge::insert(
const QPathVertex &vertex)
1235 if (!m_vertices.isEmpty()) {
1236 const QPathVertex &last = m_vertices.last();
1237 if (vertex.x == last.x && vertex.y == last.y)
1238 return m_vertices.size() - 1;
1240 for (
int i = 0; i < m_vertices.size(); ++i) {
1241 const QPathVertex &v = m_vertices.at(i);
1242 if (qFuzzyCompare(v.x, vertex.x) && qFuzzyCompare(v.y, vertex.y)) {
1248 m_vertices << vertex;
1249 return m_vertices.size() - 1;
1252static void addLineTo(QPainterPath &path,
const QPointF &point)
1254 const int elementCount = path.elementCount();
1255 if (elementCount >= 2) {
1256 const QPainterPath::Element &middle = path.elementAt(elementCount - 1);
1257 if (middle.type == QPainterPath::LineToElement) {
1258 const QPointF first = path.elementAt(elementCount - 2);
1259 const QPointF d1 = point - first;
1260 const QPointF d2 = middle - first;
1262 const QPointF p(-d1.y(), d1.x());
1264 if (qFuzzyIsNull(dot(p, d2))) {
1265 path.setElementPositionAt(elementCount - 1, point.x(), point.y());
1276 QWingedEdge::TraversalStatus status;
1278 status.traversal = traversal;
1281 path.moveTo(*list.vertex(list.edge(edge)->first));
1284 const QPathEdge *ep = list.edge(status.edge);
1286 addLineTo(path, *list.vertex(ep
->vertex(status.direction
)));
1293 status = list.next(status);
1294 }
while (status.edge != edge);
1297void QWingedEdge::simplify()
1299 for (
int i = 0; i < edgeCount(); ++i) {
1300 const QPathEdge *ep = edge(i);
1303 int flag = 0x3 << 4;
1304 if ((ep->flag & flag) == flag) {
1312QPainterPath QWingedEdge::toPath()
const
1316 for (
int i = 0; i < edgeCount(); ++i) {
1317 const QPathEdge *ep = edge(i);
1319 if (ep->flag & 16) {
1320 add(path, *
this, i, QPathEdge::LeftTraversal);
1324 add(path, *
this, i, QPathEdge::RightTraversal);
1330bool QPathClipper::intersect()
1332 if (subjectPath == clipPath)
1335 QRectF r1 = subjectPath.controlPointRect();
1336 QRectF r2 = clipPath.controlPointRect();
1337 if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1338 qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1343 bool subjectIsRect = pathToRect(subjectPath);
1344 bool clipIsRect = pathToRect(clipPath);
1346 if (subjectIsRect && clipIsRect)
1348 else if (subjectIsRect)
1349 return clipPath.intersects(r1);
1350 else if (clipIsRect)
1351 return subjectPath.intersects(r2);
1353 QPathSegments a(subjectPath.elementCount());
1354 a.setPath(subjectPath);
1355 QPathSegments b(clipPath.elementCount());
1356 b.setPath(clipPath);
1358 QIntersectionFinder finder;
1359 if (finder.hasIntersections(a, b))
1362 for (
int i = 0; i < clipPath.elementCount(); ++i) {
1363 if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1364 const QPointF point = clipPath.elementAt(i);
1365 if (r1.contains(point) && subjectPath.contains(point))
1370 for (
int i = 0; i < subjectPath.elementCount(); ++i) {
1371 if (subjectPath.elementAt(i).type == QPainterPath::MoveToElement) {
1372 const QPointF point = subjectPath.elementAt(i);
1373 if (r2.contains(point) && clipPath.contains(point))
1381bool QPathClipper::contains()
1383 if (subjectPath == clipPath)
1386 QRectF r1 = subjectPath.controlPointRect();
1387 QRectF r2 = clipPath.controlPointRect();
1388 if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
1389 qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
1394 bool clipIsRect = pathToRect(clipPath);
1396 return subjectPath.contains(r2);
1398 QPathSegments a(subjectPath.elementCount());
1399 a.setPath(subjectPath);
1400 QPathSegments b(clipPath.elementCount());
1401 b.setPath(clipPath);
1403 QIntersectionFinder finder;
1404 if (finder.hasIntersections(a, b))
1407 for (
int i = 0; i < clipPath.elementCount(); ++i) {
1408 if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
1409 const QPointF point = clipPath.elementAt(i);
1410 if (!r1.contains(point) || !subjectPath.contains(point))
1418QPathClipper::QPathClipper(
const QPainterPath &subject,
1419 const QPainterPath &clip)
1420 : subjectPath(subject)
1423 aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1424 bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1429 QWingedEdge::TraversalStatus status;
1431 status.traversal = traversal;
1436 list.edge(status.edge)->flag |= 1;
1438 list.edge(status.edge)->flag |= 2;
1440 status = list.next(status);
1441 }
while (status.edge != edge);
1444template <
typename InputIterator>
1445InputIterator
qFuzzyFind(InputIterator first, InputIterator last, qreal val)
1447 while (first != last && !QT_PREPEND_NAMESPACE(qFuzzyCompare)(qreal(*first), qreal(val)))
1454 return qFuzzyCompare(a, b);
1457bool QPathClipper::pathToRect(
const QPainterPath &path, QRectF *rect)
1459 if (path.elementCount() != 5)
1462 const bool mightBeRect = path.elementAt(0).isMoveTo()
1463 && path.elementAt(1).isLineTo()
1464 && path.elementAt(2).isLineTo()
1465 && path.elementAt(3).isLineTo()
1466 && path.elementAt(4).isLineTo();
1471 const qreal x1 = path.elementAt(0).x;
1472 const qreal y1 = path.elementAt(0).y;
1474 const qreal x2 = path.elementAt(1).x;
1475 const qreal y2 = path.elementAt(2).y;
1477 if (path.elementAt(1).y != y1)
1480 if (path.elementAt(2).x != x2)
1483 if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
1486 if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
1490 rect->setCoords(x1, y1, x2, y2);
1496QPainterPath QPathClipper::clip(Operation operation)
1500 if (op != Simplify) {
1501 if (subjectPath == clipPath)
1502 return op == BoolSub ? QPainterPath() : subjectPath;
1504 bool subjectIsRect = pathToRect(subjectPath,
nullptr);
1505 bool clipIsRect = pathToRect(clipPath,
nullptr);
1507 const QRectF clipBounds = clipPath.boundingRect();
1508 const QRectF subjectBounds = subjectPath.boundingRect();
1510 if (!clipBounds.intersects(subjectBounds)) {
1515 return QPainterPath();
1517 QPainterPath result = subjectPath;
1518 if (result.fillRule() == clipPath.fillRule()) {
1519 result.addPath(clipPath);
1520 }
else if (result.fillRule() == Qt::WindingFill) {
1521 result = result.simplified();
1522 result.addPath(clipPath);
1524 result.addPath(clipPath.simplified());
1533 if (clipBounds.contains(subjectBounds)) {
1537 return QPainterPath();
1546 }
else if (subjectBounds.contains(clipBounds)) {
1547 if (subjectIsRect) {
1550 if (clipPath.fillRule() == Qt::OddEvenFill) {
1551 QPainterPath result = clipPath;
1552 result.addRect(subjectBounds);
1555 QPainterPath result = clipPath.simplified();
1556 result.addRect(subjectBounds);
1569 if (op == BoolAnd) {
1571 return intersect(clipPath, subjectBounds);
1572 else if (clipIsRect)
1573 return intersect(subjectPath, clipBounds);
1577 QWingedEdge list(subjectPath, clipPath);
1579 doClip(list, ClipMode);
1581 QPainterPath path = list.toPath();
1585bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
1587 QList<qreal> y_coords;
1588 y_coords.reserve(list.vertexCount());
1589 for (
int i = 0; i < list.vertexCount(); ++i)
1590 y_coords << list.vertex(i)->y;
1592 std::sort(y_coords.begin(), y_coords.end());
1593 y_coords.erase(std::unique(y_coords.begin(), y_coords.end(), fuzzyCompare), y_coords.end());
1595#ifdef QDEBUG_CLIPPER
1596 printf(
"sorted y coords:\n");
1597 for (
int i = 0; i < y_coords.size(); ++i) {
1598 printf(
"%.9f\n", y_coords.at(i));
1606 qreal maxHeight = 0;
1607 for (
int i = 0; i < list.edgeCount(); ++i) {
1608 QPathEdge *edge = list.edge(i);
1611 if ((edge->flag & 0x3) == 0x3)
1614 QPathVertex *a = list.vertex(edge->first);
1615 QPathVertex *b = list.vertex(edge->second);
1617 if (qFuzzyCompare(a->y, b->y))
1622 qreal height = qAbs(a->y - b->y);
1623 if (height > maxHeight) {
1630 QPathEdge *edge = list.edge(index);
1632 QPathVertex *a = list.vertex(edge->first);
1633 QPathVertex *b = list.vertex(edge->second);
1636 const int first = qFuzzyFind(y_coords.cbegin(), y_coords.cend(), qMin(a->y, b->y)) - y_coords.cbegin();
1637 const int last = qFuzzyFind(y_coords.cbegin() + first, y_coords.cend(), qMax(a->y, b->y)) - y_coords.cbegin();
1639 Q_ASSERT(first < y_coords.size() - 1);
1640 Q_ASSERT(last < y_coords.size());
1642 qreal biggestGap = y_coords.at(first + 1) - y_coords.at(first);
1643 int bestIdx = first;
1644 for (
int i = first + 1; i < last; ++i) {
1645 qreal gap = y_coords.at(i + 1) - y_coords.at(i);
1647 if (gap > biggestGap) {
1652 const qreal bestY = 0.5 * (y_coords.at(bestIdx) + y_coords.at(bestIdx + 1));
1654#ifdef QDEBUG_CLIPPER
1655 printf(
"y: %.9f, gap: %.9f\n", bestY, biggestGap);
1658 if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
1665 if (mode == ClipMode)
1673 QWingedEdge::TraversalStatus status;
1675 status.traversal = traversal;
1683 ep
->flag |= (flag | (flag << 4));
1685#ifdef QDEBUG_CLIPPER
1686 qDebug() <<
"traverse: adding edge " << status.edge <<
", mask:" << (flag << 4) <<ep->flag;
1689 status = list.next(status);
1690 }
while (status.edge != edge);
1705static bool bool_op(
bool a,
bool b, QPathClipper::Operation op)
1708 case QPathClipper::BoolAnd:
1710 case QPathClipper::BoolOr:
1711 case QPathClipper::Simplify:
1713 case QPathClipper::BoolSub:
1721bool QWingedEdge::isInside(qreal x, qreal y)
const
1724 for (
int i = 0; i < edgeCount(); ++i) {
1725 const QPathEdge *ep = edge(i);
1728 int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
1733 QPointF a = *vertex(ep->first);
1734 QPointF b = *vertex(ep->second);
1736 if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1737 qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1739 if (intersectionX > x)
1749 QList<QCrossingEdge> crossings;
1750 for (
int i = 0; i < list.edgeCount(); ++i) {
1752 QPointF a = *list.vertex(edge->first);
1753 QPointF b = *list.vertex(edge->second);
1755 if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
1756 const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
1764bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
1766 QList<QCrossingEdge> crossings = findCrossings(list, y);
1768 Q_ASSERT(!crossings.isEmpty());
1769 std::sort(crossings.begin(), crossings.end());
1776#ifdef QDEBUG_CLIPPER
1777 qDebug() <<
"crossings:" << crossings.size();
1779 for (
int i = 0; i < crossings.size() - 1; ++i) {
1780 int ei = crossings.at(i).edge;
1781 const QPathEdge *edge = list.edge(ei);
1783 windingA += edge->windingA;
1784 windingB += edge->windingB;
1786 const bool hasLeft = (edge->flag >> 4) & 1;
1787 const bool hasRight = (edge->flag >> 4) & 2;
1789 windingD += hasLeft ^ hasRight;
1791 const bool inA = (windingA & aMask) != 0;
1792 const bool inB = (windingB & bMask) != 0;
1793 const bool inD = (windingD & 0x1) != 0;
1795 const bool inside = bool_op(inA, inB, op);
1796 const bool add = inD ^ inside;
1798#ifdef QDEBUG_CLIPPER
1799 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);
1803 if (mode == CheckMode)
1806 qreal y0 = list.vertex(edge->first)->y;
1807 qreal y1 = list.vertex(edge->second)->y;
1810 if (!(edge->flag & 1))
1811 traverse(list, ei, QPathEdge::LeftTraversal);
1813 if (!(edge->flag & 2))
1814 clear(list, ei, QPathEdge::RightTraversal);
1816 if (!(edge->flag & 1))
1817 clear(list, ei, QPathEdge::LeftTraversal);
1819 if (!(edge->flag & 2))
1820 traverse(list, ei, QPathEdge::RightTraversal);
1825 if (!(edge->flag & 1))
1826 clear(list, ei, QPathEdge::LeftTraversal);
1828 if (!(edge->flag & 2))
1829 clear(list, ei, QPathEdge::RightTraversal);
1838QList<QPainterPath> toSubpaths(
const QPainterPath &path)
1841 QList<QPainterPath> subpaths;
1845 QPainterPath current;
1846 for (
int i = 0; i < path.elementCount(); ++i) {
1847 const QPainterPath::Element &e = path.elementAt(i);
1849 case QPainterPath::MoveToElement:
1850 if (current.elementCount() > 1)
1851 subpaths += current;
1852 current = QPainterPath();
1855 case QPainterPath::LineToElement:
1858 case QPainterPath::CurveToElement: {
1859 current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2));
1863 case QPainterPath::CurveToDataElement:
1864 Q_ASSERT(!
"toSubpaths(), bad element type");
1869 if (current.elementCount() > 1)
1870 subpaths << current;
1877 Left, Top, Right, Bottom
1880static bool isVertical(Edge edge)
1882 return edge == Left || edge == Right;
1886bool compare(
const QPointF &p, qreal t)
1902QPointF intersectLine(
const QPointF &a,
const QPointF &b, qreal t)
1908 return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910 return line.pointAt((t - a.y()) / (b.y() - a.y()));
1914void addLine(QPainterPath &path,
const QLineF &line)
1916 if (path.elementCount() > 0)
1917 path.lineTo(line.p1());
1919 path.moveTo(line.p1());
1921 path.lineTo(line.p2());
1925void clipLine(
const QPointF &a,
const QPointF &b, qreal t, QPainterPath &result)
1927 bool outA = compare<edge>(a, t);
1928 bool outB = compare<edge>(b, t);
1933 addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935 addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937 addLine(result, QLineF(a, b));
1940void addBezier(QPainterPath &path,
const QBezier &bezier)
1942 if (path.elementCount() > 0)
1943 path.lineTo(bezier.pt1());
1945 path.moveTo(bezier.pt1());
1947 path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4());
1951void clipBezier(
const QPointF &a,
const QPointF &b,
const QPointF &c,
const QPointF &d, qreal t, QPainterPath &result)
1953 QBezier bezier = QBezier::fromPoints(a, b, c, d);
1955 bool outA = compare<edge>(a, t);
1956 bool outB = compare<edge>(b, t);
1957 bool outC = compare<edge>(c, t);
1958 bool outD = compare<edge>(d, t);
1960 int outCount =
int(outA) +
int(outB) +
int(outC) +
int(outD);
1965 if (outCount == 0) {
1966 addBezier(result, bezier);
1970 QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform();
1971 QBezier unflipped = bezier;
1972 QBezier flipped = bezier.mapBy(flip);
1974 qreal t0 = 0, t1 = 1;
1975 int stationary = flipped.stationaryYPoints(t0, t1);
1979 points[0] = unflipped.pt1();
1982 int segmentCount = 0;
1983 if (stationary > 0) {
1985 segments[segmentCount] = t0;
1986 points[segmentCount] = unflipped.pointAt(t0);
1988 if (stationary > 1) {
1990 segments[segmentCount] = t1;
1991 points[segmentCount] = unflipped.pointAt(t1);
1994 segments[segmentCount] = 1;
1995 points[segmentCount] = unflipped.pt4();
1997 qreal lastIntersection = 0;
1998 for (
int i = 0; i < segmentCount; ++i) {
1999 outA = compare<edge>(points[i], t);
2000 outB = compare<edge>(points[i+1], t);
2003 qreal intersection = flipped.tForY(segments[i], segments[i+1], t);
2006 addBezier(result, unflipped.getSubRange(lastIntersection, intersection));
2008 lastIntersection = intersection;
2013 addBezier(result, unflipped.getSubRange(lastIntersection, 1));
2018QPainterPath clip(
const QPainterPath &path, qreal t)
2020 QPainterPath result;
2021 for (
int i = 1; i < path.elementCount(); ++i) {
2022 const QPainterPath::Element &element = path.elementAt(i);
2023 Q_ASSERT(!element.isMoveTo());
2024 if (element.isLineTo()) {
2025 clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
2027 clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2032 int last = path.elementCount() - 1;
2033 if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
2034 clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
2039QPainterPath intersectPath(
const QPainterPath &path,
const QRectF &rect)
2041 QList<QPainterPath> subpaths = toSubpaths(path);
2043 QPainterPath result;
2044 result.setFillRule(path.fillRule());
2045 for (
int i = 0; i < subpaths.size(); ++i) {
2046 QPainterPath subPath = subpaths.at(i);
2047 QRectF bounds = subPath.boundingRect();
2048 if (bounds.intersects(rect)) {
2049 if (bounds.left() < rect.left())
2050 subPath = clip<Left>(subPath, rect.left());
2051 if (bounds.right() > rect.right())
2052 subPath = clip<Right>(subPath, rect.right());
2054 bounds = subPath.boundingRect();
2056 if (bounds.top() < rect.top())
2057 subPath = clip<Top>(subPath, rect.top());
2058 if (bounds.bottom() > rect.bottom())
2059 subPath = clip<Bottom>(subPath, rect.bottom());
2061 if (subPath.elementCount() > 1)
2062 result.addPath(subPath);
2067 if (result.boundingRect().isEmpty())
2068 return QPainterPath();
2075QPainterPath QPathClipper::intersect(
const QPainterPath &path,
const QRectF &rect)
2077 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