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

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

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

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