1 /* This file is part of the KDE project
2  * Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "TestPathSegment.h"
21 #include <KoPathSegment.h>
22 #include <KoPathPoint.h>
23 #include <QPainterPath>
24 #include <QTest>
25 
segmentAssign()26 void TestPathSegment::segmentAssign()
27 {
28     KoPathSegment s1(QPointF(0, 0), QPointF(100, 100));
29     KoPathSegment s1Copy = s1;
30     QVERIFY(s1 == s1Copy);
31 
32     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
33     KoPathSegment s2Copy = s2;
34     QVERIFY(s2 == s2Copy);
35 
36     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
37     KoPathSegment s3Copy = s3;
38     QVERIFY(s3 == s3Copy);
39 }
40 
segmentCopy()41 void TestPathSegment::segmentCopy()
42 {
43     KoPathSegment s1(QPointF(0, 0), QPointF(100, 100));
44     KoPathSegment s1Copy(s1);
45     QVERIFY(s1 == s1Copy);
46 
47     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
48     KoPathSegment s2Copy(s2);
49     QVERIFY(s2 == s2Copy);
50 
51     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
52     KoPathSegment s3Copy(s3);
53     QVERIFY(s3 == s3Copy);
54 }
55 
segmentDegree()56 void TestPathSegment::segmentDegree()
57 {
58     KoPathSegment s0(0, 0);
59     QCOMPARE(s0.degree(), -1);
60 
61     KoPathSegment s1(QPointF(0, 0), QPointF(100, 100));
62     QCOMPARE(s1.degree(), 1);
63 
64     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
65     QCOMPARE(s2.degree(), 2);
66 
67     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
68     QCOMPARE(s3.degree(), 3);
69 }
70 
segmentConvexHull()71 void TestPathSegment::segmentConvexHull()
72 {
73     KoPathSegment s1(QPointF(0, 0), QPointF(100, 100));
74     QList<QPointF> hull1 = s1.convexHull();
75     QCOMPARE(hull1.count(), 2);
76     QCOMPARE(hull1[0], QPointF(0, 0));
77     QCOMPARE(hull1[1], QPointF(100, 100));
78 
79     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
80     QList<QPointF> hull2 = s2.convexHull();
81     QCOMPARE(hull2.count(), 3);
82     QCOMPARE(hull2[0], QPointF(0, 0));
83     QCOMPARE(hull2[1], QPointF(100, 100));
84     QCOMPARE(hull2[2], QPointF(200, 0));
85 
86     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
87     QList<QPointF> hull3 = s3.convexHull();
88     QCOMPARE(hull3.count(), 4);
89     QCOMPARE(hull3[0], QPointF(0, 0));
90     QCOMPARE(hull3[1], QPointF(100, 100));
91     QCOMPARE(hull3[2], QPointF(200, 100));
92     QCOMPARE(hull3[3], QPointF(300, 0));
93 
94     KoPathSegment s4(QPointF(0, 0), QPointF(150, 100), QPointF(150, 50), QPointF(300, 0));
95     QList<QPointF> hull4 = s4.convexHull();
96     QCOMPARE(hull4.count(), 3);
97     QCOMPARE(hull4[0], QPointF(0, 0));
98     QCOMPARE(hull4[1], QPointF(150, 100));
99     QCOMPARE(hull4[2], QPointF(300, 0));
100 }
101 
segmentPointAt()102 void TestPathSegment::segmentPointAt()
103 {
104     KoPathSegment s1(QPointF(0, 0), QPointF(100, 0));
105     QCOMPARE(s1.pointAt(0.0), QPointF(0, 0));
106     QCOMPARE(s1.pointAt(0.5), QPointF(50, 0));
107     QCOMPARE(s1.pointAt(1.0), QPointF(100, 0));
108 
109     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
110     QCOMPARE(s2.pointAt(0.0), QPointF(0, 0));
111     QCOMPARE(s2.pointAt(0.5), QPointF(100, 50));
112     QCOMPARE(s2.pointAt(1.0), QPointF(200, 0));
113 
114     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
115     QCOMPARE(s3.pointAt(0.0), QPointF(0, 0));
116     QCOMPARE(s3.pointAt(1.0 / 3.0), QPointF(100, 100*2.0 / 3.0));
117     QCOMPARE(s3.pointAt(2.0 / 3.0), QPointF(200, 100*2.0 / 3.0));
118     QCOMPARE(s3.pointAt(1.0), QPointF(300, 0));
119 }
120 
segmentSplitAt()121 void TestPathSegment::segmentSplitAt()
122 {
123     KoPathSegment s1(QPointF(0, 0), QPointF(100, 0));
124     QPair<KoPathSegment, KoPathSegment> parts1 = s1.splitAt( 0.5 );
125     QCOMPARE(parts1.first.first()->point(), QPointF(0, 0));
126     QCOMPARE(parts1.first.second()->point(), QPointF(50, 0));
127     QCOMPARE(parts1.first.degree(), 1);
128     QCOMPARE(parts1.second.first()->point(), QPointF(50, 0));
129     QCOMPARE(parts1.second.second()->point(), QPointF(100, 0));
130     QCOMPARE(parts1.second.degree(), 1);
131 
132     QPainterPath p1;
133     p1.moveTo( QPoint(0, 0) );
134     p1.lineTo( QPointF(100, 0) );
135     QCOMPARE( parts1.first.second()->point(), p1.pointAtPercent( 0.5 ) );
136 
137     KoPathSegment s2(QPointF(0, 0), QPointF(100, 100), QPointF(200, 0));
138     QPair<KoPathSegment, KoPathSegment> parts2 = s2.splitAt( 0.5 );
139     QCOMPARE(parts2.first.first()->point(), QPointF(0, 0));
140     QCOMPARE(parts2.first.second()->point(), QPointF(100, 50));
141     QCOMPARE(parts2.first.degree(), 2);
142     QCOMPARE(parts2.second.first()->point(), QPointF(100, 50));
143     QCOMPARE(parts2.second.second()->point(), QPointF(200, 0));
144     QCOMPARE(parts2.second.degree(), 2);
145 
146     QPainterPath p2;
147     p2.moveTo( QPoint(0, 0) );
148     p2.quadTo( QPointF(100, 100), QPointF(200, 0) );
149     QCOMPARE( parts2.first.second()->point(), p2.pointAtPercent( 0.5 ) );
150 
151     KoPathSegment s3(QPointF(0, 0), QPointF(100, 100), QPointF(200, 100), QPointF(300, 0));
152     QPair<KoPathSegment, KoPathSegment> parts3 = s3.splitAt( 0.5 );
153     QCOMPARE(parts3.first.first()->point(), QPointF(0, 0));
154     QCOMPARE(parts3.first.second()->point(), QPointF(150, 75));
155     QCOMPARE(parts3.first.degree(), 3);
156     QCOMPARE(parts3.second.first()->point(), QPointF(150, 75));
157     QCOMPARE(parts3.second.second()->point(), QPointF(300, 0));
158     QCOMPARE(parts3.second.degree(), 3);
159 
160     QPainterPath p3;
161     p3.moveTo( QPoint(0, 0) );
162     p3.cubicTo( QPointF(100, 100), QPointF(200, 100), QPointF(300, 0) );
163     QCOMPARE( parts3.first.second()->point(), p3.pointAtPercent( 0.5 ) );
164 }
165 
segmentIntersections()166 void TestPathSegment::segmentIntersections()
167 {
168     // simple line intersections
169     {
170         KoPathSegment s1(QPointF(0, 0), QPointF(100, 0));
171         KoPathSegment s2(QPointF(50, -50), QPointF(50, 50));
172         QList<QPointF> isects = s1.intersections(s2);
173         QCOMPARE(isects.count(), 1);
174     }
175     {
176         KoPathSegment s1(QPointF(0, 0), QPointF(100, 100));
177         KoPathSegment s2(QPointF(25, 100), QPointF(75, 50));
178         QList<QPointF> isects = s1.intersections(s2);
179         QCOMPARE(isects.count(), 1);
180     }
181     // curve intersections
182     {
183         KoPathSegment s1(QPointF(0, 0), QPointF(50, 50), QPointF(100, -50), QPointF(150, 0));
184         KoPathSegment s2(QPointF(75, 75), QPointF(125, 25), QPointF(25, -25), QPointF(75, -75));
185         QList<QPointF> isects = s1.intersections(s2);
186         QCOMPARE(isects.count(), 1);
187     }
188     {
189         KoPathSegment s1(QPointF(0, 0), QPointF(50, 50), QPointF(100, -50), QPointF(150, 0));
190         KoPathSegment s2(QPointF(100, 75), QPointF(150, 25), QPointF(50, -25), QPointF(100, -75));
191         QList<QPointF> isects = s1.intersections(s2);
192         QCOMPARE(isects.count(), 1);
193     }
194     {
195         KoPathSegment s1(QPointF(0, 0), QPointF(25, 50), QPointF(75, 50), QPointF(100, 0));
196         KoPathSegment s2(QPointF(0, 30), QPointF(25, -20), QPointF(75, -20), QPointF(100, 30));
197         QList<QPointF> isects = s1.intersections(s2);
198         QCOMPARE(isects.count(), 2);
199     }
200 }
201 
segmentLength()202 void TestPathSegment::segmentLength()
203 {
204     {
205         // line segment
206         KoPathSegment s(QPointF(0, 0), QPointF(100, 0));
207         QCOMPARE(s.length(), 100.0);
208     }
209     {
210         // quadric curve segment
211         KoPathSegment s1(QPointF(0, 0), QPointF(50, 0), QPointF(100, 0));
212         QCOMPARE(s1.length(), 100.0);
213         KoPathSegment s2(QPointF(0, 0), QPointF(50, 50), QPointF(100, 0));
214         QPainterPath p2;
215         p2.moveTo(QPointF(0, 0));
216         p2.quadTo(QPointF(50, 50), QPointF(100, 0));
217         // verify that difference is less than 0.5 percent of the length
218         QVERIFY(s2.length() - p2.length() < 0.005 * s2.length(0.01));
219     }
220     {
221         // cubic curve segment
222         KoPathSegment s1(QPointF(0, 0), QPointF(25, 0), QPointF(75, 0), QPointF(100, 0));
223         QCOMPARE(s1.length(), 100.0);
224         KoPathSegment s2(QPointF(0, 0), QPointF(25, 50), QPointF(75, 50), QPointF(100, 0));
225         QPainterPath p2;
226         p2.moveTo(QPointF(0, 0));
227         p2.cubicTo(QPointF(25, 50), QPointF(75, 50), QPointF(100, 0));
228         // verify that difference is less than 0.5 percent of the length
229         QVERIFY(s2.length() - p2.length() < 0.005 * s2.length(0.01));
230     }
231 }
232 
segmentFlatness()233 void TestPathSegment::segmentFlatness()
234 {
235     // line segments
236     {
237         KoPathSegment s1(QPointF(0, 0), QPointF(100, 0));
238         QVERIFY(s1.isFlat());
239         KoPathSegment s2(QPointF(0, 0), QPointF(0, 100));
240         QVERIFY(s2.isFlat());
241         KoPathSegment s3(QPointF(0, 0), QPointF(100, 100));
242         QVERIFY(s3.isFlat());
243     }
244     // quadratic segments
245     {
246         KoPathSegment s1(QPointF(0, 0), QPointF(50, 0), QPointF(100, 0));
247         QVERIFY(s1.isFlat());
248         KoPathSegment s2(QPointF(0, 0), QPointF(0, 50), QPointF(0, 100));
249         QVERIFY(s2.isFlat());
250         KoPathSegment s3(QPointF(0, 0), QPointF(50, 50), QPointF(100, 100));
251         QVERIFY(s3.isFlat());
252         KoPathSegment s4(QPointF(0, 0), QPointF(50, 50), QPointF(100, 0));
253         QVERIFY(! s4.isFlat());
254         KoPathSegment s5(QPointF(0, 0), QPointF(50, -50), QPointF(100, 0));
255         QVERIFY(! s5.isFlat());
256         KoPathSegment s6(QPointF(0, 0), QPointF(0, 100), QPointF(100, 100));
257         QVERIFY(! s6.isFlat());
258     }
259     // cubic segments
260     {
261         KoPathSegment s1(QPointF(0, 0), QPointF(25, 0), QPointF(75, 0), QPointF(100, 0));
262         QVERIFY(s1.isFlat());
263         KoPathSegment s2(QPointF(0, 0), QPointF(0, 25), QPointF(0, 75), QPointF(0, 100));
264         QVERIFY(s2.isFlat());
265         KoPathSegment s3(QPointF(0, 0), QPointF(25, 25), QPointF(75, 75), QPointF(100, 100));
266         QVERIFY(s3.isFlat());
267         KoPathSegment s4(QPointF(0, 0), QPointF(25, 50), QPointF(75, 50), QPointF(100, 0));
268         QVERIFY(! s4.isFlat());
269         KoPathSegment s5(QPointF(0, 0), QPointF(25, -50), QPointF(75, -50), QPointF(100, 0));
270         QVERIFY(! s5.isFlat());
271         KoPathSegment s6(QPointF(0, 0), QPointF(-25, 75), QPointF(25, 125), QPointF(100, 100));
272         QVERIFY(! s6.isFlat());
273     }
274 }
275 
nearestPoint()276 void TestPathSegment::nearestPoint()
277 {
278     // line segments
279     {
280         KoPathSegment s1(QPointF(0, 0), QPointF(100, 0));
281         QCOMPARE( s1.nearestPoint( QPointF(0,0) ),    0.0 );
282         QCOMPARE( s1.nearestPoint( QPointF(-20,0) ),  0.0 );
283         QCOMPARE( s1.nearestPoint( QPointF(100,0) ),  1.0 );
284         QCOMPARE( s1.nearestPoint( QPointF(120,0) ),  1.0 );
285         QCOMPARE( s1.nearestPoint( QPointF(50,0) ),   0.5 );
286         QCOMPARE( s1.nearestPoint( QPointF(50,-10) ), 0.5 );
287         QCOMPARE( s1.nearestPoint( QPointF(50,10) ),  0.5 );
288         QCOMPARE( s1.nearestPoint( QPointF(63,50) ),  0.63 );
289         QCOMPARE( s1.nearestPoint( QPointF(63,50) ),  0.63 );
290         QCOMPARE( s1.nearestPoint( QPointF(63,50) ),  0.63 );
291         QCOMPARE( s1.nearestPoint( s1.pointAt( 0.25 ) ),  0.25 );
292         QCOMPARE( s1.nearestPoint( s1.pointAt( 0.75 ) ),  0.75 );
293     }
294     // quadratic segments
295     {
296         KoPathSegment s1(QPointF(0, 0), QPointF(50, 0), QPointF(100, 0));
297         QCOMPARE( s1.nearestPoint( QPointF(0,0) ),    0.0 );
298         QCOMPARE( s1.nearestPoint( QPointF(-20,0) ),  0.0 );
299         QCOMPARE( s1.nearestPoint( QPointF(100,0) ),  1.0 );
300         QCOMPARE( s1.nearestPoint( QPointF(120,0) ),  1.0 );
301         QCOMPARE( s1.nearestPoint( QPointF(50,0) ),   0.5 );
302         QCOMPARE( s1.nearestPoint( QPointF(50,-10) ), 0.5 );
303         QCOMPARE( s1.nearestPoint( QPointF(50,10) ),  0.5 );
304         KoPathSegment s2(QPointF(0, 0), QPointF(50, 50), QPointF(100, 0));
305         QCOMPARE( s2.nearestPoint( QPointF(0,0) ),    0.0 );
306         QCOMPARE( s2.nearestPoint( QPointF(-20,0) ),  0.0 );
307         QCOMPARE( s2.nearestPoint( QPointF(100,0) ),  1.0 );
308         QCOMPARE( s2.nearestPoint( QPointF(120,0) ),  1.0 );
309         QCOMPARE( s2.nearestPoint( QPointF(50,50) ),   0.5 );
310 
311         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.0 ) ), 0.0 );
312         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.25 ) ), 0.25 );
313         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.5 ) ), 0.5 );
314         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.75 ) ), 0.75 );
315         QCOMPARE( s2.nearestPoint( s2.pointAt( 1.0 ) ), 1.0 );
316     }
317     // cubic segments
318     {
319         // a flat cubic bezier
320         KoPathSegment s1(QPointF(0, 0), QPointF(25, 0), QPointF(75, 0), QPointF(100, 0));
321         QCOMPARE( s1.nearestPoint( QPointF(0,0) ),    0.0 );
322         QCOMPARE( s1.nearestPoint( QPointF(-20,0) ),  0.0 );
323         QCOMPARE( s1.nearestPoint( QPointF(100,0) ),  1.0 );
324         QCOMPARE( s1.nearestPoint( QPointF(120,0) ),  1.0 );
325         QCOMPARE( s1.nearestPoint( QPointF(50,0) ),   0.5 );
326         QCOMPARE( s1.nearestPoint( QPointF(50,-10) ), 0.5 );
327         QCOMPARE( s1.nearestPoint( QPointF(50,10) ),  0.5 );
328         KoPathSegment s2(QPointF(0, 0), QPointF(25, 50), QPointF(75, 50), QPointF(100, 0));
329         QCOMPARE( s2.nearestPoint( QPointF(0,0) ),    0.0 );
330         QCOMPARE( s2.nearestPoint( QPointF(-20,0) ),  0.0 );
331         QCOMPARE( s2.nearestPoint( QPointF(100,0) ),  1.0 );
332         QCOMPARE( s2.nearestPoint( QPointF(120,0) ),  1.0 );
333         QCOMPARE( s2.nearestPoint( QPointF(50,50) ),   0.5 );
334 
335         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.0 ) ), 0.0 );
336         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.25 ) ), 0.25 );
337         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.5 ) ), 0.5 );
338         QCOMPARE( s2.nearestPoint( s2.pointAt( 0.75 ) ), 0.75 );
339         QCOMPARE( s2.nearestPoint( s2.pointAt( 1.0 ) ), 1.0 );
340     }
341 }
342 
paramAtLength()343 void TestPathSegment::paramAtLength()
344 {
345     // line segment
346     {
347         KoPathSegment s1(QPointF(0,0), QPointF(100,0));
348         QCOMPARE(s1.paramAtLength(0), 0.0);
349         QCOMPARE(s1.paramAtLength(100.0), 1.0);
350         QCOMPARE(s1.paramAtLength(50.0), 0.5);
351         QCOMPARE(s1.paramAtLength(120.0), 1.0);
352     }
353     // quadratic segments
354     {
355         // a flat quadratic bezier
356         KoPathSegment s1(QPointF(0, 0), QPointF(50, 0), QPointF(100, 0));
357         QCOMPARE(s1.paramAtLength(0), 0.0);
358         QCOMPARE(s1.paramAtLength(100.0), 1.0);
359         QCOMPARE(s1.paramAtLength(120.0), 1.0);
360     }
361     // cubic segments
362     {
363         // a flat cubic bezier
364         KoPathSegment s1(QPointF(0, 0), QPointF(25, 0), QPointF(75, 0), QPointF(100, 0));
365         QCOMPARE(s1.paramAtLength(0), 0.0);
366         QCOMPARE(s1.paramAtLength(100.0), 1.0);
367         QCOMPARE(s1.paramAtLength(120.0), 1.0);
368     }
369 }
370 
371 QTEST_MAIN(TestPathSegment)
372