1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser 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.
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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
25 static cairo_path_data_t data
[] = {
26 /* Useless heading CPML_MOVE */
27 { .header
= { CPML_MOVE
, 2 }},
30 /* First segment: a couple of lines of length 2 */
31 { .header
= { CPML_MOVE
, 2 }},
33 { .header
= { CPML_LINE
, 2 }},
35 { .header
= { CPML_LINE
, 2 }},
38 /* Another useless CPML_MOVE with useless embedded data */
39 { .header
= { CPML_MOVE
, 3 }},
43 /* Second segment: a Bézier curve with a trailing CPML_CLOSE */
44 { .header
= { CPML_MOVE
, 2 }},
45 { .point
= { 10, 13 }},
46 { .header
= { CPML_CURVE
, 4 }},
48 { .point
= { 10, 11 }},
49 { .point
= { 12, 13 }},
50 { .header
= { CPML_CLOSE
, 1 }},
52 /* A valid cairo segment considered invalid by CPML
53 * because does not have a leading CPML_MOVE */
54 { .header
= { CPML_LINE
, 2 }},
55 { .point
= { 10, 0 }},
56 { .header
= { CPML_CLOSE
, 1 }},
58 /* Another valid cairo segment invalid in CPML */
59 { .header
= { CPML_CLOSE
, 1 }},
61 /* Third segment: a couple of arcs */
62 { .header
= { CPML_MOVE
, 2 }},
63 { .point
= { 14, 15 }},
64 { .header
= { CPML_ARC
, 3 }},
65 { .point
= { 16, 17 }},
66 { .point
= { 18, 19 }},
67 { .header
= { CPML_ARC
, 3 }},
68 { .point
= { 20, 21 }},
69 { .point
= { 22, 23 }},
71 /* Forth segment: a floating CPML_CLOSE */
72 { .header
= { CPML_MOVE
, 2 }},
73 { .point
= { 24, 25 }},
74 { .header
= { CPML_CLOSE
, 1 }}
77 static cairo_path_data_t noop_data
[] = {
78 /* Useless heading CPML_MOVE */
79 { .header
= { CPML_MOVE
, 2 }},
81 { .header
= { CPML_MOVE
, 4 }},
85 { .header
= { CPML_MOVE
, 2 }},
89 static cairo_path_data_t y1_data
[] = {
90 { .header
= { CPML_MOVE
, 2 }},
92 { .header
= { CPML_LINE
, 2 }},
102 cairo_path_t noop_path
= {
103 CAIRO_STATUS_SUCCESS
,
105 G_N_ELEMENTS(noop_data
)
108 cairo_path_t y1_path
= {
109 CAIRO_STATUS_SUCCESS
,
111 G_N_ELEMENTS(y1_data
)
116 _cpml_test_browsing(void)
119 cpml_segment_from_cairo(&segment
, &path
);
122 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
123 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_LINE
);
125 cpml_segment_reset(&segment
);
126 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
127 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_LINE
);
130 g_assert_cmpint(cpml_segment_next(&segment
), ==, 1);
131 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
132 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_CURVE
);
135 g_assert_cmpint(cpml_segment_next(&segment
), ==, 1);
136 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
137 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_ARC
);
140 g_assert_cmpint(cpml_segment_next(&segment
), ==, 1);
141 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
142 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_CLOSE
);
144 g_assert_cmpint(cpml_segment_next(&segment
), ==, 0);
146 cpml_segment_reset(&segment
);
147 g_assert_cmpint(segment
.data
[0].header
.type
, ==, CPML_MOVE
);
148 g_assert_cmpint(segment
.data
[2].header
.type
, ==, CPML_LINE
);
152 _cpml_test_from_cairo(void)
155 g_assert_cmpint(cpml_segment_from_cairo(NULL
, &path
), ==, 0);
156 g_assert_cmpint(cpml_segment_from_cairo(&segment
, NULL
), ==, 0);
157 g_assert_cmpint(cpml_segment_from_cairo(NULL
, NULL
), ==, 0);
159 g_assert_cmpint(cpml_segment_from_cairo(&segment
, &noop_path
), ==, 0);
160 g_assert_cmpint(cpml_segment_from_cairo(&segment
, &path
), ==, 1);
164 _cpml_test_get_length(void)
167 cpml_segment_from_cairo(&segment
, &path
);
169 g_assert_cmpfloat(cpml_segment_get_length(NULL
), ==, 0);
172 g_assert_cmpfloat(cpml_segment_get_length(&segment
), ==, 4);
173 g_assert_cmpfloat(cpml_segment_get_length(&segment
), ==, 4);
176 cpml_segment_next(&segment
);
177 /* TODO: Bézier curve length not yet implemented
178 * g_assert_cmpfloat(cpml_segment_get_length(&segment), ==, ???); */
181 cpml_segment_next(&segment
);
182 g_assert_cmpfloat(cpml_segment_get_length(&segment
), ==, 0);
185 cpml_segment_next(&segment
);
186 g_assert_cmpfloat(cpml_segment_get_length(&segment
), ==, 0);
190 _cpml_test_put_intersections(void)
192 CpmlSegment segment1
, segment2
;
195 cpml_segment_from_cairo(&segment1
, &path
);
196 cpml_segment_from_cairo(&segment2
, &y1_path
);
198 g_assert_cmpuint(cpml_segment_put_intersections(NULL
, &segment2
, 2, pair
), ==, 0);
199 g_assert_cmpuint(cpml_segment_put_intersections(&segment1
, NULL
, 2, pair
), ==, 0);
200 g_assert_cmpuint(cpml_segment_put_intersections(&segment1
, &segment2
, 0, pair
), ==, 0);
201 g_assert_cmpuint(cpml_segment_put_intersections(&segment1
, &segment2
, 2, NULL
), ==, 0);
203 /* The first segment of path intersects y1_path in (2, 1) */
204 g_assert_cmpuint(cpml_segment_put_intersections(&segment1
, &segment2
, 2, pair
), ==, 1);
205 g_assert_cmpfloat(pair
[0].x
, ==, 2);
206 g_assert_cmpfloat(pair
[0].y
, ==, 1);
208 /* The third segment of path does not intersect y1_path */
209 cpml_segment_next(&segment1
);
210 cpml_segment_next(&segment1
);
211 g_assert_cmpuint(cpml_segment_put_intersections(&segment1
, &segment2
, 2, pair
), ==, 0);
215 _cpml_test_offset(void)
217 CpmlSegment original
, *segment
;
219 /* Check a NULL segment does not crash the process */
220 cpml_segment_offset(NULL
, 1);
222 cpml_segment_from_cairo(&original
, &path
);
224 /* Working on a duplicate of the first segment to avoid modifying path */
225 segment
= cpml_segment_deep_dup(&original
);
226 cpml_segment_offset(segment
, 1);
228 g_assert_cmpint(segment
->data
[0].header
.type
, ==, CPML_MOVE
);
229 g_assert_cmpfloat(segment
->data
[1].point
.x
, ==, 0);
230 g_assert_cmpfloat(segment
->data
[1].point
.y
, ==, 1);
232 g_assert_cmpint(segment
->data
[2].header
.type
, ==, CPML_LINE
);
233 g_assert_cmpfloat(segment
->data
[3].point
.x
, ==, 1);
234 g_assert_cmpfloat(segment
->data
[3].point
.y
, ==, 1);
236 g_assert_cmpint(segment
->data
[4].header
.type
, ==, CPML_LINE
);
237 g_assert_cmpfloat(segment
->data
[5].point
.x
, ==, 1);
238 g_assert_cmpfloat(segment
->data
[5].point
.y
, ==, 2);
240 /* TODO: provide tests for arcs and curves */
246 _cpml_test_transform(void)
248 CpmlSegment original
, *segment
;
249 cairo_matrix_t matrix
;
251 cpml_segment_from_cairo(&original
, &path
);
253 /* Working on a duplicate of the first segment to avoid modifying path */
254 segment
= cpml_segment_deep_dup(&original
);
256 /* Check invalid args does not crash the process */
257 cpml_segment_transform(NULL
, &matrix
);
258 cpml_segment_transform(segment
, NULL
);
260 cairo_matrix_init_translate(&matrix
, 1, 2);
261 cpml_segment_transform(segment
, &matrix
);
263 g_assert_cmpint(segment
->data
[0].header
.type
, ==, CPML_MOVE
);
264 g_assert_cmpfloat(segment
->data
[1].point
.x
, ==, 1);
265 g_assert_cmpfloat(segment
->data
[1].point
.y
, ==, 2);
267 g_assert_cmpint(segment
->data
[2].header
.type
, ==, CPML_LINE
);
268 g_assert_cmpfloat(segment
->data
[3].point
.x
, ==, 3);
269 g_assert_cmpfloat(segment
->data
[3].point
.y
, ==, 2);
271 g_assert_cmpint(segment
->data
[4].header
.type
, ==, CPML_LINE
);
272 g_assert_cmpfloat(segment
->data
[5].point
.x
, ==, 3);
273 g_assert_cmpfloat(segment
->data
[5].point
.y
, ==, 4);
279 _cpml_test_reverse(void)
281 CpmlSegment original
, *segment
;
283 /* Check a NULL segment does not crash the process */
284 cpml_segment_reverse(NULL
);
286 cpml_segment_from_cairo(&original
, &path
);
288 /* First segment: work on a duplicate to avoid modifying path */
289 segment
= cpml_segment_deep_dup(&original
);
290 cpml_segment_reverse(segment
);
292 g_assert_cmpint(segment
->num_data
, ==, 6);
294 g_assert_cmpint(segment
->data
[0].header
.type
, ==, CPML_MOVE
);
295 g_assert_cmpfloat(segment
->data
[1].point
.x
, ==, 2);
296 g_assert_cmpfloat(segment
->data
[1].point
.y
, ==, 2);
298 g_assert_cmpint(segment
->data
[2].header
.type
, ==, CPML_LINE
);
299 g_assert_cmpfloat(segment
->data
[3].point
.x
, ==, 2);
300 g_assert_cmpfloat(segment
->data
[3].point
.y
, ==, 0);
302 g_assert_cmpint(segment
->data
[4].header
.type
, ==, CPML_LINE
);
303 g_assert_cmpfloat(segment
->data
[5].point
.x
, ==, 0);
304 g_assert_cmpfloat(segment
->data
[5].point
.y
, ==, 0);
308 /* TODO: the second segment is an invalid CPML segment because
309 * it is followed by a segment without a leading move_to.
310 * Find a decent reverse algorithm and document it.
311 * I'm just skipping that segment for now.
313 cpml_segment_next(&original
);
316 cpml_segment_next(&original
);
317 segment
= cpml_segment_deep_dup(&original
);
318 cpml_segment_reverse(segment
);
320 g_assert_cmpint(segment
->num_data
, ==, 8);
322 g_assert_cmpint(segment
->data
[0].header
.type
, ==, CPML_MOVE
);
323 g_assert_cmpfloat(segment
->data
[1].point
.x
, ==, 22);
324 g_assert_cmpfloat(segment
->data
[1].point
.y
, ==, 23);
326 g_assert_cmpint(segment
->data
[2].header
.type
, ==, CPML_ARC
);
327 g_assert_cmpfloat(segment
->data
[3].point
.x
, ==, 20);
328 g_assert_cmpfloat(segment
->data
[3].point
.y
, ==, 21);
329 g_assert_cmpfloat(segment
->data
[4].point
.x
, ==, 18);
330 g_assert_cmpfloat(segment
->data
[4].point
.y
, ==, 19);
332 g_assert_cmpint(segment
->data
[5].header
.type
, ==, CPML_ARC
);
333 g_assert_cmpfloat(segment
->data
[6].point
.x
, ==, 16);
334 g_assert_cmpfloat(segment
->data
[6].point
.y
, ==, 17);
335 g_assert_cmpfloat(segment
->data
[7].point
.x
, ==, 14);
336 g_assert_cmpfloat(segment
->data
[7].point
.y
, ==, 15);
341 cpml_segment_next(&original
);
342 segment
= cpml_segment_deep_dup(&original
);
343 cpml_segment_reverse(segment
);
345 g_assert_cmpint(segment
->num_data
, ==, 3);
347 g_assert_cmpint(segment
->data
[0].header
.type
, ==, CPML_MOVE
);
348 g_assert_cmpfloat(segment
->data
[1].point
.x
, ==, 24);
349 g_assert_cmpfloat(segment
->data
[1].point
.y
, ==, 25);
351 g_assert_cmpint(segment
->data
[2].header
.type
, ==, CPML_CLOSE
);
357 _cpml_test_to_cairo(void)
361 int length
, last_length
;
363 cr
= adg_test_cairo_context();
364 cpml_segment_from_cairo(&segment
, &path
);
366 g_assert_cmpint(adg_test_cairo_num_data(cr
), ==, 0);
367 cpml_segment_to_cairo(NULL
, cr
);
368 g_assert_cmpint(adg_test_cairo_num_data(cr
), ==, 0);
369 cpml_segment_to_cairo(&segment
, NULL
);
370 g_assert_cmpint(adg_test_cairo_num_data(cr
), ==, 0);
374 last_length
= length
;
375 cpml_segment_to_cairo(&segment
, cr
);
376 length
= adg_test_cairo_num_data(cr
);
377 g_assert_cmpint(length
, >, last_length
);
378 } while (cpml_segment_next(&segment
));
384 _cpml_test_dump(void)
386 #if GLIB_CHECK_VERSION(2, 38, 0)
387 if (g_test_subprocess()) {
390 /* This should not crash the process */
391 cpml_segment_dump(NULL
);
393 cpml_segment_from_cairo(&segment
, &path
);
394 cpml_segment_dump(&segment
);
396 cpml_segment_next(&segment
);
397 cpml_segment_dump(&segment
);
402 g_test_trap_subprocess(NULL
, 0, 0);
403 g_test_trap_assert_passed();
404 g_test_trap_assert_stderr_unmatched("?");
405 g_test_trap_assert_stdout("*NULL*");
406 g_test_trap_assert_stdout("*move*");
407 g_test_trap_assert_stdout("*line*");
408 g_test_trap_assert_stdout("*curve*");
409 g_test_trap_assert_stdout_unmatched("*arc*");
415 main(int argc
, char *argv
[])
417 adg_test_init(&argc
, &argv
);
419 adg_test_add_func("/cpml/segment/behavior/browsing", _cpml_test_browsing
);
421 adg_test_add_func("/cpml/segment/method/from-cairo", _cpml_test_from_cairo
);
422 adg_test_add_func("/cpml/segment/method/get-length", _cpml_test_get_length
);
423 adg_test_add_func("/cpml/segment/method/put-intersections", _cpml_test_put_intersections
);
424 adg_test_add_func("/cpml/segment/method/offset", _cpml_test_offset
);
425 adg_test_add_func("/cpml/segment/method/transform", _cpml_test_transform
);
426 adg_test_add_func("/cpml/segment/method/reverse", _cpml_test_reverse
);
427 adg_test_add_func("/cpml/segment/method/to-cairo", _cpml_test_to_cairo
);
428 adg_test_add_func("/cpml/segment/method/dump", _cpml_test_dump
);