Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpathclipper.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <private/qbezier_p.h>
8#include <private/qdatabuffer_p.h>
9#include <private/qnumeric_p.h>
10#include <qmath.h>
11#include <algorithm>
12
13/**
14 The algorithm is as follows:
15
16 1. Find all intersections between the two paths (including self-intersections),
17 and build a winged edge structure of non-intersecting parts.
18 2. While there are more unhandled edges:
19 3. Pick a y-coordinate from an unhandled edge.
20 4. Intersect the horizontal line at y-coordinate with all edges.
21 5. Traverse intersections left to right deciding whether each subpath should be added or not.
22 6. If the subpath should be added, traverse the winged-edge structure and add the edges to
23 a separate winged edge structure.
24 7. Mark all edges in subpaths crossing the horizontal line as handled.
25 8. (Optional) Simplify the resulting winged edge structure by merging shared edges.
26 9. Convert the resulting winged edge structure to a painter path.
27 */
28
29#include <qdebug.h>
30
32
33static inline bool fuzzyIsNull(qreal d)
34{
35 if (sizeof(qreal) == sizeof(double))
36 return qAbs(d) <= 1e-12;
37 else
38 return qAbs(d) <= 1e-5f;
39}
40
41static inline bool comparePoints(const QPointF &a, const QPointF &b)
42{
43 return fuzzyIsNull(a.x() - b.x())
44 && fuzzyIsNull(a.y() - b.y());
45}
46
47//#define QDEBUG_CLIPPER
48static qreal dot(const QPointF &a, const QPointF &b)
49{
50 return a.x() * b.x() + a.y() * b.y();
51}
52
53static void normalize(double &x, double &y)
54{
55 double reciprocal = 1 / qSqrt(x * x + y * y);
56 x *= reciprocal;
57 y *= reciprocal;
58}
59
68
70{
71public:
73 bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const;
74
75private:
76 bool linesIntersect(const QLineF &a, const QLineF &b) const;
77};
78
79bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const
80{
81 const QPointF p1 = a.p1();
82 const QPointF p2 = a.p2();
83
84 const QPointF q1 = b.p1();
85 const QPointF q2 = b.p2();
86
87 if (comparePoints(p1, p2) || comparePoints(q1, q2))
88 return false;
89
90 const bool p1_equals_q1 = comparePoints(p1, q1);
91 const bool p2_equals_q2 = comparePoints(p2, q2);
92
93 if (p1_equals_q1 && p2_equals_q2)
94 return true;
95
96 const bool p1_equals_q2 = comparePoints(p1, q2);
97 const bool p2_equals_q1 = comparePoints(p2, q1);
98
99 if (p1_equals_q2 && p2_equals_q1)
100 return true;
101
102 const QPointF pDelta = p2 - p1;
103 const QPointF qDelta = q2 - q1;
104
105 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
106
107 if (qFuzzyIsNull(par)) {
108 const QPointF normal(-pDelta.y(), pDelta.x());
109
110 // coinciding?
111 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
112 const qreal dp = dot(pDelta, pDelta);
113
114 const qreal tq1 = dot(pDelta, q1 - p1);
115 const qreal tq2 = dot(pDelta, q2 - p1);
116
117 if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
118 return true;
119
120 const qreal dq = dot(qDelta, qDelta);
121
122 const qreal tp1 = dot(qDelta, p1 - q1);
123 const qreal tp2 = dot(qDelta, p2 - q1);
124
125 if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
126 return true;
127 }
128
129 return false;
130 }
131
132 const qreal invPar = 1 / par;
133
134 const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
135 qDelta.x() * (q1.y() - p1.y())) * invPar;
136
137 if (tp < 0 || tp > 1)
138 return false;
139
140 const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
141 pDelta.x() * (q1.y() - p1.y())) * invPar;
142
143 return tq >= 0 && tq <= 1;
144}
145
147{
148 if (a.segments() == 0 || b.segments() == 0)
149 return false;
150
151 const QRectF &rb0 = b.elementBounds(0);
152
153 qreal minX = rb0.left();
154 qreal minY = rb0.top();
155 qreal maxX = rb0.right();
156 qreal maxY = rb0.bottom();
157
158 for (int i = 1; i < b.segments(); ++i) {
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());
164 }
165
166 QRectF rb(minX, minY, maxX - minX, maxY - minY);
167
168 for (int i = 0; i < a.segments(); ++i) {
169 const QRectF &r1 = a.elementBounds(i);
170
171 if (r1.left() > rb.right() || rb.left() > r1.right())
172 continue;
173 if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
174 continue;
175
176 for (int j = 0; j < b.segments(); ++j) {
177 const QRectF &r2 = b.elementBounds(j);
178
179 if (r1.left() > r2.right() || r2.left() > r1.right())
180 continue;
181 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
182 continue;
183
184 if (linesIntersect(a.lineAt(i), b.lineAt(j)))
185 return true;
186 }
187 }
188
189 return false;
190}
191
192namespace {
193struct TreeNode
194{
195 qreal splitLeft;
196 qreal splitRight;
197 bool leaf;
198
199 int lowestLeftIndex;
200 int lowestRightIndex;
201
202 union {
203 struct {
204 int first;
205 int last;
206 } interval;
207 struct {
208 int left;
209 int right;
210 } children;
211 } index;
212};
213
214struct RectF
215{
216 qreal x1;
217 qreal y1;
218 qreal x2;
219 qreal y2;
220};
221
222class SegmentTree
223{
224public:
225 SegmentTree(QPathSegments &segments);
226
227 void produceIntersections(int segment);
228
229private:
230 TreeNode buildTree(int first, int last, int depth, const RectF &bounds);
231
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);
235
236 QPathSegments &m_segments;
237 QList<int> m_index;
238
239 RectF m_bounds;
240
241 QList<TreeNode> m_tree;
242 QDataBuffer<QIntersection> m_intersections;
243};
244
245SegmentTree::SegmentTree(QPathSegments &segments)
246 : m_segments(segments),
247 m_intersections(0)
248{
249 m_bounds.x1 = qt_inf();
250 m_bounds.y1 = qt_inf();
251 m_bounds.x2 = -qt_inf();
252 m_bounds.y2 = -qt_inf();
253
254 m_index.resize(m_segments.segments());
255
256 for (int i = 0; i < m_index.size(); ++i) {
257 m_index[i] = i;
258
259 const QRectF &segmentBounds = m_segments.elementBounds(i);
260
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();
269 }
270
271 m_tree.resize(1);
272
273 TreeNode root = buildTree(0, m_index.size(), 0, m_bounds);
274 m_tree[0] = root;
275}
276
277static inline qreal coordinate(const QPointF &pos, int axis)
278{
279 return axis == 0 ? pos.x() : pos.y();
280}
281
282TreeNode SegmentTree::buildTree(int first, int last, int depth, const RectF &bounds)
283{
284 if (depth >= 24 || (last - first) <= 10) {
285 TreeNode node = {};
286 node.leaf = true;
287 node.index.interval.first = first;
288 node.index.interval.last = last;
289
290 return node;
291 }
292
293 int splitAxis = (depth & 1);
294
295 TreeNode node;
296 node.leaf = false;
297
298 qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]);
299
300 node.splitLeft = (&bounds.x1)[splitAxis];
301 node.splitRight = (&bounds.x2)[splitAxis];
302
303 node.lowestLeftIndex = INT_MAX;
304 node.lowestRightIndex = INT_MAX;
305
306 const int treeSize = m_tree.size();
307
308 node.index.children.left = treeSize;
309 node.index.children.right = treeSize + 1;
310
311 m_tree.resize(treeSize + 2);
312
313 int l = first;
314 int r = last - 1;
315
316 // partition into left and right sets
317 while (l <= r) {
318 const int index = m_index.at(l);
319 const QRectF &segmentBounds = m_segments.elementBounds(index);
320
321 qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis);
322
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;
329 ++l;
330 } else {
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]);
336 --r;
337 }
338 }
339
340 RectF lbounds = bounds;
341 (&lbounds.x2)[splitAxis] = node.splitLeft;
342
343 RectF rbounds = bounds;
344 (&rbounds.x1)[splitAxis] = node.splitRight;
345
346 TreeNode left = buildTree(first, l, depth + 1, lbounds);
347 m_tree[node.index.children.left] = left;
348
349 TreeNode right = buildTree(l, last, depth + 1, rbounds);
350 m_tree[node.index.children.right] = right;
351
352 return node;
353}
354
355void SegmentTree::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections)
356{
357 const QPointF p1 = a.p1();
358 const QPointF p2 = a.p2();
359
360 const QPointF q1 = b.p1();
361 const QPointF q2 = b.p2();
362
363 if (comparePoints(p1, p2) || comparePoints(q1, q2))
364 return;
365
366 const bool p1_equals_q1 = comparePoints(p1, q1);
367 const bool p2_equals_q2 = comparePoints(p2, q2);
368
369 if (p1_equals_q1 && p2_equals_q2)
370 return;
371
372 const bool p1_equals_q2 = comparePoints(p1, q2);
373 const bool p2_equals_q1 = comparePoints(p2, q1);
374
375 if (p1_equals_q2 && p2_equals_q1)
376 return;
377
378 const QPointF pDelta = p2 - p1;
379 const QPointF qDelta = q2 - q1;
380
381 const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
382
383 if (qFuzzyIsNull(par)) {
384 const QPointF normal(-pDelta.y(), pDelta.x());
385
386 // coinciding?
387 if (qFuzzyIsNull(dot(normal, q1 - p1))) {
388 const qreal invDp = 1 / dot(pDelta, pDelta);
389
390 const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
391 const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
392
393 if (tq1 > 0 && tq1 < 1) {
394 QIntersection intersection;
395 intersection.alphaA = tq1;
396 intersection.alphaB = 0;
397 intersection.pos = q1;
398 intersections.add(intersection);
399 }
400
401 if (tq2 > 0 && tq2 < 1) {
402 QIntersection intersection;
403 intersection.alphaA = tq2;
404 intersection.alphaB = 1;
405 intersection.pos = q2;
406 intersections.add(intersection);
407 }
408
409 const qreal invDq = 1 / dot(qDelta, qDelta);
410
411 const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
412 const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
413
414 if (tp1 > 0 && tp1 < 1) {
415 QIntersection intersection;
416 intersection.alphaA = 0;
417 intersection.alphaB = tp1;
418 intersection.pos = p1;
419 intersections.add(intersection);
420 }
421
422 if (tp2 > 0 && tp2 < 1) {
423 QIntersection intersection;
424 intersection.alphaA = 1;
425 intersection.alphaB = tp2;
426 intersection.pos = p2;
427 intersections.add(intersection);
428 }
429 }
430
431 return;
432 }
433
434 // if the lines are not parallel and share a common end point, then they
435 // don't intersect
436 if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
437 return;
438
439
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;
444
445 if (tp<0 || tp>1 || tq<0 || tq>1)
446 return;
447
448 const bool p_zero = qFuzzyIsNull(tp);
449 const bool p_one = qFuzzyIsNull(tp - 1);
450
451 const bool q_zero = qFuzzyIsNull(tq);
452 const bool q_one = qFuzzyIsNull(tq - 1);
453
454 if ((q_zero || q_one) && (p_zero || p_one))
455 return;
456
457 QPointF pt;
458 if (p_zero) {
459 pt = p1;
460 } else if (p_one) {
461 pt = p2;
462 } else if (q_zero) {
463 pt = q1;
464 } else if (q_one) {
465 pt = q2;
466 } else {
467 pt = q1 + (q2 - q1) * tq;
468 }
469
470 QIntersection intersection;
471 intersection.alphaA = tp;
472 intersection.alphaB = tq;
473 intersection.pos = pt;
474 intersections.add(intersection);
475}
476
477void SegmentTree::produceIntersections(int segment)
478{
479 const QRectF &segmentBounds = m_segments.elementBounds(segment);
480
481 RectF sbounds;
482 sbounds.x1 = segmentBounds.left();
483 sbounds.y1 = segmentBounds.top();
484 sbounds.x2 = segmentBounds.right();
485 sbounds.y2 = segmentBounds.bottom();
486
487 produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0);
488}
489
490void SegmentTree::produceIntersectionsLeaf(const TreeNode &node, int segment)
491{
492 const QRectF &r1 = m_segments.elementBounds(segment);
493 const QLineF lineA = m_segments.lineAt(segment);
494
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)
498 continue;
499
500 const QRectF &r2 = m_segments.elementBounds(other);
501
502 if (r1.left() > r2.right() || r2.left() > r1.right())
503 continue;
504 if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
505 continue;
506
507 m_intersections.reset();
508
509 const QLineF lineB = m_segments.lineAt(other);
510
511 intersectLines(lineA, lineB, m_intersections);
512
513 for (int k = 0; k < m_intersections.size(); ++k) {
514 QPathSegments::Intersection i_isect, j_isect;
515 i_isect.t = m_intersections.at(k).alphaA;
516 j_isect.t = m_intersections.at(k).alphaB;
517
518 i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos);
519
520 i_isect.next = 0;
521 j_isect.next = 0;
522
523 m_segments.addIntersection(segment, i_isect);
524 m_segments.addIntersection(other, j_isect);
525 }
526 }
527}
528
529void SegmentTree::produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis)
530{
531 if (node.leaf) {
532 produceIntersectionsLeaf(node, segment);
533 return;
534 }
535
536 RectF lbounds = nodeBounds;
537 (&lbounds.x2)[axis] = node.splitLeft;
538
539 RectF rbounds = nodeBounds;
540 (&rbounds.x1)[axis] = node.splitRight;
541
542 if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft)
543 produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis);
544
545 if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight)
546 produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis);
547}
548
549}
550
552{
553 SegmentTree tree(segments);
554
555 for (int i = 0; i < segments.segments(); ++i)
556 tree.produceIntersections(i);
557}
558
560{
561public:
568
569 struct Node {
570 int point;
571 int id;
572
575 };
576
578 : m_segments(&segments)
580 , m_id(0)
581 {
582 m_nodes.resize(m_segments->points());
583
584 for (int i = 0; i < m_nodes.size(); ++i) {
585 m_nodes.at(i).point = i;
586 m_nodes.at(i).id = -1;
587 }
588
589 m_rootNode = build(0, m_nodes.size());
590 }
591
592 int build(int begin, int end, int depth = 0);
593
595 {
596 return &m_nodes.at(m_rootNode);
597 }
598
599 inline int nextId()
600 {
601 return m_id++;
602 }
603
604private:
605 const QPathSegments *m_segments;
606 QDataBuffer<Node> m_nodes;
607
608 int m_rootNode;
609 int m_id;
610};
611
612template <typename T>
613void qTraverseKdPointTree(QKdPointTree::Node &node, T &t, int depth = 0)
614{
615 QKdPointTree::Traversal status = t(node, depth);
616
617 const bool traverseRight = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseRight);
618 const bool traverseLeft = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseLeft);
619
620 if (traverseLeft && node.left)
621 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.left, t, depth + 1);
622
623 if (traverseRight && node.right)
624 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.right, t, depth + 1);
625}
626
627static inline qreal component(const QPointF &point, unsigned int i)
628{
629 Q_ASSERT(i < 2);
630 const qreal components[] = { point.x(), point.y() };
631 return components[i];
632}
633
634int QKdPointTree::build(int begin, int end, int depth)
635{
636 Q_ASSERT(end > begin);
637
638 const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
639
640 int first = begin + 1;
641 int last = end - 1;
642
643 while (first <= last) {
644 const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
645
646 if (value < pivot)
647 ++first;
648 else {
649 qSwap(m_nodes.at(first), m_nodes.at(last));
650 --last;
651 }
652 }
653
654 if (last != begin)
655 qSwap(m_nodes.at(last), m_nodes.at(begin));
656
657 if (last > begin)
658 m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
659 else
660 m_nodes.at(last).left = nullptr;
661
662 if (last + 1 < end)
663 m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
664 else
665 m_nodes.at(last).right = nullptr;
666
667 return last;
668}
669
671{
672public:
673 QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
674 : m_result(-1)
675 , m_segments(&segments)
676 , m_tree(&tree)
677 {
678 pointComponents[0] = segments.pointAt(point).x();
679 pointComponents[1] = segments.pointAt(point).y();
680 }
681
682 inline QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
683 {
684 if (m_result != -1)
686
687 const QPointF &nodePoint = m_segments->pointAt(node.point);
688
689 const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
690
691 const qreal pivot = pivotComponents[depth & 1];
692 const qreal value = pointComponents[depth & 1];
693
694 if (fuzzyIsNull(pivot - value)) {
695 const qreal pivot2 = pivotComponents[(depth + 1) & 1];
696 const qreal value2 = pointComponents[(depth + 1) & 1];
697
698 if (fuzzyIsNull(pivot2 - value2)) {
699 if (node.id < 0)
700 node.id = m_tree->nextId();
701
702 m_result = node.id;
704 } else
706 } else if (value < pivot) {
708 } else {
710 }
711 }
712
713 int result() const
714 {
715 return m_result;
716 }
717
718private:
719 qreal pointComponents[2];
720 int m_result;
721 const QPathSegments *m_segments;
722 QKdPointTree *m_tree;
723};
724
725// merge all points that are within qFuzzyCompare range of each other
727{
728 QKdPointTree tree(*this);
729
730 if (tree.rootNode()) {
731 QDataBuffer<QPointF> mergedPoints(points());
732 QDataBuffer<int> pointIndices(points());
733
734 for (int i = 0; i < points(); ++i) {
735 QKdPointFinder finder(i, *this, tree);
736 QT_PREPEND_NAMESPACE(qTraverseKdPointTree<QKdPointFinder>)(*tree.rootNode(), finder);
737
738 Q_ASSERT(finder.result() != -1);
739
740 if (finder.result() >= mergedPoints.size())
741 mergedPoints << m_points.at(i);
742
743 pointIndices << finder.result();
744 }
745
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);
749 }
750
751 for (int i = 0; i < m_intersections.size(); ++i)
752 m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
753
754 m_points.swap(mergedPoints);
755 }
756}
757
758void QWingedEdge::intersectAndAdd()
759{
760 QIntersectionFinder finder;
761 finder.produceIntersections(m_segments);
762
763 m_segments.mergePoints();
764
765 for (int i = 0; i < m_segments.points(); ++i)
766 addVertex(m_segments.pointAt(i));
767
768 QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments());
769 for (int i = 0; i < m_segments.segments(); ++i) {
770 intersections.reset();
771
772 int pathId = m_segments.pathId(i);
773
774 const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
775 while (isect) {
776 intersections << *isect;
777
778 if (isect->next) {
779 isect += isect->next;
780 } else {
781 isect = nullptr;
782 }
783 }
784
785 std::sort(intersections.data(), intersections.data() + intersections.size());
786
787 int first = m_segments.segmentAt(i).va;
788 int second = m_segments.segmentAt(i).vb;
789
790 int last = first;
791 for (int j = 0; j < intersections.size(); ++j) {
792 const QPathSegments::Intersection &isect = intersections.at(j);
793
794 QPathEdge *ep = edge(addEdge(last, isect.vertex));
795
796 if (ep) {
797 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
798 if (pathId == 0)
799 ep->windingA += dir;
800 else
801 ep->windingB += dir;
802 }
803
804 last = isect.vertex;
805 }
806
807 QPathEdge *ep = edge(addEdge(last, second));
808
809 if (ep) {
810 const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
811 if (pathId == 0)
812 ep->windingA += dir;
813 else
814 ep->windingB += dir;
815 }
816 }
817}
818
819QWingedEdge::QWingedEdge() :
820 m_edges(0),
821 m_vertices(0),
822 m_segments(0)
823{
824}
825
826QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip) :
827 m_edges(subject.elementCount()),
828 m_vertices(subject.elementCount()),
829 m_segments(subject.elementCount())
830{
831 m_segments.setPath(subject);
832 m_segments.addPath(clip);
833
834 intersectAndAdd();
835}
836
837QWingedEdge::TraversalStatus QWingedEdge::next(const QWingedEdge::TraversalStatus &status) const
838{
839 const QPathEdge *sp = edge(status.edge);
840 Q_ASSERT(sp);
841
842 TraversalStatus result;
843 result.edge = sp->next(status.traversal, status.direction);
844 result.traversal = status.traversal;
845 result.direction = status.direction;
846
847 const QPathEdge *rp = edge(result.edge);
848 Q_ASSERT(rp);
849
850 if (sp->vertex(status.direction) == rp->vertex(status.direction))
851 result.flip();
852
853 return result;
854}
855
856static bool isLine(const QBezier &bezier)
857{
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());
861
862 // point?
863 if (equal_1_2 && equal_2_3 && equal_3_4)
864 return true;
865
866 if (comparePoints(bezier.pt1(), bezier.pt4()))
867 return equal_1_2 || equal_3_4;
868
869 return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
870}
871
872void QPathSegments::setPath(const QPainterPath &path)
873{
874 m_points.reset();
875 m_intersections.reset();
876 m_segments.reset();
877
878 m_pathId = 0;
879
880 addPath(path);
881}
882
883void QPathSegments::addPath(const QPainterPath &path)
884{
885 int firstSegment = m_segments.size();
886
887 bool hasMoveTo = false;
888 int lastMoveTo = 0;
889 int last = 0;
890 for (int i = 0; i < path.elementCount(); ++i) {
891 int current = m_points.size();
892
893 QPointF currentPoint;
894 if (path.elementAt(i).type == QPainterPath::CurveToElement)
895 currentPoint = path.elementAt(i+2);
896 else
897 currentPoint = path.elementAt(i);
898
899 if (i > 0 && comparePoints(m_points.at(lastMoveTo), currentPoint))
900 current = lastMoveTo;
901 else
902 m_points << currentPoint;
903
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);
908 hasMoveTo = true;
909 last = lastMoveTo = current;
910 break;
911 case QPainterPath::LineToElement:
912 m_segments << Segment(m_pathId, last, current);
913 last = current;
914 break;
915 case QPainterPath::CurveToElement:
916 {
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);
920 } else {
921 QRectF bounds = bezier.bounds();
922
923 // threshold based on similar algorithm as in qtriangulatingstroker.cpp
924 int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6));
925
926 if (threshold < 3) threshold = 3;
927 qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
928
929 for (int t = 1; t < threshold - 1; ++t) {
930 currentPoint = bezier.pointAt(t * one_over_threshold_minus_1);
931
932 int index = m_points.size();
933 m_segments << Segment(m_pathId, last, index);
934 last = index;
935
936 m_points << currentPoint;
937 }
938
939 m_segments << Segment(m_pathId, last, current);
940 }
941 }
942 last = current;
943 i += 2;
944 break;
945 default:
946 Q_ASSERT(false);
947 break;
948 }
949 }
950
951 if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
952 m_segments << Segment(m_pathId, last, lastMoveTo);
953
954 for (int i = firstSegment; i < m_segments.size(); ++i) {
955 const QLineF line = lineAt(i);
956
957 qreal x1 = line.p1().x();
958 qreal y1 = line.p1().y();
959 qreal x2 = line.p2().x();
960 qreal y2 = line.p2().y();
961
962 if (x2 < x1)
963 qSwap(x1, x2);
964 if (y2 < y1)
965 qSwap(y1, y2);
966
967 m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
968 }
969
970 ++m_pathId;
971}
972
973qreal QWingedEdge::delta(int vertex, int a, int b) const
974{
975 const QPathEdge *ap = edge(a);
976 const QPathEdge *bp = edge(b);
977
978 double a_angle = ap->angle;
979 double b_angle = bp->angle;
980
981 if (vertex == ap->second)
982 a_angle = ap->invAngle;
983
984 if (vertex == bp->second)
985 b_angle = bp->invAngle;
986
987 double result = b_angle - a_angle;
988
989 if (result >= 128.)
990 return result - 128.;
991 else if (result < 0)
992 return result + 128.;
993 else
994 return result;
995}
996
997QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const
998{
999 const QPathVertex *vp = vertex(vi);
1000
1001 Q_ASSERT(vp);
1002 Q_ASSERT(ei >= 0);
1003 Q_ASSERT(vp->edge >= 0);
1004
1005 int position = vp->edge;
1006 qreal d = 128.;
1007
1008 TraversalStatus status;
1009 status.direction = edge(vp->edge)->directionTo(vi);
1010 status.traversal = QPathEdge::RightTraversal;
1011 status.edge = vp->edge;
1012
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;
1016#endif
1017
1018 do {
1019 status = next(status);
1020 status.flip();
1021
1022 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1023 qreal d2 = delta(vi, ei, status.edge);
1024
1025#ifdef QDEBUG_CLIPPER
1026 const QPathEdge *op = edge(status.edge);
1027 qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle;
1028#endif
1029
1030 if (d2 < d) {
1031 position = status.edge;
1032 d = d2;
1033 }
1034 } while (status.edge != vp->edge);
1035
1036 status.traversal = QPathEdge::LeftTraversal;
1037 status.direction = QPathEdge::Forward;
1038 status.edge = position;
1039
1040 if (edge(status.edge)->vertex(status.direction) != vi)
1041 status.flip();
1042
1043#ifdef QDEBUG_CLIPPER
1044 qDebug() << "Inserting edge" << ei << "to" << (status.traversal == QPathEdge::LeftTraversal ? "left" : "right") << "of edge" << status.edge;
1045#endif
1046
1047 Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
1048
1049 return status;
1050}
1051
1052void QWingedEdge::removeEdge(int ei)
1053{
1054 QPathEdge *ep = edge(ei);
1055
1056 TraversalStatus status;
1057 status.direction = QPathEdge::Forward;
1058 status.traversal = QPathEdge::RightTraversal;
1059 status.edge = ei;
1060
1061 TraversalStatus forwardRight = next(status);
1062 forwardRight.flipDirection();
1063
1064 status.traversal = QPathEdge::LeftTraversal;
1065 TraversalStatus forwardLeft = next(status);
1066 forwardLeft.flipDirection();
1067
1068 status.direction = QPathEdge::Backward;
1069 TraversalStatus backwardLeft = next(status);
1070 backwardLeft.flipDirection();
1071
1072 status.traversal = QPathEdge::RightTraversal;
1073 TraversalStatus backwardRight = next(status);
1074 backwardRight.flipDirection();
1075
1076 edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
1077 edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
1078
1079 edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
1080 edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
1081
1082 ep->setNext(QPathEdge::Forward, ei);
1083 ep->setNext(QPathEdge::Backward, ei);
1084
1085 QPathVertex *a = vertex(ep->first);
1086 QPathVertex *b = vertex(ep->second);
1087
1088 a->edge = backwardRight.edge;
1089 b->edge = forwardRight.edge;
1090}
1091
1092static int commonEdge(const QWingedEdge &list, int a, int b)
1093{
1094 const QPathVertex *ap = list.vertex(a);
1095 Q_ASSERT(ap);
1096
1097 const QPathVertex *bp = list.vertex(b);
1098 Q_ASSERT(bp);
1099
1100 if (ap->edge < 0 || bp->edge < 0)
1101 return -1;
1102
1103 QWingedEdge::TraversalStatus status;
1104 status.edge = ap->edge;
1105 status.direction = list.edge(status.edge)->directionTo(a);
1106 status.traversal = QPathEdge::RightTraversal;
1107
1108 do {
1109 const QPathEdge *ep = list.edge(status.edge);
1110
1111 if ((ep->first == a && ep->second == b)
1112 || (ep->first == b && ep->second == a))
1113 return status.edge;
1114
1115 status = list.next(status);
1116 status.flip();
1117 } while (status.edge != ap->edge);
1118
1119 return -1;
1120}
1121
1122static double computeAngle(const QPointF &v)
1123{
1124#if 1
1125 if (v.x() == 0) {
1126 return v.y() <= 0 ? 0 : 64.;
1127 } else if (v.y() == 0) {
1128 return v.x() <= 0 ? 32. : 96.;
1129 }
1130
1131 double vx = v.x();
1132 double vy = v.y();
1133 normalize(vx, vy);
1134 if (vy < 0) {
1135 if (vx < 0) { // 0 - 32
1136 return -32. * vx;
1137 } else { // 96 - 128
1138 return 128. - 32. * vx;
1139 }
1140 } else { // 32 - 96
1141 return 64. + 32. * vx;
1142 }
1143#else
1144 // doesn't seem to be robust enough
1145 return qAtan2(v.x(), v.y()) + Q_PI;
1146#endif
1147}
1148
1149int QWingedEdge::addEdge(const QPointF &a, const QPointF &b)
1150{
1151 int fi = insert(a);
1152 int si = insert(b);
1153
1154 return addEdge(fi, si);
1155}
1156
1157int QWingedEdge::addEdge(int fi, int si)
1158{
1159 if (fi == si)
1160 return -1;
1161
1162 int common = commonEdge(*this, fi, si);
1163 if (common >= 0)
1164 return common;
1165
1166 m_edges << QPathEdge(fi, si);
1167
1168 int ei = m_edges.size() - 1;
1169
1170 QPathVertex *fp = vertex(fi);
1171 QPathVertex *sp = vertex(si);
1172
1173 QPathEdge *ep = edge(ei);
1174
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;
1180
1181 QPathVertex *vertices[2] = { fp, sp };
1182 QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
1183
1184#ifdef QDEBUG_CLIPPER
1185 printf("** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
1186#endif
1187
1188 for (int i = 0; i < 2; ++i) {
1189 QPathVertex *vp = vertices[i];
1190 if (vp->edge < 0) {
1191 vp->edge = ei;
1192 ep->setNext(dirs[i], ei);
1193 } else {
1194 int vi = ep->vertex(dirs[i]);
1195 Q_ASSERT(vertex(vi) == vertices[i]);
1196
1197 TraversalStatus os = findInsertStatus(vi, ei);
1198 QPathEdge *op = edge(os.edge);
1199
1200 Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
1201
1202 TraversalStatus ns = next(os);
1203 ns.flipDirection();
1204 QPathEdge *np = edge(ns.edge);
1205
1206 op->setNext(os.traversal, os.direction, ei);
1207 np->setNext(ns.traversal, ns.direction, ei);
1208
1209 int oe = os.edge;
1210 int ne = ns.edge;
1211
1212 os = next(os);
1213 ns = next(ns);
1214
1215 os.flipDirection();
1216 ns.flipDirection();
1217
1218 Q_ASSERT(os.edge == ei);
1219 Q_ASSERT(ns.edge == ei);
1220
1221 ep->setNext(os.traversal, os.direction, oe);
1222 ep->setNext(ns.traversal, ns.direction, ne);
1223 }
1224 }
1225
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);
1230
1231 return ei;
1232}
1233
1234int QWingedEdge::insert(const QPathVertex &vertex)
1235{
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;
1240
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)) {
1244 return i;
1245 }
1246 }
1247 }
1248
1249 m_vertices << vertex;
1250 return m_vertices.size() - 1;
1251}
1252
1253static void addLineTo(QPainterPath &path, const QPointF &point)
1254{
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;
1262
1263 const QPointF p(-d1.y(), d1.x());
1264
1265 if (qFuzzyIsNull(dot(p, d2))) {
1266 path.setElementPositionAt(elementCount - 1, point.x(), point.y());
1267 return;
1268 }
1269 }
1270 }
1271
1272 path.lineTo(point);
1273}
1274
1275static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
1276{
1277 QWingedEdge::TraversalStatus status;
1278 status.edge = edge;
1279 status.traversal = traversal;
1280 status.direction = QPathEdge::Forward;
1281
1282 path.moveTo(*list.vertex(list.edge(edge)->first));
1283
1284 do {
1285 const QPathEdge *ep = list.edge(status.edge);
1286
1287 addLineTo(path, *list.vertex(ep->vertex(status.direction)));
1288
1289 if (status.traversal == QPathEdge::LeftTraversal)
1290 ep->flag &= ~16;
1291 else
1292 ep->flag &= ~32;
1293
1294 status = list.next(status);
1295 } while (status.edge != edge);
1296}
1297
1298void QWingedEdge::simplify()
1299{
1300 for (int i = 0; i < edgeCount(); ++i) {
1301 const QPathEdge *ep = edge(i);
1302
1303 // if both sides are part of the inside then we can collapse the edge
1304 int flag = 0x3 << 4;
1305 if ((ep->flag & flag) == flag) {
1306 removeEdge(i);
1307
1308 ep->flag &= ~flag;
1309 }
1310 }
1311}
1312
1313QPainterPath QWingedEdge::toPath() const
1314{
1315 QPainterPath path;
1316
1317 for (int i = 0; i < edgeCount(); ++i) {
1318 const QPathEdge *ep = edge(i);
1319
1320 if (ep->flag & 16) {
1321 add(path, *this, i, QPathEdge::LeftTraversal);
1322 }
1323
1324 if (ep->flag & 32)
1325 add(path, *this, i, QPathEdge::RightTraversal);
1326 }
1327
1328 return path;
1329}
1330
1331bool QPathClipper::intersect()
1332{
1333 if (subjectPath == clipPath)
1334 return true;
1335
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())) {
1340 // no way we could intersect
1341 return false;
1342 }
1343
1344 bool subjectIsRect = pathToRect(subjectPath);
1345 bool clipIsRect = pathToRect(clipPath);
1346
1347 if (subjectIsRect && clipIsRect)
1348 return true;
1349 else if (subjectIsRect)
1350 return clipPath.intersects(r1);
1351 else if (clipIsRect)
1352 return subjectPath.intersects(r2);
1353
1354 QPathSegments a(subjectPath.elementCount());
1355 a.setPath(subjectPath);
1356 QPathSegments b(clipPath.elementCount());
1357 b.setPath(clipPath);
1358
1359 QIntersectionFinder finder;
1360 if (finder.hasIntersections(a, b))
1361 return true;
1362
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))
1367 return true;
1368 }
1369 }
1370
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))
1375 return true;
1376 }
1377 }
1378
1379 return false;
1380}
1381
1382bool QPathClipper::contains()
1383{
1384 if (subjectPath == clipPath)
1385 return false;
1386
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())) {
1391 // no intersection -> not contained
1392 return false;
1393 }
1394
1395 bool clipIsRect = pathToRect(clipPath);
1396 if (clipIsRect)
1397 return subjectPath.contains(r2);
1398
1399 QPathSegments a(subjectPath.elementCount());
1400 a.setPath(subjectPath);
1401 QPathSegments b(clipPath.elementCount());
1402 b.setPath(clipPath);
1403
1404 QIntersectionFinder finder;
1405 if (finder.hasIntersections(a, b))
1406 return false;
1407
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))
1412 return false;
1413 }
1414 }
1415
1416 return true;
1417}
1418
1419QPathClipper::QPathClipper(const QPainterPath &subject,
1420 const QPainterPath &clip)
1421 : subjectPath(subject)
1422 , clipPath(clip)
1423{
1424 aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1425 bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
1426}
1427
1428static void clear(QWingedEdge& list, int edge, QPathEdge::Traversal traversal)
1429{
1430 QWingedEdge::TraversalStatus status;
1431 status.edge = edge;
1432 status.traversal = traversal;
1433 status.direction = QPathEdge::Forward;
1434
1435 do {
1436 if (status.traversal == QPathEdge::LeftTraversal)
1437 list.edge(status.edge)->flag |= 1;
1438 else
1439 list.edge(status.edge)->flag |= 2;
1440
1441 status = list.next(status);
1442 } while (status.edge != edge);
1443}
1444
1445template <typename InputIterator>
1446InputIterator qFuzzyFind(InputIterator first, InputIterator last, qreal val)
1447{
1448 while (first != last && !QT_PREPEND_NAMESPACE(qFuzzyCompare)(qreal(*first), qreal(val)))
1449 ++first;
1450 return first;
1451}
1452
1453static bool fuzzyCompare(qreal a, qreal b)
1454{
1455 return qFuzzyCompare(a, b);
1456}
1457
1458bool QPathClipper::pathToRect(const QPainterPath &path, QRectF *rect)
1459{
1460 if (path.elementCount() != 5)
1461 return false;
1462
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();
1468
1469 if (!mightBeRect)
1470 return false;
1471
1472 const qreal x1 = path.elementAt(0).x;
1473 const qreal y1 = path.elementAt(0).y;
1474
1475 const qreal x2 = path.elementAt(1).x;
1476 const qreal y2 = path.elementAt(2).y;
1477
1478 if (path.elementAt(1).y != y1)
1479 return false;
1480
1481 if (path.elementAt(2).x != x2)
1482 return false;
1483
1484 if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
1485 return false;
1486
1487 if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
1488 return false;
1489
1490 if (rect)
1491 rect->setCoords(x1, y1, x2, y2);
1492
1493 return true;
1494}
1495
1496
1497QPainterPath QPathClipper::clip(Operation operation)
1498{
1499 op = operation;
1500
1501 if (op != Simplify) {
1502 if (subjectPath == clipPath)
1503 return op == BoolSub ? QPainterPath() : subjectPath;
1504
1505 bool subjectIsRect = pathToRect(subjectPath, nullptr);
1506 bool clipIsRect = pathToRect(clipPath, nullptr);
1507
1508 const QRectF clipBounds = clipPath.boundingRect();
1509 const QRectF subjectBounds = subjectPath.boundingRect();
1510
1511 if (!clipBounds.intersects(subjectBounds)) {
1512 switch (op) {
1513 case BoolSub:
1514 return subjectPath;
1515 case BoolAnd:
1516 return QPainterPath();
1517 case BoolOr: {
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);
1524 } else {
1525 result.addPath(clipPath.simplified());
1526 }
1527 return result;
1528 }
1529 default:
1530 break;
1531 }
1532 }
1533
1534 if (clipBounds.contains(subjectBounds)) {
1535 if (clipIsRect) {
1536 switch (op) {
1537 case BoolSub:
1538 return QPainterPath();
1539 case BoolAnd:
1540 return subjectPath;
1541 case BoolOr:
1542 return clipPath;
1543 default:
1544 break;
1545 }
1546 }
1547 } else if (subjectBounds.contains(clipBounds)) {
1548 if (subjectIsRect) {
1549 switch (op) {
1550 case BoolSub:
1551 if (clipPath.fillRule() == Qt::OddEvenFill) {
1552 QPainterPath result = clipPath;
1553 result.addRect(subjectBounds);
1554 return result;
1555 } else {
1556 QPainterPath result = clipPath.simplified();
1557 result.addRect(subjectBounds);
1558 return result;
1559 }
1560 case BoolAnd:
1561 return clipPath;
1562 case BoolOr:
1563 return subjectPath;
1564 default:
1565 break;
1566 }
1567 }
1568 }
1569
1570 if (op == BoolAnd) {
1571 if (subjectIsRect)
1572 return intersect(clipPath, subjectBounds);
1573 else if (clipIsRect)
1574 return intersect(subjectPath, clipBounds);
1575 }
1576 }
1577
1578 QWingedEdge list(subjectPath, clipPath);
1579
1580 doClip(list, ClipMode);
1581
1582 QPainterPath path = list.toPath();
1583 return path;
1584}
1585
1586bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
1587{
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;
1592
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());
1595
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));
1600 }
1601#endif
1602
1603 bool found;
1604 do {
1605 found = false;
1606 int index = 0;
1607 qreal maxHeight = 0;
1608 for (int i = 0; i < list.edgeCount(); ++i) {
1609 QPathEdge *edge = list.edge(i);
1610
1611 // have both sides of this edge already been handled?
1612 if ((edge->flag & 0x3) == 0x3)
1613 continue;
1614
1615 QPathVertex *a = list.vertex(edge->first);
1616 QPathVertex *b = list.vertex(edge->second);
1617
1618 if (qFuzzyCompare(a->y, b->y))
1619 continue;
1620
1621 found = true;
1622
1623 qreal height = qAbs(a->y - b->y);
1624 if (height > maxHeight) {
1625 index = i;
1626 maxHeight = height;
1627 }
1628 }
1629
1630 if (found) {
1631 QPathEdge *edge = list.edge(index);
1632
1633 QPathVertex *a = list.vertex(edge->first);
1634 QPathVertex *b = list.vertex(edge->second);
1635
1636 // FIXME: this can be optimized by using binary search
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();
1639
1640 Q_ASSERT(first < y_coords.size() - 1);
1641 Q_ASSERT(last < y_coords.size());
1642
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);
1647
1648 if (gap > biggestGap) {
1649 bestIdx = i;
1650 biggestGap = gap;
1651 }
1652 }
1653 const qreal bestY = 0.5 * (y_coords.at(bestIdx) + y_coords.at(bestIdx + 1));
1654
1655#ifdef QDEBUG_CLIPPER
1656 printf("y: %.9f, gap: %.9f\n", bestY, biggestGap);
1657#endif
1658
1659 if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
1660 return true;
1661
1662 edge->flag |= 0x3;
1663 }
1664 } while (found);
1665
1666 if (mode == ClipMode)
1667 list.simplify();
1668
1669 return false;
1670}
1671
1672static void traverse(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
1673{
1674 QWingedEdge::TraversalStatus status;
1675 status.edge = edge;
1676 status.traversal = traversal;
1677 status.direction = QPathEdge::Forward;
1678
1679 do {
1680 int flag = status.traversal == QPathEdge::LeftTraversal ? 1 : 2;
1681
1682 QPathEdge *ep = list.edge(status.edge);
1683
1684 ep->flag |= (flag | (flag << 4));
1685
1686#ifdef QDEBUG_CLIPPER
1687 qDebug() << "traverse: adding edge " << status.edge << ", mask:" << (flag << 4) <<ep->flag;
1688#endif
1689
1690 status = list.next(status);
1691 } while (status.edge != edge);
1692}
1693
1695{
1696 int edge;
1698
1699 bool operator<(const QCrossingEdge &edge) const
1700 {
1701 return x < edge.x;
1702 }
1703};
1705
1706static bool bool_op(bool a, bool b, QPathClipper::Operation op)
1707{
1708 switch (op) {
1709 case QPathClipper::BoolAnd:
1710 return a && b;
1711 case QPathClipper::BoolOr:
1712 case QPathClipper::Simplify:
1713 return a || b;
1714 case QPathClipper::BoolSub:
1715 return a && !b;
1716 default:
1717 Q_ASSERT(false);
1718 return false;
1719 }
1720}
1721
1722bool QWingedEdge::isInside(qreal x, qreal y) const
1723{
1724 int winding = 0;
1725 for (int i = 0; i < edgeCount(); ++i) {
1726 const QPathEdge *ep = edge(i);
1727
1728 // left xor right
1729 int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
1730
1731 if (!w)
1732 continue;
1733
1734 QPointF a = *vertex(ep->first);
1735 QPointF b = *vertex(ep->second);
1736
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());
1739
1740 if (intersectionX > x)
1741 winding += w;
1742 }
1743 }
1744
1745 return winding & 1;
1746}
1747
1748static QList<QCrossingEdge> findCrossings(const QWingedEdge &list, qreal y)
1749{
1750 QList<QCrossingEdge> crossings;
1751 for (int i = 0; i < list.edgeCount(); ++i) {
1752 const QPathEdge *edge = list.edge(i);
1753 QPointF a = *list.vertex(edge->first);
1754 QPointF b = *list.vertex(edge->second);
1755
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());
1758 const QCrossingEdge edge = { i, intersection };
1759 crossings << edge;
1760 }
1761 }
1762 return crossings;
1763}
1764
1765bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
1766{
1767 QList<QCrossingEdge> crossings = findCrossings(list, y);
1768
1769 Q_ASSERT(!crossings.isEmpty());
1770 std::sort(crossings.begin(), crossings.end());
1771
1772 int windingA = 0;
1773 int windingB = 0;
1774
1775 int windingD = 0;
1776
1777#ifdef QDEBUG_CLIPPER
1778 qDebug() << "crossings:" << crossings.size();
1779#endif
1780 for (int i = 0; i < crossings.size() - 1; ++i) {
1781 int ei = crossings.at(i).edge;
1782 const QPathEdge *edge = list.edge(ei);
1783
1784 windingA += edge->windingA;
1785 windingB += edge->windingB;
1786
1787 const bool hasLeft = (edge->flag >> 4) & 1;
1788 const bool hasRight = (edge->flag >> 4) & 2;
1789
1790 windingD += hasLeft ^ hasRight;
1791
1792 const bool inA = (windingA & aMask) != 0;
1793 const bool inB = (windingB & bMask) != 0;
1794 const bool inD = (windingD & 0x1) != 0;
1795
1796 const bool inside = bool_op(inA, inB, op);
1797 const bool add = inD ^ inside;
1798
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);
1801#endif
1802
1803 if (add) {
1804 if (mode == CheckMode)
1805 return true;
1806
1807 qreal y0 = list.vertex(edge->first)->y;
1808 qreal y1 = list.vertex(edge->second)->y;
1809
1810 if (y0 < y1) {
1811 if (!(edge->flag & 1))
1812 traverse(list, ei, QPathEdge::LeftTraversal);
1813
1814 if (!(edge->flag & 2))
1815 clear(list, ei, QPathEdge::RightTraversal);
1816 } else {
1817 if (!(edge->flag & 1))
1818 clear(list, ei, QPathEdge::LeftTraversal);
1819
1820 if (!(edge->flag & 2))
1821 traverse(list, ei, QPathEdge::RightTraversal);
1822 }
1823
1824 ++windingD;
1825 } else {
1826 if (!(edge->flag & 1))
1827 clear(list, ei, QPathEdge::LeftTraversal);
1828
1829 if (!(edge->flag & 2))
1830 clear(list, ei, QPathEdge::RightTraversal);
1831 }
1832 }
1833
1834 return false;
1835}
1836
1837namespace {
1838
1839QList<QPainterPath> toSubpaths(const QPainterPath &path)
1840{
1841
1842 QList<QPainterPath> subpaths;
1843 if (path.isEmpty())
1844 return subpaths;
1845
1846 QPainterPath current;
1847 for (int i = 0; i < path.elementCount(); ++i) {
1848 const QPainterPath::Element &e = path.elementAt(i);
1849 switch (e.type) {
1850 case QPainterPath::MoveToElement:
1851 if (current.elementCount() > 1)
1852 subpaths += current;
1853 current = QPainterPath();
1854 current.moveTo(e);
1855 break;
1856 case QPainterPath::LineToElement:
1857 current.lineTo(e);
1858 break;
1859 case QPainterPath::CurveToElement: {
1860 current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2));
1861 i+=2;
1862 break;
1863 }
1864 case QPainterPath::CurveToDataElement:
1865 Q_ASSERT(!"toSubpaths(), bad element type");
1866 break;
1867 }
1868 }
1869
1870 if (current.elementCount() > 1)
1871 subpaths << current;
1872
1873 return subpaths;
1874}
1875
1876enum Edge
1877{
1878 Left, Top, Right, Bottom
1879};
1880
1881static bool isVertical(Edge edge)
1882{
1883 return edge == Left || edge == Right;
1884}
1885
1886template <Edge edge>
1887bool compare(const QPointF &p, qreal t)
1888{
1889 switch (edge)
1890 {
1891 case Left:
1892 return p.x() < t;
1893 case Right:
1894 return p.x() > t;
1895 case Top:
1896 return p.y() < t;
1897 default:
1898 return p.y() > t;
1899 }
1900}
1901
1902template <Edge edge>
1903QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t)
1904{
1905 QLineF line(a, b);
1906 switch (edge) {
1907 case Left:
1908 case Right:
1909 return line.pointAt((t - a.x()) / (b.x() - a.x()));
1910 default:
1911 return line.pointAt((t - a.y()) / (b.y() - a.y()));
1912 }
1913}
1914
1915void addLine(QPainterPath &path, const QLineF &line)
1916{
1917 if (path.elementCount() > 0)
1918 path.lineTo(line.p1());
1919 else
1920 path.moveTo(line.p1());
1921
1922 path.lineTo(line.p2());
1923}
1924
1925template <Edge edge>
1926void clipLine(const QPointF &a, const QPointF &b, qreal t, QPainterPath &result)
1927{
1928 bool outA = compare<edge>(a, t);
1929 bool outB = compare<edge>(b, t);
1930 if (outA && outB)
1931 return;
1932
1933 if (outA)
1934 addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
1935 else if (outB)
1936 addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
1937 else
1938 addLine(result, QLineF(a, b));
1939}
1940
1941void addBezier(QPainterPath &path, const QBezier &bezier)
1942{
1943 if (path.elementCount() > 0)
1944 path.lineTo(bezier.pt1());
1945 else
1946 path.moveTo(bezier.pt1());
1947
1948 path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4());
1949}
1950
1951template <Edge edge>
1952void clipBezier(const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, qreal t, QPainterPath &result)
1953{
1954 QBezier bezier = QBezier::fromPoints(a, b, c, d);
1955
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);
1960
1961 int outCount = int(outA) + int(outB) + int(outC) + int(outD);
1962
1963 if (outCount == 4)
1964 return;
1965
1966 if (outCount == 0) {
1967 addBezier(result, bezier);
1968 return;
1969 }
1970
1971 QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform();
1972 QBezier unflipped = bezier;
1973 QBezier flipped = bezier.mapBy(flip);
1974
1975 qreal t0 = 0, t1 = 1;
1976 int stationary = flipped.stationaryYPoints(t0, t1);
1977
1978 qreal segments[4];
1979 QPointF points[4];
1980 points[0] = unflipped.pt1();
1981 segments[0] = 0;
1982
1983 int segmentCount = 0;
1984 if (stationary > 0) {
1985 ++segmentCount;
1986 segments[segmentCount] = t0;
1987 points[segmentCount] = unflipped.pointAt(t0);
1988 }
1989 if (stationary > 1) {
1990 ++segmentCount;
1991 segments[segmentCount] = t1;
1992 points[segmentCount] = unflipped.pointAt(t1);
1993 }
1994 ++segmentCount;
1995 segments[segmentCount] = 1;
1996 points[segmentCount] = unflipped.pt4();
1997
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);
2002
2003 if (outA != outB) {
2004 qreal intersection = flipped.tForY(segments[i], segments[i+1], t);
2005
2006 if (outB)
2007 addBezier(result, unflipped.getSubRange(lastIntersection, intersection));
2008
2009 lastIntersection = intersection;
2010 }
2011 }
2012
2013 if (!outB)
2014 addBezier(result, unflipped.getSubRange(lastIntersection, 1));
2015}
2016
2017// clips a single subpath against a single edge
2018template <Edge edge>
2019QPainterPath clip(const QPainterPath &path, qreal t)
2020{
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);
2027 } else {
2028 clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
2029 i += 2;
2030 }
2031 }
2032
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);
2036
2037 return result;
2038}
2039
2040QPainterPath intersectPath(const QPainterPath &path, const QRectF &rect)
2041{
2042 QList<QPainterPath> subpaths = toSubpaths(path);
2043
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());
2054
2055 bounds = subPath.boundingRect();
2056
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());
2061
2062 if (subPath.elementCount() > 1)
2063 result.addPath(subPath);
2064 }
2065 }
2066 // The algorithm above might return one side of \a rect if there was no intersection,
2067 // so only return intersections that are not empty rectangles.
2068 if (result.boundingRect().isEmpty())
2069 return QPainterPath();
2070 else
2071 return result;
2072}
2073
2074}
2075
2076QPainterPath QPathClipper::intersect(const QPainterPath &path, const QRectF &rect)
2077{
2078 return intersectPath(path, rect);
2079}
2080
2081QT_END_NAMESPACE
bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const
void produceIntersections(QPathSegments &segments)
QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
int result() const
QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
Node * rootNode()
QKdPointTree(const QPathSegments &segments)
int build(int begin, int end, int depth=0)
int vertex(Direction direction) const
int points() const
int segments() 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