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