source: trunk/src/gui/painting/qpathclipper.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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