1 /* WARNING: These tests are not completely correct!
2 * Specifically, 'M 0,0 L 1,1 z' and 'M 0,0 L 1,1 L 0,0 z' are treated as equal, but aren't.
3 * This difference is (probably?) only relevant in the context of markers.
4 * However, since NArtBpath has no (valid) way to distinguish these two and is being retired,
5 * these tests have not been updated to reflect this.
6 */
8 #include <cxxtest/TestSuite.h>
9 #include "libnr/n-art-bpath.h"
10 #include "svg/svg.h"
11 #include "2geom/coord.h"
12 #include "prefs-utils.h"
13 #include "streq.h"
14 #include <string>
15 #include <vector>
16 #include <glib/gmem.h>
18 class SvgPathNRTest : public CxxTest::TestSuite
19 {
20 private:
21 std::vector<std::string> rectanglesAbsoluteClosed;
22 std::vector<std::string> rectanglesRelativeClosed;
23 std::vector<std::string> rectanglesAbsoluteOpen;
24 std::vector<std::string> rectanglesRelativeOpen;
25 NArtBpath rectangleBpath[5+1];
26 public:
27 SvgPathNRTest() {
28 // Lots of ways to define the same rectangle
29 rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 Z");
30 rectanglesAbsoluteClosed.push_back("M 1,2 L 4,2 L 4,8 L 1,8 z");
31 rectanglesAbsoluteClosed.push_back("M 1,2 4,2 4,8 1,8 z");
32 rectanglesAbsoluteClosed.push_back("M 1,2 H 4 V 8 H 1 z");
33 rectanglesRelativeClosed.push_back("m 1,2 l 3,0 l 0,6 l -3,0 z");
34 rectanglesRelativeClosed.push_back("m 1,2 3,0 0,6 -3,0 z");
35 rectanglesRelativeClosed.push_back("m 1,2 h 3 v 6 h -3 z");
36 rectanglesAbsoluteOpen.push_back("M 1,2 L 4,2 L 4,8 L 1,8 L 1,2");
37 rectanglesAbsoluteOpen.push_back("M 1,2 4,2 4,8 1,8 1,2");
38 rectanglesAbsoluteOpen.push_back("M 1,2 H 4 V 8 H 1 V 2");
39 rectanglesRelativeOpen.push_back("m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6");
40 rectanglesRelativeOpen.push_back("m 1,2 3,0 0,6 -3,0 0,-6");
41 rectanglesRelativeOpen.push_back("m 1,2 h 3 v 6 h -3 v -6");
42 rectangleBpath[0].code = NR_MOVETO;
43 rectangleBpath[0].x3 = 1;
44 rectangleBpath[0].y3 = 2;
45 rectangleBpath[1].code = NR_LINETO;
46 rectangleBpath[1].x3 = 4;
47 rectangleBpath[1].y3 = 2;
48 rectangleBpath[2].code = NR_LINETO;
49 rectangleBpath[2].x3 = 4;
50 rectangleBpath[2].y3 = 8;
51 rectangleBpath[3].code = NR_LINETO;
52 rectangleBpath[3].x3 = 1;
53 rectangleBpath[3].y3 = 8;
54 rectangleBpath[4].code = NR_LINETO;
55 rectangleBpath[4].x3 = 1;
56 rectangleBpath[4].y3 = 2;
57 rectangleBpath[5].code = NR_END;
58 // TODO: Also test some (smooth) cubic/quadratic beziers and elliptical arcs
59 }
61 // createSuite and destroySuite get us per-suite setup and teardown
62 // without us having to worry about static initialization order, etc.
63 static SvgPathNRTest *createSuite() { return new SvgPathNRTest(); }
64 static void destroySuite( SvgPathNRTest *suite ) { delete suite; }
66 void testReadRectanglesAbsoluteClosed()
67 {
68 rectangleBpath[0].code = NR_MOVETO;
69 for(size_t i=0; i<rectanglesAbsoluteClosed.size(); i++) {
70 NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteClosed[i].c_str());
71 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
72 g_free(bpath);
73 }
74 }
76 void testReadRectanglesRelativeClosed()
77 {
78 rectangleBpath[0].code = NR_MOVETO;
79 for(size_t i=0; i<rectanglesRelativeClosed.size(); i++) {
80 NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeClosed[i].c_str());
81 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
82 g_free(bpath);
83 }
84 }
86 void testReadRectanglesAbsoluteOpen()
87 {
88 rectangleBpath[0].code = NR_MOVETO_OPEN;
89 for(size_t i=0; i<rectanglesAbsoluteOpen.size(); i++) {
90 NArtBpath * bpath = sp_svg_read_path(rectanglesAbsoluteOpen[i].c_str());
91 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
92 g_free(bpath);
93 }
94 }
96 void testReadRectanglesRelativeOpen()
97 {
98 rectangleBpath[0].code = NR_MOVETO_OPEN;
99 for(size_t i=0; i<rectanglesRelativeOpen.size(); i++) {
100 NArtBpath * bpath = sp_svg_read_path(rectanglesRelativeOpen[i].c_str());
101 TS_ASSERT(bpathEqual(bpath,rectangleBpath));
102 g_free(bpath);
103 }
104 }
106 void testReadConcatenatedPaths()
107 {
108 NArtBpath bpath_good[4*5+1];
109 for(size_t i=0; i<4; i++) {
110 memcpy(bpath_good+i*5,rectangleBpath,sizeof(rectangleBpath[0])*5);
111 }
112 bpath_good[0*5].code = NR_MOVETO;
113 bpath_good[1*5].code = NR_MOVETO_OPEN;
114 bpath_good[2*5].code = NR_MOVETO;
115 bpath_good[3*5].code = NR_MOVETO_OPEN;
116 bpath_good[4*5].code = NR_END;
117 for(size_t i=0; i<5; i++) {
118 bpath_good[1*5+i].x3 += bpath_good[0*5+4].x3;
119 bpath_good[1*5+i].y3 += bpath_good[0*5+4].y3;
120 }
121 for(size_t i=0; i<5; i++) {
122 bpath_good[2*5+i].x3 += bpath_good[1*5+4].x3;
123 bpath_good[2*5+i].y3 += bpath_good[1*5+4].y3;
124 }
125 std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0];
126 NArtBpath * bpath = sp_svg_read_path(path_str.c_str());
127 TS_ASSERT(bpathEqual(bpath,bpath_good));
128 g_free(bpath);
129 }
131 void testReadZeroLengthSubpaths() {
132 // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant
133 NArtBpath bpath_good[8+1];
134 bpath_good[0].code = NR_MOVETO_OPEN;
135 bpath_good[0].x3 = bpath_good[0].y3 = 0;
136 bpath_good[1].code = NR_MOVETO_OPEN;
137 bpath_good[1].x3 = bpath_good[1].y3 = 1;
138 bpath_good[2].code = NR_LINETO;
139 bpath_good[2].x3 = bpath_good[2].y3 = 2;
140 bpath_good[3].code = NR_MOVETO;
141 bpath_good[3].x3 = bpath_good[3].y3 = 3;
142 bpath_good[4].code = NR_MOVETO;
143 bpath_good[4].x3 = bpath_good[4].y3 = 4;
144 bpath_good[5].code = NR_LINETO;
145 bpath_good[5].x3 = bpath_good[5].y3 = 5;
146 bpath_good[6].code = NR_LINETO;
147 bpath_good[6].x3 = bpath_good[6].y3 = 4;
148 bpath_good[7].code = NR_MOVETO_OPEN;
149 bpath_good[7].x3 = bpath_good[7].y3 = 6;
150 bpath_good[8].code = NR_END;
151 { // Test absolute version
152 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";
153 NArtBpath * bpath = sp_svg_read_path(path_str);
154 TS_ASSERT(bpathEqual(bpath,bpath_good));
155 g_free(bpath);
156 }
157 { // Test relative version
158 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";
159 NArtBpath * bpath = sp_svg_read_path(path_str);
160 TS_ASSERT(bpathEqual(bpath,bpath_good));
161 g_free(bpath);
162 }
163 }
165 void testReadImplicitMoveto() {
166 NArtBpath bpath_good[6+1];
167 bpath_good[0].code = NR_MOVETO;
168 bpath_good[0].x3 = bpath_good[0].y3 = 1;
169 bpath_good[1].code = NR_LINETO;
170 bpath_good[1].x3 = bpath_good[1].y3 = 2;
171 bpath_good[2].code = NR_LINETO;
172 bpath_good[2].x3 = bpath_good[2].y3 = 1;
173 bpath_good[3].code = NR_MOVETO;
174 bpath_good[3].x3 = bpath_good[3].y3 = 1;
175 bpath_good[4].code = NR_LINETO;
176 bpath_good[4].x3 = bpath_good[4].y3 = 3;
177 bpath_good[5].code = NR_LINETO;
178 bpath_good[5].x3 = bpath_good[5].y3 = 1;
179 bpath_good[6].code = NR_END;
180 { // Test absolute version
181 char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
182 NArtBpath * bpath = sp_svg_read_path(path_str);
183 TS_ASSERT(bpathEqual(bpath,bpath_good));
184 g_free(bpath);
185 }
186 { // Test relative version
187 char const * path_str = "M 1,1 L 2,2 z L 3,3 z";
188 NArtBpath * bpath = sp_svg_read_path(path_str);
189 TS_ASSERT(bpathEqual(bpath,bpath_good));
190 g_free(bpath);
191 }
192 }
194 void testReadFloatingPoint() {
195 NArtBpath bpath_good[5+1];
196 bpath_good[0].code = NR_MOVETO;
197 bpath_good[0].x3 = .01;
198 bpath_good[0].y3 = .02;
199 bpath_good[1].code = NR_LINETO;
200 bpath_good[1].x3 = .04;
201 bpath_good[1].y3 = .02;
202 bpath_good[2].code = NR_LINETO;
203 bpath_good[2].x3 = 1.5;
204 bpath_good[2].y3 = 1.6;
205 bpath_good[3].code = NR_LINETO;
206 bpath_good[3].x3 = .01;
207 bpath_good[3].y3 = .08;
208 bpath_good[4].code = NR_LINETO;
209 bpath_good[4].x3 = .01;
210 bpath_good[4].y3 = .02;
211 bpath_good[5].code = NR_END;
212 { // Test decimals
213 char const * path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z";
214 NArtBpath * bpath = sp_svg_read_path(path_str);
215 TS_ASSERT(bpathEqual(bpath,bpath_good));
216 g_free(bpath);
217 }
218 { // Test exponent
219 char const * path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z";
220 NArtBpath * bpath = sp_svg_read_path(path_str);
221 TS_ASSERT(bpathEqual(bpath,bpath_good));
222 g_free(bpath);
223 }
224 }
226 void testReadImplicitSeparation() {
227 // Coordinates need not be separated by whitespace if they can still be read unambiguously
228 NArtBpath bpath_good[5+1];
229 bpath_good[0].code = NR_MOVETO;
230 bpath_good[0].x3 = .1;
231 bpath_good[0].y3 = .2;
232 bpath_good[1].code = NR_LINETO;
233 bpath_good[1].x3 = .4;
234 bpath_good[1].y3 = .2;
235 bpath_good[2].code = NR_LINETO;
236 bpath_good[2].x3 = .4;
237 bpath_good[2].y3 = .8;
238 bpath_good[3].code = NR_LINETO;
239 bpath_good[3].x3 = .1;
240 bpath_good[3].y3 = .8;
241 bpath_good[4].code = NR_LINETO;
242 bpath_good[4].x3 = .1;
243 bpath_good[4].y3 = .2;
244 bpath_good[5].code = NR_END;
245 { // Test absolute
246 char const * path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z";
247 NArtBpath * bpath = sp_svg_read_path(path_str);
248 TS_ASSERT(bpathEqual(bpath,bpath_good));
249 g_free(bpath);
250 }
251 { // Test relative
252 char const * path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z";
253 NArtBpath * bpath = sp_svg_read_path(path_str);
254 TS_ASSERT(bpathEqual(bpath,bpath_good));
255 g_free(bpath);
256 }
257 }
259 void testReadErrorMisplacedCharacter() {
260 char const * path_str;
261 NArtBpath * bpath;
262 NArtBpath * bpath_good = rectangleBpath;
263 bpath_good[0].code = NR_MOVETO;
264 // Comma in the wrong place (commas may only appear between parameters)
265 path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15";
266 bpath = sp_svg_read_path(path_str);
267 TS_ASSERT(bpathEqual(bpath,bpath_good));
268 g_free(bpath);
269 // Comma in the wrong place (commas may only appear between parameters)
270 path_str = "M 1,2 4,2 4,8 1,8 z m,13,15";
271 bpath = sp_svg_read_path(path_str);
272 TS_ASSERT(bpathEqual(bpath,bpath_good));
273 g_free(bpath);
274 // Period in the wrong place (no numbers after a 'z')
275 path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15";
276 bpath = sp_svg_read_path(path_str);
277 TS_ASSERT(bpathEqual(bpath,bpath_good));
278 g_free(bpath);
279 // Sign in the wrong place (no numbers after a 'z')
280 path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15";
281 bpath = sp_svg_read_path(path_str);
282 TS_ASSERT(bpathEqual(bpath,bpath_good));
283 g_free(bpath);
284 // Digit in the wrong place (no numbers after a 'z')
285 path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15";
286 bpath = sp_svg_read_path(path_str);
287 TS_ASSERT(bpathEqual(bpath,bpath_good));
288 g_free(bpath);
289 // Digit in the wrong place (no numbers after a 'z')
290 path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15";
291 bpath = sp_svg_read_path(path_str);
292 TS_ASSERT(bpathEqual(bpath,bpath_good));
293 g_free(bpath);
294 }
296 void testReadErrorUnrecognizedCharacter() {
297 char const * path_str;
298 NArtBpath * bpath;
299 NArtBpath * bpath_good = rectangleBpath;
300 bpath_good[0].code = NR_MOVETO;
301 // Unrecognized character
302 path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15";
303 bpath = sp_svg_read_path(path_str);
304 TS_ASSERT(bpathEqual(bpath,bpath_good));
305 g_free(bpath);
306 // Unrecognized character
307 path_str = "M 1,2 4,2 4,8 1,8 z m &13,15";
308 bpath = sp_svg_read_path(path_str);
309 TS_ASSERT(bpathEqual(bpath,bpath_good));
310 g_free(bpath);
311 }
313 void testReadErrorTypo() {
314 char const * path_str;
315 NArtBpath * bpath;
316 NArtBpath * bpath_good = rectangleBpath;
317 bpath_good[0].code = NR_MOVETO;
318 // Typo
319 path_str = "M 1,2 4,2 4,8 1,8 z j 13,15";
320 bpath = sp_svg_read_path(path_str);
321 TS_ASSERT(bpathEqual(bpath,bpath_good));
322 g_free(bpath);
324 bpath_good[0].code = NR_MOVETO_OPEN;
325 // Typo
326 path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15";
327 bpath = sp_svg_read_path(path_str);
328 TS_ASSERT(bpathEqual(bpath,bpath_good));
329 g_free(bpath);
330 }
332 void testReadErrorIllformedNumbers() {
333 char const * path_str;
334 NArtBpath * bpath;
335 NArtBpath * bpath_good = rectangleBpath;
336 bpath_good[0].code = NR_MOVETO;
337 // Double exponent
338 path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15";
339 bpath = sp_svg_read_path(path_str);
340 TS_ASSERT(bpathEqual(bpath,bpath_good));
341 g_free(bpath);
342 // Double sign
343 path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15";
344 bpath = sp_svg_read_path(path_str);
345 TS_ASSERT(bpathEqual(bpath,bpath_good));
346 g_free(bpath);
347 // Double sign
348 path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15";
349 bpath = sp_svg_read_path(path_str);
350 TS_ASSERT(bpathEqual(bpath,bpath_good));
351 g_free(bpath);
352 // No digit
353 path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15";
354 bpath = sp_svg_read_path(path_str);
355 TS_ASSERT(bpathEqual(bpath,bpath_good));
356 g_free(bpath);
357 // No digit
358 path_str = "M 1,2 4,2 4,8 1,8 z m .,15";
359 bpath = sp_svg_read_path(path_str);
360 TS_ASSERT(bpathEqual(bpath,bpath_good));
361 g_free(bpath);
362 // No digit
363 path_str = "M 1,2 4,2 4,8 1,8 z m +,15";
364 bpath = sp_svg_read_path(path_str);
365 TS_ASSERT(bpathEqual(bpath,bpath_good));
366 g_free(bpath);
367 // No digit
368 path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15";
369 bpath = sp_svg_read_path(path_str);
370 TS_ASSERT(bpathEqual(bpath,bpath_good));
371 g_free(bpath);
372 }
374 void testReadErrorJunk() {
375 char const * path_str;
376 NArtBpath * bpath;
377 NArtBpath * bpath_good = rectangleBpath;
378 bpath_good[0].code = NR_MOVETO;
379 // Junk
380 path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23";
381 bpath = sp_svg_read_path(path_str);
382 TS_ASSERT(bpathEqual(bpath,bpath_good));
383 g_free(bpath);
384 }
386 void testReadErrorStopReading() {
387 char const * path_str;
388 NArtBpath * bpath;
389 NArtBpath * bpath_good = rectangleBpath;
390 bpath_good[0].code = NR_MOVETO;
391 // Unrecognized parameter
392 path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
393 bpath = sp_svg_read_path(path_str);
394 TS_ASSERT(bpathEqual(bpath,bpath_good));
395 g_free(bpath);
396 // Invalid parameter
397 path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34";
398 bpath = sp_svg_read_path(path_str);
399 TS_ASSERT(bpathEqual(bpath,bpath_good));
400 g_free(bpath);
401 // Illformed parameter
402 path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34";
403 bpath = sp_svg_read_path(path_str);
404 TS_ASSERT(bpathEqual(bpath,bpath_good));
405 g_free(bpath);
407 bpath_good[0].code = NR_MOVETO_OPEN;
408 // "Third" parameter
409 path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23";
410 bpath = sp_svg_read_path(path_str);
411 TS_ASSERT(bpathEqual(bpath,bpath_good));
412 g_free(bpath);
413 }
415 void testRoundTrip() {
416 // This is the easiest way to (also) test writing path data, as a path can be written in more than one way.
417 NArtBpath * bpath;
418 NArtBpath * new_bpath;
419 char * path_str;
420 // Rectangle (closed)
421 bpath = sp_svg_read_path(rectanglesAbsoluteClosed[0].c_str());
422 path_str = sp_svg_write_path(bpath);
423 new_bpath = sp_svg_read_path(path_str);
424 TS_ASSERT(bpathEqual(bpath,new_bpath));
425 g_free(bpath); g_free(path_str); g_free(new_bpath);
426 // Rectangle (open)
427 bpath = sp_svg_read_path(rectanglesAbsoluteOpen[0].c_str());
428 path_str = sp_svg_write_path(bpath);
429 new_bpath = sp_svg_read_path(path_str);
430 TS_ASSERT(bpathEqual(bpath,new_bpath));
431 g_free(bpath); g_free(path_str); g_free(new_bpath);
432 // Concatenated rectangles
433 bpath = sp_svg_read_path((rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + rectanglesAbsoluteOpen[0]).c_str());
434 path_str = sp_svg_write_path(bpath);
435 new_bpath = sp_svg_read_path(path_str);
436 TS_ASSERT(bpathEqual(bpath,new_bpath));
437 g_free(bpath); g_free(path_str); g_free(new_bpath);
438 // Zero-length subpaths
439 bpath = sp_svg_read_path("M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6");
440 path_str = sp_svg_write_path(bpath);
441 new_bpath = sp_svg_read_path(path_str);
442 TS_ASSERT(bpathEqual(bpath,new_bpath));
443 g_free(bpath); g_free(path_str); g_free(new_bpath);
444 // Floating-point
445 bpath = sp_svg_read_path("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");
446 path_str = sp_svg_write_path(bpath);
447 new_bpath = sp_svg_read_path(path_str);
448 TS_ASSERT(bpathEqual(bpath, new_bpath, 1e-17));
449 g_free(bpath); g_free(path_str); g_free(new_bpath);
450 }
452 void testMinexpPrecision() {
453 NArtBpath * bpath;
454 char * path_str;
455 // Default values
456 prefs_set_int_attribute("options.svgoutput", "allowrelativecoordinates", 1);
457 prefs_set_int_attribute("options.svgoutput", "forcerepeatcommands", 0);
458 prefs_set_int_attribute("options.svgoutput", "numericprecision", 8);
459 prefs_set_int_attribute("options.svgoutput", "minimumexponent", -8);
460 bpath = sp_svg_read_path("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");
461 path_str = sp_svg_write_path(bpath);
462 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 );
463 g_free(bpath); g_free(path_str);
464 }
466 private:
467 bool bpathEqual(NArtBpath const * a, NArtBpath const * b, double eps = 1e-16) {
468 while(a->code != NR_END && b->code == a->code) {
469 switch(a->code) {
470 case NR_MOVETO:
471 case NR_MOVETO_OPEN:
472 case NR_LINETO:
473 if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
474 break;
475 case NR_CURVETO:
476 if (!Geom::are_near(a->x1,b->x1, eps) || !Geom::are_near(a->y1,b->y1, eps)) return false;
477 if (!Geom::are_near(a->x2,b->x2, eps) || !Geom::are_near(a->y2,b->y2, eps)) return false;
478 if (!Geom::are_near(a->x3,b->x3, eps) || !Geom::are_near(a->y3,b->y3, eps)) return false;
479 break;
480 default:
481 TS_FAIL("Unknown path code!");
482 }
483 a++;
484 b++;
485 }
486 return a->code == b->code;
487 }
488 };
491 /*
492 Local Variables:
493 mode:c++
494 c-file-style:"stroustrup"
495 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
496 indent-tabs-mode:nil
497 fill-column:99
498 End:
499 */
500 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :