1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
41 #include "private/qpathclipper_p.h"
43 #include "pathcompare.h"
45 #include <QtTest/QtTest>
47 #include <qpainterpath.h>
54 class tst_QPathClipper : public QObject
60 virtual ~tst_QPathClipper();
63 void clipTest(int subjectIndex, int clipIndex, QPathClipper::Operation op);
65 QList<QPainterPath> paths;
71 void testWingedEdge();
73 void testComparePaths();
81 void testIntersections();
82 void testIntersections2();
83 void testIntersections3();
84 void testIntersections4();
85 void testIntersections5();
86 void testIntersections6();
87 void testIntersections7();
88 void testIntersections8();
89 void testIntersections9();
91 void zeroDerivativeCurves();
93 void task204301_data();
102 Q_DECLARE_METATYPE(QPainterPath)
103 Q_DECLARE_METATYPE(QPathClipper::Operation)
105 tst_QPathClipper::tst_QPathClipper()
109 tst_QPathClipper::~tst_QPathClipper()
113 void tst_QPathClipper::initTestCase()
115 paths << Paths::rect();
116 paths << Paths::heart();
117 paths << Paths::body();
118 paths << Paths::mailbox();
119 paths << Paths::deer();
120 paths << Paths::fire();
122 paths << Paths::random1();
123 paths << Paths::random2();
125 paths << Paths::heart2();
126 paths << Paths::rect2();
127 paths << Paths::rect3();
128 paths << Paths::rect4();
129 paths << Paths::rect5();
130 paths << Paths::rect6();
132 paths << Paths::frame1();
133 paths << Paths::frame2();
134 paths << Paths::frame3();
135 paths << Paths::frame4();
137 paths << Paths::triangle1();
138 paths << Paths::triangle2();
140 paths << Paths::node();
141 paths << Paths::interRect();
143 paths << Paths::simpleCurve();
144 paths << Paths::simpleCurve2();
145 paths << Paths::simpleCurve3();
147 paths << Paths::bezier1();
148 paths << Paths::bezier2();
149 paths << Paths::bezier3();
150 paths << Paths::bezier4();
152 paths << Paths::bezierFlower();
153 paths << Paths::lips();
154 paths << Paths::clover();
155 paths << Paths::ellipses();
156 paths << Paths::windingFill();
157 paths << Paths::oddEvenFill();
158 paths << Paths::squareWithHole();
159 paths << Paths::circleWithHole();
160 paths << Paths::bezierQuadrant();
162 // make sure all the bounding rects are centered at the origin
163 for (int i = 0; i < paths.size(); ++i) {
164 QRectF bounds = paths[i].boundingRect();
168 -bounds.center().x(), -bounds.center().y());
170 paths[i] = m.map(paths[i]);
174 static QPainterPath samplePath1()
177 path.moveTo(QPointF(200, 246.64789));
178 path.lineTo(QPointF(200, 206.64789));
179 path.lineTo(QPointF(231.42858, 206.64789));
180 path.lineTo(QPointF(231.42858, 246.64789));
181 path.lineTo(QPointF(200, 246.64789));
185 static QPainterPath samplePath2()
188 path.moveTo(QPointF(200, 146.64789));
189 path.lineTo(QPointF(200, 106.64789));
190 path.lineTo(QPointF(231.42858, 106.64789));
191 path.lineTo(QPointF(231.42858, 146.64789));
192 path.lineTo(QPointF(200, 146.64789));
196 static QPainterPath samplePath3()
199 path.moveTo(QPointF(231.42858, 80.933609));
200 path.lineTo(QPointF(200, 80.933609));
201 path.lineTo(QPointF(200, 96.64788999999999));
202 path.lineTo(QPointF(231.42858, 96.64788999999999));
203 path.lineTo(QPointF(231.42858, 80.933609));
207 static QPainterPath samplePath4()
210 path.moveTo(QPointF(288.571434, 80.933609));
211 path.lineTo(QPointF(431.42858, 80.933609));
212 path.lineTo(QPointF(431.42858, 96.64788999999999));
213 path.lineTo(QPointF(288.571434, 96.64788999999999));
214 path.lineTo(QPointF(288.571434, 80.933609));
218 static QPainterPath samplePath5()
221 path.moveTo(QPointF(588.571434, 80.933609));
222 path.lineTo(QPointF(682.85715, 80.933609));
223 path.lineTo(QPointF(682.85715, 96.64788999999999));
224 path.lineTo(QPointF(588.571434, 96.64788999999999));
225 path.lineTo(QPointF(588.571434, 80.933609));
229 static QPainterPath samplePath6()
232 path.moveTo(QPointF(588.571434, 80.933609));
233 path.lineTo(QPointF(200, 80.933609));
234 path.lineTo(QPointF(200, 446.6479));
235 path.lineTo(QPointF(682.85715, 446.6479));
236 path.lineTo(QPointF(682.85715, 96.64788999999999));
237 path.lineTo(QPointF(731.42858, 96.64788999999999));
238 path.lineTo(QPointF(731.42858, 56.64788999999999));
239 path.lineTo(QPointF(588.571434, 56.64788999999999));
240 path.lineTo(QPointF(588.571434, 80.933609));
244 static QPainterPath samplePath7()
247 path.moveTo(QPointF(682.85715, 206.64789));
248 path.lineTo(QPointF(682.85715, 246.64789));
249 path.lineTo(QPointF(588.571434, 246.64789));
250 path.lineTo(QPointF(588.571434, 206.64789));
251 path.lineTo(QPointF(682.85715, 206.64789));
255 static QPainterPath samplePath8()
258 path.moveTo(QPointF(682.85715, 406.64789));
259 path.lineTo(QPointF(682.85715, 446.64789));
260 path.lineTo(QPointF(588.571434, 446.64789));
261 path.lineTo(QPointF(588.571434, 406.64789));
262 path.lineTo(QPointF(682.85715, 406.64789));
266 static QPainterPath samplePath9()
269 path.moveTo(QPointF(682.85715, 426.64789));
270 path.lineTo(QPointF(682.85715, 446.6479));
271 path.lineTo(QPointF(568.571434, 446.6479));
272 path.lineTo(QPointF(568.571434, 426.64789));
273 path.lineTo(QPointF(682.85715, 426.64789));
277 static QPainterPath samplePath10()
280 path.moveTo(QPointF(511.42858, 446.6479));
281 path.lineTo(QPointF(368.571434, 446.6479));
282 path.lineTo(QPointF(368.571434, 426.64789));
283 path.lineTo(QPointF(511.42858, 426.64789));
284 path.lineTo(QPointF(511.42858, 446.6479));
288 static QPainterPath samplePath13()
291 path.moveTo(QPointF(160, 200));
292 path.lineTo(QPointF(100, 200));
293 path.lineTo(QPointF(100, 130));
294 path.lineTo(QPointF(160, 130));
295 path.lineTo(QPointF(160, 200));
299 static QPainterPath samplePath14()
303 path.moveTo(160, 80);
304 path.lineTo(160, 180);
305 path.lineTo(100, 180);
306 path.lineTo(100, 80);
307 path.lineTo(160, 80);
308 path.moveTo(160, 80);
309 path.lineTo(160, 100);
310 path.lineTo(120, 100);
311 path.lineTo(120, 80);
316 void tst_QPathClipper::clip_data()
318 //create the testtable instance and define the elements
319 QTest::addColumn<QPainterPath>("subject");
320 QTest::addColumn<QPainterPath>("clip");
321 QTest::addColumn<QPathClipper::Operation>("op");
322 QTest::addColumn<QPainterPath>("result");
324 //next we fill it with data
325 QTest::newRow( "simple1" ) << Paths::frame3()
327 << QPathClipper::BoolAnd
330 QTest::newRow( "simple2" ) << Paths::frame3()
331 << Paths::frame4() * QTransform().translate(0, -100)
332 << QPathClipper::BoolAnd
335 QTest::newRow( "simple3" ) << Paths::frame3()
336 << Paths::frame4() * QTransform().translate(0, -150)
337 << QPathClipper::BoolAnd
340 QTest::newRow( "simple4" ) << Paths::frame3()
341 << Paths::frame4() * QTransform().translate(200, -150)
342 << QPathClipper::BoolAnd
345 QTest::newRow( "simple5" ) << Paths::frame3()
346 << Paths::frame4() * QTransform().translate(500, -150)
347 << QPathClipper::BoolAnd
350 QTest::newRow( "simple6" ) << Paths::frame3()
351 << Paths::frame4() * QTransform().translate(500, -150)
352 << QPathClipper::BoolOr
355 QTest::newRow( "simple7" ) << Paths::frame3()
356 << Paths::frame4() * QTransform().translate(500, 0)
357 << QPathClipper::BoolAnd
360 QTest::newRow( "simple8" ) << Paths::frame3()
361 << Paths::frame4() * QTransform().translate(500, 200)
362 << QPathClipper::BoolAnd
365 QTest::newRow( "simple9" ) << Paths::frame3()
366 << Paths::frame4() * QTransform().translate(480, 220)
367 << QPathClipper::BoolAnd
370 QTest::newRow( "simple10" ) << Paths::frame3()
371 << Paths::frame4() * QTransform().translate(280, 220)
372 << QPathClipper::BoolAnd
375 QTest::newRow( "simple_move_to1" ) << Paths::rect4()
376 << Paths::rect2() * QTransform().translate(-20, 50)
377 << QPathClipper::BoolAnd
380 QTest::newRow( "simple_move_to2" ) << Paths::rect4()
381 << Paths::rect2() * QTransform().translate(-20, 0)
382 << QPathClipper::BoolAnd
386 // sanity check to make sure comparePaths declared above works
387 void tst_QPathClipper::testComparePaths()
392 a.addRect(0, 0, 10, 10);
393 b.addRect(0, 0, 10.00001, 10.00001);
395 QVERIFY(!QPathCompare::comparePaths(a, b));
398 b.addRect(0, 0, 10.00000000001, 10.00000000001);
400 QVERIFY(QPathCompare::comparePaths(a, b));
408 QVERIFY(QPathCompare::comparePaths(a, b));
410 QVERIFY(QPathCompare::comparePaths(a, b));
418 QVERIFY(!QPathCompare::comparePaths(a, b));
421 void tst_QPathClipper::clip()
423 if (sizeof(double) != sizeof(qreal)) {
424 QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
426 QFETCH( QPainterPath, subject );
427 QFETCH( QPainterPath, clip );
428 QFETCH( QPathClipper::Operation, op );
429 QFETCH( QPainterPath, result);
430 QPathClipper clipper(subject, clip);
431 QPainterPath x = clipper.clip(op);
433 QVERIFY(QPathCompare::comparePaths(x, result));
436 static inline QPointF randomPointInRect(const QRectF &rect)
438 qreal rx = qrand() / (RAND_MAX + 1.);
439 qreal ry = qrand() / (RAND_MAX + 1.);
441 return QPointF(rect.left() + rx * rect.width(),
442 rect.top() + ry * rect.height());
445 void tst_QPathClipper::clipTest(int subjectIndex, int clipIndex, QPathClipper::Operation op)
447 const QPainterPath &subject = paths[subjectIndex];
448 const QPainterPath &clip = paths[clipIndex];
449 const int count = 40;
451 QRectF bounds = subject.boundingRect().united(clip.boundingRect());
453 const qreal adjustX = bounds.width() * 0.01;
454 const qreal adjustY = bounds.height() * 0.01;
456 // make sure we test some points that are outside both paths as well
457 bounds = bounds.adjusted(-adjustX, -adjustY, adjustX, adjustY);
460 const qreal scale = qMin(dim / bounds.width(), dim / bounds.height());
462 QPathClipper clipper(subject, clip);
463 QPainterPath result = clipper.clip(op);
465 // using the image here is a bit of a hacky way to make sure we don't test points that
466 // are too close to the path edges to avoid test fails that are due to numerical errors
467 QImage img(dim, dim, QImage::Format_ARGB32_Premultiplied);
470 p.setRenderHint(QPainter::Antialiasing);
471 p.scale(scale, scale);
472 p.translate(-bounds.topLeft());
473 p.setPen(QPen(Qt::black, 0));
475 p.setPen(QPen(Qt::red, 0));
479 for (int i = 0; i < count; ++i) {
483 point = randomPointInRect(bounds);
484 const QPointF imagePoint = (point - bounds.topLeft()) * scale;
486 pixel = img.pixel(int(imagePoint.x()), int(imagePoint.y()));
487 } while (qAlpha(pixel) > 0);
489 const bool inSubject = subject.contains(point);
490 const bool inClip = clip.contains(point);
492 const bool inResult = result.contains(point);
494 bool expected = false;
496 case QPathClipper::BoolAnd:
497 expected = inSubject && inClip;
499 case QPathClipper::BoolOr:
500 expected = inSubject || inClip;
502 case QPathClipper::BoolSub:
503 expected = inSubject && !inClip;
509 if (expected != inResult) {
512 op == QPathClipper::BoolAnd ? "and" :
513 op == QPathClipper::BoolOr ? "or" : "sub";
514 sprintf(str, "Expected: %d, actual: %d, subject: %d, clip: %d, op: %s\n",
515 int(expected), int(inResult), subjectIndex, clipIndex, opStr);
521 void tst_QPathClipper::clip2()
523 if (sizeof(double) != sizeof(qreal))
524 QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
528 for (int i = 0; i < paths.size(); ++i) {
529 for (int j = 0; j <= i; ++j) {
530 QPathClipper::Operation op = QPathClipper::Operation((operation++) % 3);
536 void tst_QPathClipper::clip3()
540 // this subset should work correctly for qreal = float
541 for (int i = 0; i < 20; ++i) {
542 for (int j = 0; j <= i; ++j) {
543 QPathClipper::Operation op = QPathClipper::Operation((operation++) % 3);
549 void tst_QPathClipper::testIntersections()
554 path1.addRect(0, 0, 100, 100);
555 path2.addRect(20, 20, 20, 20);
556 QVERIFY(path1.intersects(path2));
557 QVERIFY(path2.intersects(path1));
558 QVERIFY(path1.contains(path2));
559 QVERIFY(!path2.contains(path1));
561 path1 = QPainterPath();
562 path2 = QPainterPath();
563 path1.addEllipse(0, 0, 100, 100);
564 path2.addEllipse(200, 200, 100, 100);
565 QVERIFY(!path1.intersects(path2));
566 QVERIFY(!path2.intersects(path1));
567 QVERIFY(!path1.contains(path2));
568 QVERIFY(!path2.contains(path1));
570 path1 = QPainterPath();
571 path2 = QPainterPath();
572 path1.addEllipse(0, 0, 100, 100);
573 path2.addEllipse(50, 50, 100, 100);
574 QVERIFY(path1.intersects(path2));
575 QVERIFY(path2.intersects(path1));
576 QVERIFY(!path1.contains(path2));
577 QVERIFY(!path2.contains(path1));
579 path1 = QPainterPath();
580 path2 = QPainterPath();
581 path1.addRect(100, 100, 100, 100);
582 path2.addRect(50, 100, 100, 20);
583 QVERIFY(path1.intersects(path2));
584 QVERIFY(path2.intersects(path1));
585 QVERIFY(!path1.contains(path2));
586 QVERIFY(!path2.contains(path1));
588 path1 = QPainterPath();
589 path2 = QPainterPath();
590 path1.addRect(100, 100, 100, 100);
591 path2.addRect(110, 201, 100, 20);
592 QVERIFY(!path1.intersects(path2));
593 QVERIFY(!path2.intersects(path1));
594 QVERIFY(!path1.contains(path2));
595 QVERIFY(!path2.contains(path1));
597 path1 = QPainterPath();
598 path2 = QPainterPath();
599 path1.addRect(0, 0, 100, 100);
600 path2.addRect(20, 20, 20, 20);
601 path2.addRect(25, 25, 5, 5);
602 QVERIFY(path1.intersects(path2));
603 QVERIFY(path2.intersects(path1));
604 QVERIFY(path1.contains(path2));
605 QVERIFY(!path2.contains(path1));
608 void tst_QPathClipper::testIntersections2()
613 path1 = QPainterPath();
614 path2 = QPainterPath();
617 path1.lineTo(107,-8);
618 path1.lineTo(107,107);
619 path1.lineTo(-8,107);
623 path2.lineTo(100,100);
627 QVERIFY(path1.intersects(path2));
628 QVERIFY(path2.intersects(path1));
629 QVERIFY(path1.contains(path2));
630 QVERIFY(!path2.contains(path1));
632 path1.closeSubpath();
634 QVERIFY(path1.intersects(path2));
635 QVERIFY(path2.intersects(path1));
636 QVERIFY(path1.contains(path2));
637 QVERIFY(!path2.contains(path1));
640 void tst_QPathClipper::testIntersections3()
642 QPainterPath path1 = Paths::node();
643 QPainterPath path2 = Paths::interRect();
645 QVERIFY(path1.intersects(path2));
646 QVERIFY(path2.intersects(path1));
649 void tst_QPathClipper::testIntersections4()
660 QVERIFY(path1.intersects(path2));
661 QVERIFY(path2.intersects(path1));
664 void tst_QPathClipper::testIntersections5()
669 path1.addRect(0, 0, 4, 4);
670 path1.addRect(2, 1, 1, 1);
671 path2.addRect(0.5, 2, 1, 1);
673 QVERIFY(path1.intersects(path2));
674 QVERIFY(path2.intersects(path1));
677 void tst_QPathClipper::testIntersections6()
682 path1.moveTo(QPointF(-115.567, -98.3254));
683 path1.lineTo(QPointF(-45.9007, -98.3254));
684 path1.lineTo(QPointF(-45.9007, -28.6588));
685 path1.lineTo(QPointF(-115.567, -28.6588));
687 path2.moveTo(QPointF(-110, -110));
688 path2.lineTo(QPointF(110, -110));
689 path2.lineTo(QPointF(110, 110));
690 path2.lineTo(QPointF(-110, 110));
691 path2.lineTo(QPointF(-110, -110));
693 QVERIFY(path1.intersects(path2));
694 QVERIFY(path2.intersects(path1));
698 void tst_QPathClipper::testIntersections7()
703 path1.addRect(0, 0, 10, 10);
704 path2.addRect(5, 0, 10, 10);
706 QVERIFY(path1.intersects(path2));
707 QVERIFY(path2.intersects(path1));
709 path1 = QPainterPath();
710 path2 = QPainterPath();
711 path1.addRect(0, 0, 10, 10);
712 path2.addRect(0, 5, 10, 10);
714 QVERIFY(path1.intersects(path2));
715 QVERIFY(path2.intersects(path1));
717 path1 = QPainterPath();
718 path2 = QPainterPath();
719 path1.addRect(0, 0, 10, 10);
720 path2.addRect(0, 0, 10, 10);
722 QVERIFY(path1.intersects(path2));
723 QVERIFY(path2.intersects(path1));
726 path1 = QPainterPath();
727 path2 = QPainterPath();
728 path1.addRect(1, 1, 10, 10);
729 path2.addRect(5, 1, 10, 10);
731 QVERIFY(path1.intersects(path2));
732 QVERIFY(path2.intersects(path1));
734 path1 = QPainterPath();
735 path2 = QPainterPath();
736 path1.addRect(1, 1, 10, 10);
737 path2.addRect(1, 5, 10, 10);
739 QVERIFY(path1.intersects(path2));
740 QVERIFY(path2.intersects(path1));
742 path1 = QPainterPath();
743 path2 = QPainterPath();
744 path1.addRect(1, 1, 10, 10);
745 path2.addRect(1, 1, 10, 10);
747 QVERIFY(path1.intersects(path2));
748 QVERIFY(path2.intersects(path1));
750 path1 = QPainterPath();
751 path2 = QPainterPath();
752 path1.addRect(1, 1, 10, 10);
753 path2.addRect(5, 5, 10, 10);
755 QVERIFY(path1.intersects(path2));
756 QVERIFY(path2.intersects(path1));
758 path1 = QPainterPath();
759 path2 = QPainterPath();
760 path1.addRect(1, 1, 10, 10);
761 path2.addRect(9, 9, 10, 10);
763 QVERIFY(path1.intersects(path2));
764 QVERIFY(path2.intersects(path1));
766 path1 = QPainterPath();
767 path2 = QPainterPath();
768 path1.addRect(1, 1, 10, 10);
769 path2.addRect(10, 10, 10, 10);
771 QVERIFY(path1.intersects(path2));
772 QVERIFY(path2.intersects(path1));
774 path1 = QPainterPath();
775 path2 = QPainterPath();
776 path1.addRect(1, 1, 9, 9);
777 path2.addRect(11, 11, 10, 10);
779 QVERIFY(!path1.intersects(path2));
780 QVERIFY(!path2.intersects(path1));
782 path1 = QPainterPath();
783 path2 = QPainterPath();
784 path1.addRect(1, 1, 10, 10);
785 path2.addRect(12, 12, 10, 10);
787 QVERIFY(!path1.intersects(path2));
788 QVERIFY(!path2.intersects(path1));
790 path1 = QPainterPath();
791 path2 = QPainterPath();
792 path1.addRect(11, 11, 10, 10);
793 path2.addRect(12, 12, 10, 10);
795 QVERIFY(path1.intersects(path2));
796 QVERIFY(path2.intersects(path1));
798 path1 = QPainterPath();
799 path2 = QPainterPath();
800 path1.addRect(11, 11, 10, 10);
801 path2.addRect(10, 10, 10, 10);
803 QVERIFY(path1.intersects(path2));
804 QVERIFY(path2.intersects(path1));
808 void tst_QPathClipper::testIntersections8()
810 QPainterPath path1 = Paths::node() * QTransform().translate(100, 50);
811 QPainterPath path2 = Paths::node() * QTransform().translate(150, 50);;
813 QVERIFY(path1.intersects(path2));
814 QVERIFY(path2.intersects(path1));
816 path1 = Paths::node();
817 path2 = Paths::node();
819 QVERIFY(path1.intersects(path2));
820 QVERIFY(path2.intersects(path1));
822 path1 = Paths::node();
823 path2 = Paths::node() * QTransform().translate(0, 30);
825 QVERIFY(path1.intersects(path2));
826 QVERIFY(path2.intersects(path1));
828 path1 = Paths::node();
829 path2 = Paths::node() * QTransform().translate(30, 0);
831 QVERIFY(path1.intersects(path2));
832 QVERIFY(path2.intersects(path1));
834 path1 = Paths::node();
835 path2 = Paths::node() * QTransform().translate(30, 30);
837 QVERIFY(path1.intersects(path2));
838 QVERIFY(path2.intersects(path1));
840 path1 = Paths::node();
841 path2 = Paths::node() * QTransform().translate(1, 1);
843 QVERIFY(path1.intersects(path2));
844 QVERIFY(path2.intersects(path1));
848 void tst_QPathClipper::testIntersections9()
853 path1.addRect(QRectF(-1,143, 146, 106));
854 path2.addRect(QRectF(-9,145, 150, 100));
856 QVERIFY(path1.intersects(path2));
857 QVERIFY(path2.intersects(path1));
859 path1 = QPainterPath();;
860 path2 = QPainterPath();
862 path1.addRect(QRectF(-1,191, 136, 106));
863 path2.addRect(QRectF(-19,194, 150, 100));
864 QVERIFY(path1.intersects(path2));
865 QVERIFY(path2.intersects(path1));
867 path1 = QPainterPath();;
868 path2 = QPainterPath();
870 path1.moveTo(-1 , 143);
871 path1.lineTo(148 , 143);
872 path1.lineTo(148 , 250);
873 path1.lineTo(-1 , 250);
875 path2.moveTo(-5 , 146);
876 path2.lineTo(145 , 146);
877 path2.lineTo(145 , 246);
878 path2.lineTo(-5 , 246);
879 path2.lineTo(-5 , 146);
881 QVERIFY(path1.intersects(path2));
882 QVERIFY(path2.intersects(path1));
885 QPainterPath pathFromRect(qreal x, qreal y, qreal w, qreal h)
888 path.addRect(QRectF(x, y, w, h));
892 QPainterPath pathFromLine(qreal x1, qreal y1, qreal x2, qreal y2)
900 static int loopLength(const QWingedEdge &list, QWingedEdge::TraversalStatus status)
902 int start = status.edge;
907 status = list.next(status);
908 } while (status.edge != start);
913 void tst_QPathClipper::testWingedEdge()
917 int e1 = list.addEdge(QPointF(0, 0), QPointF(10, 0));
918 int e2 = list.addEdge(QPointF(0, 0), QPointF(0, 10));
919 int e3 = list.addEdge(QPointF(0, 0), QPointF(-10, 0));
920 int e4 = list.addEdge(QPointF(0, 0), QPointF(0, -10));
922 QCOMPARE(list.edgeCount(), 4);
923 QCOMPARE(list.vertexCount(), 5);
925 QWingedEdge::TraversalStatus status = { e1, QPathEdge::RightTraversal, QPathEdge::Forward };
927 status = list.next(status);
928 QCOMPARE(status.direction, QPathEdge::Backward);
929 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
930 QCOMPARE(status.edge, e1);
932 status = list.next(status);
933 QCOMPARE(status.direction, QPathEdge::Forward);
934 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
935 QCOMPARE(status.edge, e4);
937 status = list.next(status);
938 QCOMPARE(status.direction, QPathEdge::Backward);
939 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
940 QCOMPARE(status.edge, e4);
942 status = list.next(status);
943 QCOMPARE(status.direction, QPathEdge::Forward);
944 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
945 QCOMPARE(status.edge, e3);
947 status = list.next(status);
948 QCOMPARE(status.direction, QPathEdge::Backward);
949 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
950 QCOMPARE(status.edge, e3);
952 status = list.next(status);
953 QCOMPARE(status.direction, QPathEdge::Forward);
954 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
955 QCOMPARE(status.edge, e2);
957 status = list.next(status);
958 QCOMPARE(status.direction, QPathEdge::Backward);
959 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
960 QCOMPARE(status.edge, e2);
962 status = list.next(status);
963 QCOMPARE(status.direction, QPathEdge::Forward);
964 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
965 QCOMPARE(status.edge, e1);
969 int e1 = list.addEdge(QPointF(5, 0), QPointF(5, 10));
970 int e2 = list.addEdge(QPointF(5, 0), QPointF(10, 5));
971 int e3 = list.addEdge(QPointF(10, 5), QPointF(5, 10));
972 int e4 = list.addEdge(QPointF(5, 0), QPointF(0, 5));
973 int e5 = list.addEdge(QPointF(0, 5), QPointF(5, 10));
975 QCOMPARE(list.edgeCount(), 5);
976 QCOMPARE(list.vertexCount(), 4);
978 QWingedEdge::TraversalStatus status = { e1, QPathEdge::RightTraversal, QPathEdge::Forward };
980 status = list.next(status);
981 QCOMPARE(status.direction, QPathEdge::Backward);
982 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
983 QCOMPARE(status.edge, e5);
985 status = list.next(status);
986 QCOMPARE(status.direction, QPathEdge::Backward);
987 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
988 QCOMPARE(status.edge, e4);
990 status = list.next(status);
991 QCOMPARE(status.direction, QPathEdge::Forward);
992 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
993 QCOMPARE(status.edge, e1);
995 QCOMPARE(loopLength(list, status), 3);
998 QCOMPARE(status.direction, QPathEdge::Backward);
999 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1000 QCOMPARE(loopLength(list, status), 3);
1002 status = list.next(status);
1003 QCOMPARE(status.direction, QPathEdge::Forward);
1004 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1005 QCOMPARE(status.edge, e2);
1007 status = list.next(status);
1008 QCOMPARE(status.direction, QPathEdge::Forward);
1009 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1010 QCOMPARE(status.edge, e3);
1012 status = list.next(status);
1013 QCOMPARE(status.direction, QPathEdge::Backward);
1014 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1015 QCOMPARE(status.edge, e1);
1017 status = list.next(status);
1019 QCOMPARE(status.direction, QPathEdge::Backward);
1020 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1021 QCOMPARE(status.edge, e2);
1022 QCOMPARE(loopLength(list, status), 4);
1024 status = list.next(status);
1025 QCOMPARE(status.direction, QPathEdge::Forward);
1026 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1027 QCOMPARE(status.edge, e4);
1029 status = list.next(status);
1030 QCOMPARE(status.direction, QPathEdge::Forward);
1031 QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1032 QCOMPARE(status.edge, e5);
1034 status = list.next(status);
1035 QCOMPARE(status.direction, QPathEdge::Backward);
1036 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1037 QCOMPARE(status.edge, e3);
1039 status = list.next(status);
1040 QCOMPARE(status.direction, QPathEdge::Backward);
1041 QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1042 QCOMPARE(status.edge, e2);
1045 QPainterPath path = pathFromRect(0, 0, 20, 20);
1046 QWingedEdge list(path, QPainterPath());
1048 QCOMPARE(list.edgeCount(), 4);
1049 QCOMPARE(list.vertexCount(), 4);
1051 QWingedEdge::TraversalStatus status = { 0, QPathEdge::RightTraversal, QPathEdge::Forward };
1053 QPathEdge *edge = list.edge(status.edge);
1054 QCOMPARE(QPointF(*list.vertex(edge->first)), QPointF(0, 0));
1055 QCOMPARE(QPointF(*list.vertex(edge->second)), QPointF(20, 0));
1057 status = list.next(status);
1058 QCOMPARE(status.edge, 1);
1060 status = list.next(status);
1061 QCOMPARE(status.edge, 2);
1063 status = list.next(status);
1064 QCOMPARE(status.edge, 3);
1066 status = list.next(status);
1067 QCOMPARE(status.edge, 0);
1069 status.flipDirection();
1070 status = list.next(status);
1071 QCOMPARE(status.edge, 3);
1073 status = list.next(status);
1074 QCOMPARE(status.edge, 2);
1076 status = list.next(status);
1077 QCOMPARE(status.edge, 1);
1079 status = list.next(status);
1080 QCOMPARE(status.edge, 0);
1082 QWingedEdge list2(path, pathFromRect(10, 5, 20, 10));
1084 QCOMPARE(list2.edgeCount(), 12);
1085 QCOMPARE(list2.vertexCount(), 10);
1087 status.flipDirection();
1088 QCOMPARE(loopLength(list2, status), 8);
1090 status = list2.next(status);
1091 edge = list2.edge(status.edge);
1092 QCOMPARE(QPointF(*list2.vertex(edge->first)), QPointF(20, 0));
1093 QCOMPARE(QPointF(*list2.vertex(edge->second)), QPointF(20, 5));
1095 status = list2.next(status);
1096 status.flipTraversal();
1098 edge = list2.edge(status.edge);
1099 QCOMPARE(QPointF(*list2.vertex(edge->first)), QPointF(10, 5));
1100 QCOMPARE(QPointF(*list2.vertex(edge->second)), QPointF(20, 5));
1102 QCOMPARE(loopLength(list2, status), 4);
1104 status.flipDirection();
1105 status = list2.next(status);
1106 status.flipTraversal();
1108 edge = list2.edge(status.edge);
1109 QCOMPARE(QPointF(*list2.vertex(edge->first)), QPointF(20, 5));
1110 QCOMPARE(QPointF(*list2.vertex(edge->second)), QPointF(20, 15));
1112 QCOMPARE(loopLength(list2, status), 4);
1113 status = list2.next(status);
1114 status = list2.next(status);
1116 edge = list2.edge(status.edge);
1117 QCOMPARE(QPointF(*list2.vertex(edge->first)), QPointF(30, 5));
1118 QCOMPARE(QPointF(*list2.vertex(edge->second)), QPointF(30, 15));
1122 void tst_QPathClipper::zeroDerivativeCurves()
1124 // zero derivative at end
1127 a.cubicTo(100, 0, 100, 100, 100, 100);
1137 QPainterPath c = a.united(b);
1138 QVERIFY(c.contains(QPointF(25, 125)));
1139 QVERIFY(c.contains(QPointF(75, 125)));
1140 QVERIFY(c.contains(QPointF(125, 125)));
1143 // zero derivative at start
1146 a.cubicTo(100, 0, 100, 100, 100, 100);
1156 QPainterPath c = a.united(b);
1157 QVERIFY(c.contains(QPointF(25, 125)));
1158 QVERIFY(c.contains(QPointF(75, 125)));
1159 QVERIFY(c.contains(QPointF(125, 125)));
1163 static bool strictContains(const QPainterPath &a, const QPainterPath &b)
1165 return b.subtracted(a) == QPainterPath();
1168 Q_DECLARE_METATYPE(QPolygonF)
1170 void tst_QPathClipper::task204301_data()
1172 QTest::addColumn<QPolygonF>("points");
1175 QPointF a(51.09013255685567855835, 31.30814891308546066284);
1176 QPointF b(98.39898971840739250183, 11.02079074829816818237);
1177 QPointF c(91.23911846894770860672, 45.86981737054884433746);
1178 QPointF d(66.58616356085985898972, 63.10526528395712375641);
1179 QPointF e(82.08219456479714892794, 94.90238165489137145414);
1180 QPointF f(16.09013040543221251255, 105.66263409332729850121);
1181 QPointF g(10.62811442650854587555, 65.09154842235147953033);
1182 QPointF h(5.16609844751656055450, 24.52046275138854980469);
1184 v << a << b << c << d << e << f << g << h;
1185 QTest::newRow("failed_on_linux") << v;
1189 QPointF a(50.014648437500000, 24.392089843750000);
1190 QPointF b(92.836303710937500, 5.548706054687500);
1191 QPointF c(92.145690917968750, 54.390258789062500);
1192 QPointF d(65.402221679687500, 74.345092773437500);
1193 QPointF e(80.789794921787347, 124.298095703129690);
1194 QPointF f(34.961242675812954, 87.621459960852135);
1195 QPointF g(18.305969238281250, 57.426757812500000);
1196 QPointF h(1.650695800781250, 27.232055664062500);
1198 v << a << b << c << d << e << f << g << h;
1199 QTest::newRow("failed_on_windows") << v;
1203 void tst_QPathClipper::task204301()
1205 QFETCH(QPolygonF, points);
1207 QPointF a = points[0];
1208 QPointF b = points[1];
1209 QPointF c = points[2];
1210 QPointF d = points[3];
1211 QPointF e = points[4];
1212 QPointF f = points[5];
1213 QPointF g = points[6];
1214 QPointF h = points[7];
1217 subA.addPolygon(QPolygonF() << a << b << c << d);
1218 subA.closeSubpath();
1221 subB.addPolygon(QPolygonF() << f << e << d << g);
1222 subB.closeSubpath();
1225 subC.addPolygon(QPolygonF() << h << a << d << g);
1226 subC.closeSubpath();
1233 QPainterPath simplified = path.simplified();
1235 QVERIFY(strictContains(simplified, subA));
1236 QVERIFY(strictContains(simplified, subB));
1237 QVERIFY(strictContains(simplified, subC));
1240 void tst_QPathClipper::task209056()
1243 p1.moveTo( QPointF(188.506, 287.793) );
1244 p1.lineTo( QPointF(288.506, 287.793) );
1245 p1.lineTo( QPointF(288.506, 387.793) );
1246 p1.lineTo( QPointF(188.506, 387.793) );
1247 p1.lineTo( QPointF(188.506, 287.793) );
1250 p2.moveTo( QPointF(419.447, 164.383) );
1251 p2.cubicTo( QPointF(419.447, 69.5486), QPointF(419.447, 259.218),QPointF(419.447, 164.383) );
1253 p2.cubicTo( QPointF(48.9378, 259.218), QPointF(131.879, 336.097),QPointF(234.192, 336.097) );
1254 p2.cubicTo( QPointF(336.506, 336.097), QPointF(419.447, 259.218),QPointF(419.447, 164.383) );
1256 QPainterPath p3 = p1.intersected(p2);
1258 QVERIFY(p3 != QPainterPath());
1261 void tst_QPathClipper::task251909()
1270 p2.moveTo(0, 8e-14);
1271 p2.lineTo(10, -8e-14);
1275 QPainterPath result = p1.united(p2);
1277 QVERIFY(result.elementCount() <= 5);
1280 void tst_QPathClipper::qtbug3778()
1282 if (sizeof(double) != sizeof(qreal)) {
1283 QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
1286 path1.moveTo(200, 3.22409e-5);
1287 // e-5 and higher leads to a bug
1288 // Using 3.22409e-4 starts to work correctly
1290 path1.lineTo(1.07025e-13, 1450);
1291 path1.lineTo(750, 950);
1292 path1.lineTo(950, 750);
1293 path1.lineTo(200, 3.22409e-13);
1297 path2.lineTo(200, 800);
1298 path2.lineTo(600, 1500);
1299 path2.lineTo(1500, 1400);
1300 path2.lineTo(1900, 1200);
1301 path2.lineTo(2000, 1000);
1302 path2.lineTo(1400, 0);
1305 QPainterPath p12 = path1.intersected(path2);
1307 QVERIFY(p12.contains(QPointF(100, 100)));
1310 QTEST_MAIN(tst_QPathClipper)
1313 #include "tst_qpathclipper.moc"