Code

Unit test cleanup.
[inkscape.git] / src / svg / svg-path-geom-test.h
1 #include <cxxtest/TestSuite.h>
2 #include "2geom/coord.h"
3 #include "2geom/curves.h"
4 #include "2geom/pathvector.h"
5 #include "svg/svg.h"
6 #include "preferences.h"
7 #include "streq.h"
8 #include <stdio.h>
9 #include <string>
10 #include <vector>
11 #include <glib/gmem.h>
13 class SvgPathGeomTest : public CxxTest::TestSuite
14 {
15 private:
16     std::vector<std::string> rectanglesAbsoluteClosed;
17     std::vector<std::string> rectanglesRelativeClosed;
18     std::vector<std::string> rectanglesAbsoluteOpen;
19     std::vector<std::string> rectanglesRelativeOpen;
20     std::vector<std::string> rectanglesAbsoluteClosed2;
21     std::vector<std::string> rectanglesRelativeClosed2;
22     Geom::PathVector rectanglepvopen;
23     Geom::PathVector rectanglepvclosed;
24     Geom::PathVector rectanglepvclosed2;
25 public:
26     SvgPathGeomTest() {
27         // Lots of ways to define the same rectangle
28         rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z");
29         rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z");
30         rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z");
31         rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z");
32         rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z");
33         rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z");
34         rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2");
35         rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2");
36         rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2");
37         rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6");
38         rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6");
39         rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6");
40         rectanglesAbsoluteClosed2.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 z");
41         rectanglesAbsoluteClosed2.push_back("M 1,2 4,2 4,8 1,8 1,2 z");
42         rectanglesAbsoluteClosed2.push_back("M 1,2 H 4 V 8 H 1 V 2 z");
43         rectanglesRelativeClosed2.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6 z");
44         rectanglesRelativeClosed2.push_back("m 1,2 3,0 0,6 -3,0 0,-6 z");
45         rectanglesRelativeClosed2.push_back("m 1,2 h 3 v 6 h -3 v -6 z");
46         rectanglepvopen.push_back(Geom::Path(Geom::Point(1,2)));
47         rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
48         rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
49         rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
50         rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2)));
51         rectanglepvclosed.push_back(Geom::Path(Geom::Point(1,2)));
52         rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
53         rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
54         rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
55         rectanglepvclosed.back().close();
56         rectanglepvclosed2.push_back(Geom::Path(Geom::Point(1,2)));
57         rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,2),Geom::Point(4,2)));
58         rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,2),Geom::Point(4,8)));
59         rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4,8),Geom::Point(1,8)));
60         rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1,8),Geom::Point(1,2)));
61         rectanglepvclosed2.back().close();
62         // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
63         // TODO: Should we make it mandatory that h/v in the path data results in a H/VLineSegment?
64         //       If so, the tests should be modified to reflect this.
65     }
67 // createSuite and destroySuite get us per-suite setup and teardown
68 // without us having to worry about static initialization order, etc.
69     static SvgPathGeomTest *createSuite() { return new SvgPathGeomTest(); }
70     static void destroySuite( SvgPathGeomTest *suite ) { delete suite; }
72     void testReadRectanglesAbsoluteClosed()
73     {
74         for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
75             Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed[i].c_str());
76             TSM_ASSERT(rectanglesAbsoluteClosed[i].c_str(), bpathEqual(pv,rectanglepvclosed));
77         }
78     }
80     void testReadRectanglesRelativeClosed()
81     {
82         for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
83             Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed[i].c_str());
84             TSM_ASSERT(rectanglesRelativeClosed[i].c_str(), bpathEqual(pv,rectanglepvclosed));
85         }
86     }
88     void testReadRectanglesAbsoluteOpen()
89     {
90         for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
91             Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteOpen[i].c_str());
92             TSM_ASSERT(rectanglesAbsoluteOpen[i].c_str(), bpathEqual(pv,rectanglepvopen));
93         }
94     }
96     void testReadRectanglesRelativeOpen()
97     {
98         for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
99             Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeOpen[i].c_str());
100             TSM_ASSERT(rectanglesRelativeOpen[i].c_str(), bpathEqual(pv,rectanglepvopen));
101         }
102     }
104     void testReadRectanglesAbsoluteClosed2()
105     {
106         for(size_t i=0; i<rectanglesAbsoluteClosed2.size(); i++) {
107             Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed2[i].c_str());
108             TSM_ASSERT(rectanglesAbsoluteClosed2[i].c_str(), bpathEqual(pv,rectanglepvclosed2));
109         }
110     }
112     void testReadRectanglesRelativeClosed2()
113     {
114         for(size_t i=0; i<rectanglesRelativeClosed2.size(); i++) {
115             Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed2[i].c_str());
116             TSM_ASSERT(rectanglesRelativeClosed2[i].c_str(), bpathEqual(pv,rectanglepvclosed2));
117         }
118     }
120     void testReadConcatenatedPaths()
121     {
122         // Note that finalPoint doesn't actually return the final point of the path, just the last given point... (but since this might be intentional and we're not testing lib2geom here, we just specify the final point explicitly
123         Geom::PathVector pv_good;
124         pv_good.push_back(rectanglepvclosed.back());
125         pv_good.push_back(rectanglepvopen.back() * Geom::Translate(1,2)/* * Geom::Translate(pv_good[0].finalPoint())*/);
126         pv_good.push_back(rectanglepvclosed.back() * Geom::Translate(2,4)/* *Geom::Translate(pv_good[1].finalPoint())*/);
127         pv_good.push_back(rectanglepvopen.back());
128         pv_good[0].close();
129         pv_good[1].close(false);
130         pv_good[2].close();
131         pv_good[3].close(false);
132         std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
133         Geom::PathVector pv = sp_svg_read_pathv(path_str.c_str());
134         TS_ASSERT(bpathEqual(pv,pv_good));
135     }
137     void testReadZeroLengthSubpaths() {
138         // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
139         Geom::PathVector pv_good;
140         pv_good.push_back(Geom::Path(Geom::Point(0,0)));
141         pv_good.push_back(Geom::Path(Geom::Point(1,1)));
142         pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));
143         pv_good.push_back(Geom::Path(Geom::Point(3,3)));
144         pv_good.back().close();
145         pv_good.push_back(Geom::Path(Geom::Point(4,4)));
146         pv_good.back().append(Geom::LineSegment(Geom::Point(4,4),Geom::Point(5,5)));
147         pv_good.back().close();
148         pv_good.push_back(Geom::Path(Geom::Point(6,6)));
149         {   // Test absolute version
150             char const * path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";
151             Geom::PathVector pv = sp_svg_read_pathv(path_str);
152             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
153         }
154         {   // Test relative version
155             char const * path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2";
156             Geom::PathVector pv = sp_svg_read_pathv(path_str);
157             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
158         }
159     }
161     void testReadImplicitMoveto() {
162         TS_WARN("Currently lib2geom (/libnr) has no way of specifying the difference between 'M 0,0 ... z M 0,0 L 1,0' and 'M 0,0 ... z L 1,0', the SVG specification does state that these should be handled differently with respect to markers however, see the description of the 'orient' attribute of the 'marker' element.");
163         Geom::PathVector pv_good;
164         pv_good.push_back(Geom::Path(Geom::Point(1,1)));
165         pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(2,2)));
166         pv_good.back().close();
167         pv_good.push_back(Geom::Path(Geom::Point(1,1)));
168         pv_good.back().append(Geom::LineSegment(Geom::Point(1,1),Geom::Point(3,3)));
169         pv_good.back().close();
170         {   // Test absolute version
171             char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
172             Geom::PathVector pv = sp_svg_read_pathv(path_str);
173             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
174         }
175         {   // Test relative version
176             char const * path_str = "M 1,1 l 1,1 z l 2,2 z";
177             Geom::PathVector pv = sp_svg_read_pathv(path_str);
178             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
179         }
180     }
182     void testReadFloatingPoint() {
183         Geom::PathVector pv_good1;
184         pv_good1.push_back(Geom::Path(Geom::Point(.01,.02)));
185         pv_good1.back().append(Geom::LineSegment(Geom::Point(.01,.02),Geom::Point(.04,.02)));
186         pv_good1.back().append(Geom::LineSegment(Geom::Point(.04,.02),Geom::Point(1.5,1.6)));
187         pv_good1.back().append(Geom::LineSegment(Geom::Point(1.5,1.6),Geom::Point(.01,.08)));
188         pv_good1.back().append(Geom::LineSegment(Geom::Point(.01,.08),Geom::Point(.01,.02)));
189         pv_good1.back().close();
190         {   // Test decimals
191             char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
192             Geom::PathVector pv = sp_svg_read_pathv(path_str);
193             TSM_ASSERT(path_str, bpathEqual(pv,pv_good1));
194         }
195         Geom::PathVector pv_good2;
196         pv_good2.push_back(Geom::Path(Geom::Point(.01,.02)));
197         pv_good2.back().append(Geom::LineSegment(Geom::Point(.01,.02),Geom::Point(.04,.02)));
198         pv_good2.back().append(Geom::LineSegment(Geom::Point(.04,.02),Geom::Point(1.5,1.6)));
199         pv_good2.back().append(Geom::LineSegment(Geom::Point(1.5,1.6),Geom::Point(.01,.08)));
200         pv_good2.back().close();
201         {   // Test exponent
202             char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
203             Geom::PathVector pv = sp_svg_read_pathv(path_str);
204             TSM_ASSERT(path_str, bpathEqual(pv,pv_good2));
205         }
206     }
208     void testReadImplicitSeparation() {
209         // Coordinates need not be separated by whitespace if they can still be read unambiguously
210         Geom::PathVector pv_good;
211         pv_good.push_back(Geom::Path(Geom::Point(.1,.2)));
212         pv_good.back().append(Geom::LineSegment(Geom::Point(.1,.2),Geom::Point(.4,.2)));
213         pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.2),Geom::Point(.4,.8)));
214         pv_good.back().append(Geom::LineSegment(Geom::Point(.4,.8),Geom::Point(.1,.8)));
215         pv_good.back().close();
216         {   // Test absolute
217             char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
218             Geom::PathVector pv = sp_svg_read_pathv(path_str);
219             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
220         }
221         {   // Test relative
222             char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
223             Geom::PathVector pv = sp_svg_read_pathv(path_str);
224             TSM_ASSERT(path_str, bpathEqual(pv,pv_good));
225         }
226     }
228     void testReadErrorMisplacedCharacter() {
229         char const * path_str;
230         Geom::PathVector pv;
231         // Comma in the wrong place (commas may only appear between parameters)
232         path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
233         pv = sp_svg_read_pathv(path_str);
234         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
235         // Comma in the wrong place (commas may only appear between parameters)
236         path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
237         pv = sp_svg_read_pathv(path_str);
238         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
239         // Period in the wrong place (no numbers after a 'z')
240         path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
241         pv = sp_svg_read_pathv(path_str);
242         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
243         // Sign in the wrong place (no numbers after a 'z')
244         path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
245         pv = sp_svg_read_pathv(path_str);
246         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
247         // Digit in the wrong place (no numbers after a 'z')
248         path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
249         pv = sp_svg_read_pathv(path_str);
250         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
251         // Digit in the wrong place (no numbers after a 'z')
252         path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
253         pv = sp_svg_read_pathv(path_str);
254         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
255     }
257     void testReadErrorUnrecognizedCharacter() {
258         char const * path_str;
259         Geom::PathVector pv;
260         // Unrecognized character
261         path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
262         pv = sp_svg_read_pathv(path_str);
263         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
264         // Unrecognized character
265         path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
266         pv = sp_svg_read_pathv(path_str);
267         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
268     }
270     void testReadErrorTypo() {
271         char const * path_str;
272         Geom::PathVector pv;
273         // Typo
274         path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
275         pv = sp_svg_read_pathv(path_str);
276         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
278         // Typo
279         path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
280         pv = sp_svg_read_pathv(path_str);
281         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvopen));
282     }
284     void testReadErrorIllformedNumbers() {
285         char const * path_str;
286         Geom::PathVector pv;
287         // Double exponent
288         path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
289         pv = sp_svg_read_pathv(path_str);
290         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
291         // Double sign
292         path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
293         pv = sp_svg_read_pathv(path_str);
294         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
295         // Double sign
296         path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
297         pv = sp_svg_read_pathv(path_str);
298         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
299         // No digit
300         path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
301         pv = sp_svg_read_pathv(path_str);
302         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
303         // No digit
304         path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
305         pv = sp_svg_read_pathv(path_str);
306         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
307         // No digit
308         path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
309         pv = sp_svg_read_pathv(path_str);
310         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
311         // No digit
312         path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
313         pv = sp_svg_read_pathv(path_str);
314         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
315     }
317     void testReadErrorJunk() {
318         char const * path_str;
319         Geom::PathVector pv;
320         // Junk
321         path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
322         pv = sp_svg_read_pathv(path_str);
323         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
324     }
326     void testReadErrorStopReading() {
327         char const * path_str;
328         Geom::PathVector pv;
329         // Unrecognized parameter
330         path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
331         pv = sp_svg_read_pathv(path_str);
332         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
333         // Invalid parameter
334         path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
335         pv = sp_svg_read_pathv(path_str);
336         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
337         // Illformed parameter
338         path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
339         pv = sp_svg_read_pathv(path_str);
340         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvclosed));
342         // "Third" parameter
343         path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
344         pv = sp_svg_read_pathv(path_str);
345         TSM_ASSERT(path_str, bpathEqual(pv,rectanglepvopen));
346     }
348     void testRoundTrip() {
349         // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
350         Geom::PathVector pv;
351         Geom::PathVector new_pv;
352         std::string org_path_str;
353         char * path_str;
354         // Rectangle (closed)
355         org_path_str = rectanglesAbsoluteClosed[0];
356         pv = sp_svg_read_pathv(org_path_str.c_str());
357         path_str = sp_svg_write_path(pv);
358         new_pv = sp_svg_read_pathv(path_str);
359         TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
360         g_free(path_str);
361         // Rectangle (open)
362         org_path_str = rectanglesAbsoluteOpen[0];
363         pv = sp_svg_read_pathv(org_path_str.c_str());
364         path_str = sp_svg_write_path(pv);
365         new_pv = sp_svg_read_pathv(path_str);
366         TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
367         g_free(path_str);
368         // Concatenated rectangles
369         org_path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
370         pv = sp_svg_read_pathv(org_path_str.c_str());
371         path_str = sp_svg_write_path(pv);
372         new_pv = sp_svg_read_pathv(path_str);
373         TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
374         g_free(path_str);
375         // Zero-length subpaths
376         org_path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6";
377         pv = sp_svg_read_pathv(org_path_str.c_str());
378         path_str = sp_svg_write_path(pv);
379         new_pv = sp_svg_read_pathv(path_str);
380         TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv,new_pv));
381         g_free(path_str);
382         // Floating-point
383         org_path_str = "M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z""M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z";
384         pv = sp_svg_read_pathv(org_path_str.c_str());
385         path_str = sp_svg_write_path(pv);
386         new_pv = sp_svg_read_pathv(path_str);
387         TSM_ASSERT(org_path_str.c_str(), bpathEqual(pv, new_pv, 1e-17));
388         g_free(path_str);
389     }
391     void testMinexpPrecision() {
392         Geom::PathVector pv;
393         char * path_str;
394         // Default values
395         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
396         prefs->setBool("/options/svgoutput/allowrelativecoordinates", true);
397         prefs->setBool("/options/svgoutput/forcerepeatcommands", false);
398         prefs->setInt("/options/svgoutput/numericprecision", 8);
399         prefs->setInt("/options/svgoutput/minimumexponent", -8);
400         pv = sp_svg_read_pathv("M 123456781,1.23456781e-8 L 123456782,1.23456782e-8 L 123456785,1.23456785e-8 L 10123456400,1.23456785e-8 L 123456789,1.23456789e-8 L 123456789,101.234564e-8 L 123456789,1.23456789e-8");
401         path_str = sp_svg_write_path(pv);
402         TS_ASSERT_RELATION( streq_rel , "m 123456780,1.2345678e-8 0,0 10,1e-15 9999999210,0 -9999999210,0 0,9.99999921e-7 0,-9.99999921e-7" , path_str );
403         g_free(path_str);
404     }
406 private:
407     bool bpathEqual(Geom::PathVector const &a, Geom::PathVector const &b, double eps = 1e-16) {
408         if (a.size() != b.size()) {
409             char temp[100];
410             sprintf(temp, "PathVectors not the same size: %u != %u", static_cast<unsigned int>(a.size()),static_cast<unsigned int>( b.size()));
411             TS_FAIL(temp);
412             return false;
413         }
414         for(size_t i=0; i<a.size(); i++) {
415             Geom::Path const &pa = a[i];
416             Geom::Path const &pb = b[i];
417             if (pa.closed() && !pb.closed()) {
418                 char temp[100];
419                 sprintf(temp, "Left subpath is closed, right subpath is open. Subpath: %u", static_cast<unsigned int>(i));
420                 TS_FAIL(temp);
421                 return false;
422             }
423             if (!pa.closed() && pb.closed()) {
424                 char temp[100];
425                 sprintf(temp, "Right subpath is closed, left subpath is open. Subpath: %u", static_cast<unsigned int>(i));
426                 TS_FAIL(temp);
427                 return false;
428             }
429             if (pa.size() != pb.size()) {
430                 char temp[100];
431                 sprintf(temp, "Not the same number of segments: %u != %u, subpath: %u", static_cast<unsigned int>(pa.size()), static_cast<unsigned int>(pb.size()), static_cast<unsigned int>(i));
432                 TS_FAIL(temp);
433                 return false;
434             }
435             for(size_t j=0; j<pa.size(); j++) {
436                 Geom::Curve const* ca = &pa[j];
437                 Geom::Curve const* cb = &pb[j];
438                 if (typeid(*ca) == typeid(*cb))
439                 {
440                     if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))
441                     {
442                         Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb);
443                         if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {
444                             char temp[200];
445                             sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
446                             TS_FAIL(temp);
447                             return false;
448                         }
449                         if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {
450                             char temp[200];
451                             sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
452                             TS_FAIL(temp);
453                             return false;
454                         }
455                     }
456                     else if(Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca))
457                     {
458                         Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb);
459                         if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
460                             char temp[200];
461                             sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
462                             TS_FAIL(temp);
463                             return false;
464                         }
465                         if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
466                             char temp[200];
467                             sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
468                             TS_FAIL(temp);
469                             return false;
470                         }
471                     }
472                     else if(Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca))
473                     {
474                         Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb);
475                         if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
476                             char temp[200];
477                             sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
478                             TS_FAIL(temp);
479                             return false;
480                         }
481                         if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
482                             char temp[200];
483                             sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
484                             TS_FAIL(temp);
485                             return false;
486                         }
487                     }
488                     else if(Geom::CubicBezier const *la = dynamic_cast<Geom::CubicBezier const*>(ca))
489                     {
490                         Geom::CubicBezier const *lb = dynamic_cast<Geom::CubicBezier const*>(cb);
491                         if (!Geom::are_near((*la)[0],(*lb)[0], eps)) {
492                             char temp[200];
493                             sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
494                             TS_FAIL(temp);
495                             return false;
496                         }
497                         if (!Geom::are_near((*la)[1],(*lb)[1], eps)) {
498                             char temp[200];
499                             sprintf(temp, "Different 1st control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
500                             TS_FAIL(temp);
501                             return false;
502                         }
503                         if (!Geom::are_near((*la)[2],(*lb)[2], eps)) {
504                             char temp[200];
505                             sprintf(temp, "Different 2nd control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[2][Geom::X], (*la)[2][Geom::Y], (*lb)[2][Geom::X], (*lb)[2][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
506                             TS_FAIL(temp);
507                             return false;
508                         }
509                         if (!Geom::are_near((*la)[3],(*lb)[3], eps)) {
510                             char temp[200];
511                             sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la)[3][Geom::X], (*la)[3][Geom::Y], (*lb)[3][Geom::X], (*lb)[3][Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
512                             TS_FAIL(temp);
513                             return false;
514                         }
515                     }
516                     else
517                     {
518                         char temp[200];
519                         sprintf(temp, "Unknown curve type: %s, subpath: %u, segment: %u", typeid(*ca).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j));
520                         TS_FAIL(temp);
521                     }
522                 }
523                 else // not same type
524                 {
525                     if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca))
526                     {
527                         if (Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb)) {
528                             if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
529                                 char temp[200];
530                                 sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
531                                 TS_FAIL(temp);
532                                 return false;
533                             }
534                             if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
535                                 char temp[200];
536                                 sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
537                                 TS_FAIL(temp);
538                                 return false;
539                             }
540                             char temp[200];
541                             sprintf(temp, "A LineSegment and an HLineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j));
542                             TS_TRACE(temp);
543                         } else if (Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb)) {
544                             if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
545                                 char temp[200];
546                                 sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
547                                 TS_FAIL(temp);
548                                 return false;
549                             }
550                             if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
551                                 char temp[200];
552                                 sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
553                                 TS_FAIL(temp);
554                                 return false;
555                             }
556                             char temp[200];
557                             sprintf(temp, "A LineSegment and a VLineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j));
558                             TS_TRACE(temp);
559                         } else {
560                             char temp[200];
561                             sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j));
562                             TS_FAIL(temp);
563                         }
564                     }
565                     else if(Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb))
566                     {
567                         if (Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca)) {
568                             if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
569                                 char temp[200];
570                                 sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
571                                 TS_FAIL(temp);
572                                 return false;
573                             }
574                             if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
575                                 char temp[200];
576                                 sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
577                                 TS_FAIL(temp);
578                                 return false;
579                             }
580                             char temp[200];
581                             sprintf(temp, "An HLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j));
582                             TS_TRACE(temp);
583                         } else if (Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca)) {
584                             if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) {
585                                 char temp[200];
586                                 sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
587                                 TS_FAIL(temp);
588                                 return false;
589                             }
590                             if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) {
591                                 char temp[200];
592                                 sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j));
593                                 TS_FAIL(temp);
594                                 return false;
595                             }
596                             char temp[200];
597                             sprintf(temp, "A VLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j));
598                             TS_TRACE(temp);
599                         } else {
600                             char temp[200];
601                             sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j));
602                             TS_FAIL(temp);
603                             return false;
604                         }
605                     } else {
606                         char temp[200];
607                         sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j));
608                         TS_FAIL(temp);
609                     }
610                 }
611             }
612         }
613         return true;
614     }
615 };
618 /*
619   Local Variables:
620   mode:c++
621   c-file-style:"stroustrup"
622   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
623   indent-tabs-mode:nil
624   fill-column:99
625   End:
626 */
627 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :