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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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