Make QPen default to 1-width non-cosmetic.
[profile/ivi/qtbase.git] / tests / auto / gui / painting / qpathclipper / tst_qpathclipper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "private/qpathclipper_p.h"
42 #include "paths.h"
43 #include "pathcompare.h"
44
45 #include <QtTest/QtTest>
46
47 #include <qpainterpath.h>
48 #include <qpolygon.h>
49 #include <qdebug.h>
50 #include <qpainter.h>
51
52 #include <math.h>
53
54 class tst_QPathClipper : public QObject
55 {
56     Q_OBJECT
57
58 public:
59     tst_QPathClipper();
60     virtual ~tst_QPathClipper();
61
62 private:
63     void clipTest(int subjectIndex, int clipIndex, QPathClipper::Operation op);
64
65     QList<QPainterPath> paths;
66
67 public slots:
68     void initTestCase();
69
70 private slots:
71     void testWingedEdge();
72
73     void testComparePaths();
74
75     void clip_data();
76     void clip();
77
78     void clip2();
79     void clip3();
80
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();
90
91     void zeroDerivativeCurves();
92
93     void task204301_data();
94     void task204301();
95
96     void task209056();
97     void task251909();
98
99     void qtbug3778();
100 };
101
102 Q_DECLARE_METATYPE(QPainterPath)
103 Q_DECLARE_METATYPE(QPathClipper::Operation)
104
105 tst_QPathClipper::tst_QPathClipper()
106 {
107 }
108
109 tst_QPathClipper::~tst_QPathClipper()
110 {
111 }
112
113 void tst_QPathClipper::initTestCase()
114 {
115     paths << Paths::rect();
116     paths << Paths::heart();
117     paths << Paths::body();
118     paths << Paths::mailbox();
119     paths << Paths::deer();
120     paths << Paths::fire();
121
122     paths << Paths::random1();
123     paths << Paths::random2();
124
125     paths << Paths::heart2();
126     paths << Paths::rect2();
127     paths << Paths::rect3();
128     paths << Paths::rect4();
129     paths << Paths::rect5();
130     paths << Paths::rect6();
131
132     paths << Paths::frame1();
133     paths << Paths::frame2();
134     paths << Paths::frame3();
135     paths << Paths::frame4();
136
137     paths << Paths::triangle1();
138     paths << Paths::triangle2();
139
140     paths << Paths::node();
141     paths << Paths::interRect();
142
143     paths << Paths::simpleCurve();
144     paths << Paths::simpleCurve2();
145     paths << Paths::simpleCurve3();
146
147     paths << Paths::bezier1();
148     paths << Paths::bezier2();
149     paths << Paths::bezier3();
150     paths << Paths::bezier4();
151
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();
161
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();
165
166         QMatrix m(1, 0,
167                   0, 1,
168                   -bounds.center().x(), -bounds.center().y());
169
170         paths[i] = m.map(paths[i]);
171     }
172 }
173
174 static QPainterPath samplePath1()
175 {
176     QPainterPath path;
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));
182     return path;
183 }
184
185 static QPainterPath samplePath2()
186 {
187     QPainterPath path;
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));
193     return path;
194 }
195
196 static QPainterPath samplePath3()
197 {
198     QPainterPath path;
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));
204     return path;
205 }
206
207 static QPainterPath samplePath4()
208 {
209     QPainterPath path;
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));
215     return path;
216 }
217
218 static QPainterPath samplePath5()
219 {
220     QPainterPath path;
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));
226     return path;
227 }
228
229 static QPainterPath samplePath6()
230 {
231     QPainterPath path;
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));
241     return path;
242 }
243
244 static QPainterPath samplePath7()
245 {
246     QPainterPath path;
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));
252     return path;
253 }
254
255 static QPainterPath samplePath8()
256 {
257     QPainterPath path;
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));
263     return path;
264 }
265
266 static QPainterPath samplePath9()
267 {
268     QPainterPath path;
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));
274     return path;
275 }
276
277 static QPainterPath samplePath10()
278 {
279     QPainterPath path;
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));
285     return path;
286 }
287
288 static QPainterPath samplePath13()
289 {
290     QPainterPath path;
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));
296     return path;
297 }
298
299 static QPainterPath samplePath14()
300 {
301     QPainterPath path;
302
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);
312
313     return path;
314 }
315
316 void tst_QPathClipper::clip_data()
317 {
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");
323
324     //next we fill it with data
325     QTest::newRow( "simple1" )  << Paths::frame3()
326                                 << Paths::frame4()
327                                 << QPathClipper::BoolAnd
328                                 << samplePath1();
329
330     QTest::newRow( "simple2" )  << Paths::frame3()
331                                 << Paths::frame4() * QTransform().translate(0, -100)
332                                 << QPathClipper::BoolAnd
333                                 << samplePath2();
334
335     QTest::newRow( "simple3" )  << Paths::frame3()
336                                 << Paths::frame4() * QTransform().translate(0, -150)
337                                 << QPathClipper::BoolAnd
338                                 << samplePath3();
339
340     QTest::newRow( "simple4" )  << Paths::frame3()
341                                 << Paths::frame4() * QTransform().translate(200, -150)
342                                 << QPathClipper::BoolAnd
343                                 << samplePath4();
344
345     QTest::newRow( "simple5" )  << Paths::frame3()
346                                 << Paths::frame4() * QTransform().translate(500, -150)
347                                 << QPathClipper::BoolAnd
348                                 << samplePath5();
349
350     QTest::newRow( "simple6" )  << Paths::frame3()
351                                 << Paths::frame4() * QTransform().translate(500, -150)
352                                 << QPathClipper::BoolOr
353                                 << samplePath6();
354
355     QTest::newRow( "simple7" )  << Paths::frame3()
356                                 << Paths::frame4() * QTransform().translate(500, 0)
357                                 << QPathClipper::BoolAnd
358                                 << samplePath7();
359
360     QTest::newRow( "simple8" )  << Paths::frame3()
361                                 << Paths::frame4() * QTransform().translate(500, 200)
362                                 << QPathClipper::BoolAnd
363                                 << samplePath8();
364
365     QTest::newRow( "simple9" )  << Paths::frame3()
366                                 << Paths::frame4() * QTransform().translate(480, 220)
367                                 << QPathClipper::BoolAnd
368                                 << samplePath9();
369
370     QTest::newRow( "simple10" )  << Paths::frame3()
371                                  << Paths::frame4() * QTransform().translate(280, 220)
372                                  << QPathClipper::BoolAnd
373                                  << samplePath10();
374
375     QTest::newRow( "simple_move_to1" )  << Paths::rect4()
376                                        << Paths::rect2() * QTransform().translate(-20, 50)
377                                        << QPathClipper::BoolAnd
378                                        << samplePath13();
379
380     QTest::newRow( "simple_move_to2" )  << Paths::rect4()
381                                         << Paths::rect2() * QTransform().translate(-20, 0)
382                                         << QPathClipper::BoolAnd
383                                         << samplePath14();
384 }
385
386 // sanity check to make sure comparePaths declared above works
387 void tst_QPathClipper::testComparePaths()
388 {
389     QPainterPath a;
390     QPainterPath b;
391
392     a.addRect(0, 0, 10, 10);
393     b.addRect(0, 0, 10.00001, 10.00001);
394
395     QVERIFY(!QPathCompare::comparePaths(a, b));
396
397     b = QPainterPath();
398     b.addRect(0, 0, 10.00000000001, 10.00000000001);
399
400     QVERIFY(QPathCompare::comparePaths(a, b));
401
402     b = QPainterPath();
403     b.moveTo(10, 0);
404     b.lineTo(0, 0);
405     b.lineTo(0, 10);
406     b.lineTo(10, 10);
407
408     QVERIFY(QPathCompare::comparePaths(a, b));
409     b.lineTo(10, 0);
410     QVERIFY(QPathCompare::comparePaths(a, b));
411
412     b = QPainterPath();
413     b.moveTo(10, 0);
414     b.lineTo(0, 10);
415     b.lineTo(0, 0);
416     b.lineTo(10, 10);
417
418     QVERIFY(!QPathCompare::comparePaths(a, b));
419 }
420
421 void tst_QPathClipper::clip()
422 {
423     if (sizeof(double) != sizeof(qreal)) {
424         QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
425     }
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);
432
433     QVERIFY(QPathCompare::comparePaths(x, result));
434 }
435
436 static inline QPointF randomPointInRect(const QRectF &rect)
437 {
438     qreal rx = qrand() / (RAND_MAX + 1.);
439     qreal ry = qrand() / (RAND_MAX + 1.);
440
441     return QPointF(rect.left() + rx * rect.width(),
442                    rect.top() + ry * rect.height());
443 }
444
445 void tst_QPathClipper::clipTest(int subjectIndex, int clipIndex, QPathClipper::Operation op)
446 {
447     const QPainterPath &subject = paths[subjectIndex];
448     const QPainterPath &clip = paths[clipIndex];
449     const int count = 40;
450
451     QRectF bounds = subject.boundingRect().united(clip.boundingRect());
452
453     const qreal adjustX = bounds.width() * 0.01;
454     const qreal adjustY = bounds.height() * 0.01;
455
456     // make sure we test some points that are outside both paths as well
457     bounds = bounds.adjusted(-adjustX, -adjustY, adjustX, adjustY);
458
459     const int dim = 256;
460     const qreal scale = qMin(dim / bounds.width(), dim / bounds.height());
461
462     QPathClipper clipper(subject, clip);
463     QPainterPath result = clipper.clip(op);
464
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);
468     img.fill(0x0);
469     QPainter p(&img);
470     p.setRenderHint(QPainter::Antialiasing);
471     p.scale(scale, scale);
472     p.translate(-bounds.topLeft());
473     p.setPen(QPen(Qt::black, 0));
474     p.drawPath(subject);
475     p.setPen(QPen(Qt::red, 0));
476     p.drawPath(clip);
477     p.end();
478
479     for (int i = 0; i < count; ++i) {
480         QPointF point;
481         QRgb pixel;
482         do {
483             point = randomPointInRect(bounds);
484             const QPointF imagePoint = (point - bounds.topLeft()) * scale;
485
486             pixel = img.pixel(int(imagePoint.x()), int(imagePoint.y()));
487         } while (qAlpha(pixel) > 0);
488
489         const bool inSubject = subject.contains(point);
490         const bool inClip = clip.contains(point);
491
492         const bool inResult = result.contains(point);
493
494         bool expected = false;
495         switch (op) {
496         case QPathClipper::BoolAnd:
497             expected = inSubject && inClip;
498             break;
499         case QPathClipper::BoolOr:
500             expected = inSubject || inClip;
501             break;
502         case QPathClipper::BoolSub:
503             expected = inSubject && !inClip;
504             break;
505         default:
506             break;
507         }
508
509         if (expected != inResult) {
510             char str[256];
511             const char *opStr =
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);
516             QFAIL(str);
517         }
518     }
519 }
520
521 void tst_QPathClipper::clip2()
522 {
523     if (sizeof(double) != sizeof(qreal))
524         QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
525
526     int operation = 0;
527
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);
531             clipTest(i, j, op);
532         }
533     }
534 }
535
536 void tst_QPathClipper::clip3()
537 {
538     int operation = 0;
539
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);
544             clipTest(i, j, op);
545         }
546     }
547 }
548
549 void tst_QPathClipper::testIntersections()
550 {
551     QPainterPath path1;
552     QPainterPath path2;
553
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));
560
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));
569
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));
578
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));
587
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));
596
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));
606 }
607
608 void tst_QPathClipper::testIntersections2()
609 {
610     QPainterPath path1;
611     QPainterPath path2;
612
613     path1 = QPainterPath();
614     path2 = QPainterPath();
615
616     path1.moveTo(-8,-8);
617     path1.lineTo(107,-8);
618     path1.lineTo(107,107);
619     path1.lineTo(-8,107);
620
621     path2.moveTo(0,0);
622     path2.lineTo(100,0);
623     path2.lineTo(100,100);
624     path2.lineTo(0,100);
625     path2.lineTo(0,0);
626
627     QVERIFY(path1.intersects(path2));
628     QVERIFY(path2.intersects(path1));
629     QVERIFY(path1.contains(path2));
630     QVERIFY(!path2.contains(path1));
631
632     path1.closeSubpath();
633
634     QVERIFY(path1.intersects(path2));
635     QVERIFY(path2.intersects(path1));
636     QVERIFY(path1.contains(path2));
637     QVERIFY(!path2.contains(path1));
638 }
639
640 void tst_QPathClipper::testIntersections3()
641 {
642     QPainterPath path1 = Paths::node();
643     QPainterPath path2 = Paths::interRect();
644
645     QVERIFY(path1.intersects(path2));
646     QVERIFY(path2.intersects(path1));
647 }
648
649 void tst_QPathClipper::testIntersections4()
650 {
651     QPainterPath path1;
652     QPainterPath path2;
653
654     path1.moveTo(-5, 0);
655     path1.lineTo(5, 0);
656
657     path2.moveTo(0, -5);
658     path2.lineTo(0, 5);
659
660     QVERIFY(path1.intersects(path2));
661     QVERIFY(path2.intersects(path1));
662 }
663
664 void tst_QPathClipper::testIntersections5()
665 {
666     QPainterPath path1;
667     QPainterPath path2;
668
669     path1.addRect(0, 0, 4, 4);
670     path1.addRect(2, 1, 1, 1);
671     path2.addRect(0.5, 2, 1, 1);
672
673     QVERIFY(path1.intersects(path2));
674     QVERIFY(path2.intersects(path1));
675 }
676
677 void tst_QPathClipper::testIntersections6()
678 {
679     QPainterPath path1;
680     QPainterPath path2;
681
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));
686
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));
692
693     QVERIFY(path1.intersects(path2));
694     QVERIFY(path2.intersects(path1));
695 }
696
697
698 void tst_QPathClipper::testIntersections7()
699 {
700     QPainterPath path1;
701     QPainterPath path2;
702
703     path1.addRect(0, 0, 10, 10);
704     path2.addRect(5, 0, 10, 10);
705
706     QVERIFY(path1.intersects(path2));
707     QVERIFY(path2.intersects(path1));
708
709     path1 = QPainterPath();
710     path2 = QPainterPath();
711     path1.addRect(0, 0, 10, 10);
712     path2.addRect(0, 5, 10, 10);
713
714     QVERIFY(path1.intersects(path2));
715     QVERIFY(path2.intersects(path1));
716
717     path1 = QPainterPath();
718     path2 = QPainterPath();
719     path1.addRect(0, 0, 10, 10);
720     path2.addRect(0, 0, 10, 10);
721
722     QVERIFY(path1.intersects(path2));
723     QVERIFY(path2.intersects(path1));
724
725     ///
726     path1 = QPainterPath();
727     path2 = QPainterPath();
728     path1.addRect(1, 1, 10, 10);
729     path2.addRect(5, 1, 10, 10);
730
731     QVERIFY(path1.intersects(path2));
732     QVERIFY(path2.intersects(path1));
733
734     path1 = QPainterPath();
735     path2 = QPainterPath();
736     path1.addRect(1, 1, 10, 10);
737     path2.addRect(1, 5, 10, 10);
738
739     QVERIFY(path1.intersects(path2));
740     QVERIFY(path2.intersects(path1));
741
742     path1 = QPainterPath();
743     path2 = QPainterPath();
744     path1.addRect(1, 1, 10, 10);
745     path2.addRect(1, 1, 10, 10);
746
747     QVERIFY(path1.intersects(path2));
748     QVERIFY(path2.intersects(path1));
749
750     path1 = QPainterPath();
751     path2 = QPainterPath();
752     path1.addRect(1, 1, 10, 10);
753     path2.addRect(5, 5, 10, 10);
754
755     QVERIFY(path1.intersects(path2));
756     QVERIFY(path2.intersects(path1));
757
758     path1 = QPainterPath();
759     path2 = QPainterPath();
760     path1.addRect(1, 1, 10, 10);
761     path2.addRect(9, 9, 10, 10);
762
763     QVERIFY(path1.intersects(path2));
764     QVERIFY(path2.intersects(path1));
765
766     path1 = QPainterPath();
767     path2 = QPainterPath();
768     path1.addRect(1, 1, 10, 10);
769     path2.addRect(10, 10, 10, 10);
770
771     QVERIFY(path1.intersects(path2));
772     QVERIFY(path2.intersects(path1));
773
774     path1 = QPainterPath();
775     path2 = QPainterPath();
776     path1.addRect(1, 1, 9, 9);
777     path2.addRect(11, 11, 10, 10);
778
779     QVERIFY(!path1.intersects(path2));
780     QVERIFY(!path2.intersects(path1));
781
782     path1 = QPainterPath();
783     path2 = QPainterPath();
784     path1.addRect(1, 1, 10, 10);
785     path2.addRect(12, 12, 10, 10);
786
787     QVERIFY(!path1.intersects(path2));
788     QVERIFY(!path2.intersects(path1));
789
790     path1 = QPainterPath();
791     path2 = QPainterPath();
792     path1.addRect(11, 11, 10, 10);
793     path2.addRect(12, 12, 10, 10);
794
795     QVERIFY(path1.intersects(path2));
796     QVERIFY(path2.intersects(path1));
797
798     path1 = QPainterPath();
799     path2 = QPainterPath();
800     path1.addRect(11, 11, 10, 10);
801     path2.addRect(10, 10, 10, 10);
802
803     QVERIFY(path1.intersects(path2));
804     QVERIFY(path2.intersects(path1));
805 }
806
807
808 void tst_QPathClipper::testIntersections8()
809 {
810     QPainterPath path1 = Paths::node() * QTransform().translate(100, 50);
811     QPainterPath path2 = Paths::node() * QTransform().translate(150, 50);;
812
813     QVERIFY(path1.intersects(path2));
814     QVERIFY(path2.intersects(path1));
815
816     path1 = Paths::node();
817     path2 = Paths::node();
818
819     QVERIFY(path1.intersects(path2));
820     QVERIFY(path2.intersects(path1));
821
822     path1 = Paths::node();
823     path2 = Paths::node() * QTransform().translate(0, 30);
824
825     QVERIFY(path1.intersects(path2));
826     QVERIFY(path2.intersects(path1));
827
828     path1 = Paths::node();
829     path2 = Paths::node() * QTransform().translate(30, 0);
830
831     QVERIFY(path1.intersects(path2));
832     QVERIFY(path2.intersects(path1));
833
834     path1 = Paths::node();
835     path2 = Paths::node() * QTransform().translate(30, 30);
836
837     QVERIFY(path1.intersects(path2));
838     QVERIFY(path2.intersects(path1));
839
840     path1 = Paths::node();
841     path2 = Paths::node() * QTransform().translate(1, 1);
842
843     QVERIFY(path1.intersects(path2));
844     QVERIFY(path2.intersects(path1));
845 }
846
847
848 void tst_QPathClipper::testIntersections9()
849 {
850     QPainterPath path1;
851     QPainterPath path2;
852
853     path1.addRect(QRectF(-1,143, 146, 106));
854     path2.addRect(QRectF(-9,145, 150, 100));
855
856     QVERIFY(path1.intersects(path2));
857     QVERIFY(path2.intersects(path1));
858
859     path1 = QPainterPath();;
860     path2 = QPainterPath();
861
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));
866
867     path1 = QPainterPath();;
868     path2 = QPainterPath();
869
870     path1.moveTo(-1 ,  143);
871     path1.lineTo(148 ,  143);
872     path1.lineTo(148 ,  250);
873     path1.lineTo(-1 ,  250);
874
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);
880
881     QVERIFY(path1.intersects(path2));
882     QVERIFY(path2.intersects(path1));
883 }
884
885 QPainterPath pathFromRect(qreal x, qreal y, qreal w, qreal h)
886 {
887     QPainterPath path;
888     path.addRect(QRectF(x, y, w, h));
889     return path;
890 }
891
892 QPainterPath pathFromLine(qreal x1, qreal y1, qreal x2, qreal y2)
893 {
894     QPainterPath path;
895     path.moveTo(x1, y1);
896     path.lineTo(x2, y2);
897     return path;
898 }
899
900 static int loopLength(const QWingedEdge &list, QWingedEdge::TraversalStatus status)
901 {
902     int start = status.edge;
903
904     int length = 0;
905     do {
906         ++length;
907         status = list.next(status);
908     } while (status.edge != start);
909
910     return length;
911 }
912
913 void tst_QPathClipper::testWingedEdge()
914 {
915     {
916         QWingedEdge list;
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));
921
922         QCOMPARE(list.edgeCount(), 4);
923         QCOMPARE(list.vertexCount(), 5);
924
925         QWingedEdge::TraversalStatus status = { e1, QPathEdge::RightTraversal, QPathEdge::Forward };
926
927         status = list.next(status);
928         QCOMPARE(status.direction, QPathEdge::Backward);
929         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
930         QCOMPARE(status.edge, e1);
931
932         status = list.next(status);
933         QCOMPARE(status.direction, QPathEdge::Forward);
934         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
935         QCOMPARE(status.edge, e4);
936
937         status = list.next(status);
938         QCOMPARE(status.direction, QPathEdge::Backward);
939         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
940         QCOMPARE(status.edge, e4);
941
942         status = list.next(status);
943         QCOMPARE(status.direction, QPathEdge::Forward);
944         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
945         QCOMPARE(status.edge, e3);
946
947         status = list.next(status);
948         QCOMPARE(status.direction, QPathEdge::Backward);
949         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
950         QCOMPARE(status.edge, e3);
951
952         status = list.next(status);
953         QCOMPARE(status.direction, QPathEdge::Forward);
954         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
955         QCOMPARE(status.edge, e2);
956
957         status = list.next(status);
958         QCOMPARE(status.direction, QPathEdge::Backward);
959         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
960         QCOMPARE(status.edge, e2);
961
962         status = list.next(status);
963         QCOMPARE(status.direction, QPathEdge::Forward);
964         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
965         QCOMPARE(status.edge, e1);
966     }
967     {
968         QWingedEdge list;
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));
974
975         QCOMPARE(list.edgeCount(), 5);
976         QCOMPARE(list.vertexCount(), 4);
977
978         QWingedEdge::TraversalStatus status = { e1, QPathEdge::RightTraversal, QPathEdge::Forward };
979
980         status = list.next(status);
981         QCOMPARE(status.direction, QPathEdge::Backward);
982         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
983         QCOMPARE(status.edge, e5);
984
985         status = list.next(status);
986         QCOMPARE(status.direction, QPathEdge::Backward);
987         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
988         QCOMPARE(status.edge, e4);
989
990         status = list.next(status);
991         QCOMPARE(status.direction, QPathEdge::Forward);
992         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
993         QCOMPARE(status.edge, e1);
994
995         QCOMPARE(loopLength(list, status), 3);
996
997         status.flip();
998         QCOMPARE(status.direction, QPathEdge::Backward);
999         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1000         QCOMPARE(loopLength(list, status), 3);
1001
1002         status = list.next(status);
1003         QCOMPARE(status.direction, QPathEdge::Forward);
1004         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1005         QCOMPARE(status.edge, e2);
1006
1007         status = list.next(status);
1008         QCOMPARE(status.direction, QPathEdge::Forward);
1009         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1010         QCOMPARE(status.edge, e3);
1011
1012         status = list.next(status);
1013         QCOMPARE(status.direction, QPathEdge::Backward);
1014         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1015         QCOMPARE(status.edge, e1);
1016
1017         status = list.next(status);
1018         status.flip();
1019         QCOMPARE(status.direction, QPathEdge::Backward);
1020         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1021         QCOMPARE(status.edge, e2);
1022         QCOMPARE(loopLength(list, status), 4);
1023
1024         status = list.next(status);
1025         QCOMPARE(status.direction, QPathEdge::Forward);
1026         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1027         QCOMPARE(status.edge, e4);
1028
1029         status = list.next(status);
1030         QCOMPARE(status.direction, QPathEdge::Forward);
1031         QCOMPARE(status.traversal, QPathEdge::RightTraversal);
1032         QCOMPARE(status.edge, e5);
1033
1034         status = list.next(status);
1035         QCOMPARE(status.direction, QPathEdge::Backward);
1036         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1037         QCOMPARE(status.edge, e3);
1038
1039         status = list.next(status);
1040         QCOMPARE(status.direction, QPathEdge::Backward);
1041         QCOMPARE(status.traversal, QPathEdge::LeftTraversal);
1042         QCOMPARE(status.edge, e2);
1043     }
1044     {
1045         QPainterPath path = pathFromRect(0, 0, 20, 20);
1046         QWingedEdge list(path, QPainterPath());
1047
1048         QCOMPARE(list.edgeCount(), 4);
1049         QCOMPARE(list.vertexCount(), 4);
1050
1051         QWingedEdge::TraversalStatus status = { 0, QPathEdge::RightTraversal, QPathEdge::Forward };
1052
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));
1056
1057         status = list.next(status);
1058         QCOMPARE(status.edge, 1);
1059
1060         status = list.next(status);
1061         QCOMPARE(status.edge, 2);
1062
1063         status = list.next(status);
1064         QCOMPARE(status.edge, 3);
1065
1066         status = list.next(status);
1067         QCOMPARE(status.edge, 0);
1068
1069         status.flipDirection();
1070         status = list.next(status);
1071         QCOMPARE(status.edge, 3);
1072
1073         status = list.next(status);
1074         QCOMPARE(status.edge, 2);
1075
1076         status = list.next(status);
1077         QCOMPARE(status.edge, 1);
1078
1079         status = list.next(status);
1080         QCOMPARE(status.edge, 0);
1081
1082         QWingedEdge list2(path, pathFromRect(10, 5, 20, 10));
1083
1084         QCOMPARE(list2.edgeCount(), 12);
1085         QCOMPARE(list2.vertexCount(), 10);
1086
1087         status.flipDirection();
1088         QCOMPARE(loopLength(list2, status), 8);
1089
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));
1094
1095         status = list2.next(status);
1096         status.flipTraversal();
1097
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));
1101
1102         QCOMPARE(loopLength(list2, status), 4);
1103
1104         status.flipDirection();
1105         status = list2.next(status);
1106         status.flipTraversal();
1107
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));
1111
1112         QCOMPARE(loopLength(list2, status), 4);
1113         status = list2.next(status);
1114         status = list2.next(status);
1115
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));
1119     }
1120 }
1121
1122 void tst_QPathClipper::zeroDerivativeCurves()
1123 {
1124     // zero derivative at end
1125     {
1126         QPainterPath a;
1127         a.cubicTo(100, 0, 100, 100, 100, 100);
1128         a.lineTo(100, 200);
1129         a.lineTo(0, 200);
1130
1131         QPainterPath b;
1132         b.moveTo(50, 100);
1133         b.lineTo(150, 100);
1134         b.lineTo(150, 150);
1135         b.lineTo(50, 150);
1136
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)));
1141     }
1142
1143     // zero derivative at start
1144     {
1145         QPainterPath a;
1146         a.cubicTo(100, 0, 100, 100, 100, 100);
1147         a.lineTo(100, 200);
1148         a.lineTo(0, 200);
1149
1150         QPainterPath b;
1151         b.moveTo(50, 100);
1152         b.lineTo(150, 100);
1153         b.lineTo(150, 150);
1154         b.lineTo(50, 150);
1155
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)));
1160     }
1161 }
1162
1163 static bool strictContains(const QPainterPath &a, const QPainterPath &b)
1164 {
1165     return b.subtracted(a) == QPainterPath();
1166 }
1167
1168 Q_DECLARE_METATYPE(QPolygonF)
1169
1170 void tst_QPathClipper::task204301_data()
1171 {
1172     QTest::addColumn<QPolygonF>("points");
1173
1174     {
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);
1183         QPolygonF v;
1184         v << a << b << c << d << e << f << g << h;
1185         QTest::newRow("failed_on_linux") << v;
1186     }
1187
1188     {
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);
1197         QPolygonF v;
1198         v << a << b << c << d << e << f << g << h;
1199         QTest::newRow("failed_on_windows") << v;
1200     }
1201 }
1202
1203 void tst_QPathClipper::task204301()
1204 {
1205     QFETCH(QPolygonF, points);
1206
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];
1215
1216     QPainterPath subA;
1217     subA.addPolygon(QPolygonF() << a << b << c << d);
1218     subA.closeSubpath();
1219
1220     QPainterPath subB;
1221     subB.addPolygon(QPolygonF() << f << e << d << g);
1222     subB.closeSubpath();
1223
1224     QPainterPath subC;
1225     subC.addPolygon(QPolygonF() << h << a << d << g);
1226     subC.closeSubpath();
1227
1228     QPainterPath path;
1229     path.addPath(subA);
1230     path.addPath(subB);
1231     path.addPath(subC);
1232
1233     QPainterPath simplified = path.simplified();
1234
1235     QVERIFY(strictContains(simplified, subA));
1236     QVERIFY(strictContains(simplified, subB));
1237     QVERIFY(strictContains(simplified, subC));
1238 }
1239
1240 void tst_QPathClipper::task209056()
1241 {
1242     QPainterPath p1;
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) );
1248
1249     QPainterPath p2;
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) );
1252
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) );
1255
1256     QPainterPath p3 = p1.intersected(p2);
1257
1258     QVERIFY(p3 != QPainterPath());
1259 }
1260
1261 void tst_QPathClipper::task251909()
1262 {
1263     QPainterPath p1;
1264     p1.moveTo(0, -10);
1265     p1.lineTo(10, -10);
1266     p1.lineTo(10, 0);
1267     p1.lineTo(0, 0);
1268
1269     QPainterPath p2;
1270     p2.moveTo(0, 8e-14);
1271     p2.lineTo(10, -8e-14);
1272     p2.lineTo(10, 10);
1273     p2.lineTo(0, 10);
1274
1275     QPainterPath result = p1.united(p2);
1276
1277     QVERIFY(result.elementCount() <= 5);
1278 }
1279
1280 void tst_QPathClipper::qtbug3778()
1281 {
1282     if (sizeof(double) != sizeof(qreal)) {
1283         QSKIP("This test only works for qreal=double, otherwise ends in rounding errors");
1284     }
1285     QPainterPath path1;
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
1289     path1.lineTo(0, 0);
1290     path1.lineTo(1.07025e-13, 1450);
1291     path1.lineTo(750, 950);
1292     path1.lineTo(950, 750);
1293     path1.lineTo(200, 3.22409e-13);
1294
1295     QPainterPath path2;
1296     path2.moveTo(0, 0);
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);
1303     path2.lineTo(0, 0);
1304
1305     QPainterPath p12 = path1.intersected(path2);
1306
1307     QVERIFY(p12.contains(QPointF(100, 100)));
1308 }
1309
1310 QTEST_MAIN(tst_QPathClipper)
1311
1312
1313 #include "tst_qpathclipper.moc"