Fix doc typo
[adg.git] / demo / cpml-demo.c
blob008430cdf4d75bd5b2bfeb4759dc82617056fbb5
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2017 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.
20 #include "demo.h"
21 #include <cpml.h>
22 #include <math.h>
23 #include <gtk/gtk.h>
26 static void parse_args (gint *p_argc,
27 gchar **p_argv[]);
28 static cairo_path_t *
29 duplicate_and_stroke (cairo_t *cr);
30 static void stroke_and_destroy (cairo_t *cr,
31 cairo_path_t *path);
33 static void browsing (GtkWidget *widget,
34 cairo_t *cr);
36 static void browsing_segment (GtkToggleButton*togglebutton,
37 gpointer user_data);
38 static void browsing_primitive (GtkToggleButton*togglebutton,
39 gpointer user_data);
40 static void browsing_reset (GtkButton *button,
41 gpointer user_data);
42 static void browsing_next (GtkButton *button,
43 gpointer user_data);
45 static gboolean arcs (GtkWidget *widget,
46 cairo_t *cr);
48 static void arc3p (cairo_t *cr,
49 double x1,
50 double y1,
51 double x2,
52 double y2,
53 double x3,
54 double y3);
56 static gboolean intersections (GtkWidget *widget,
57 cairo_t *cr);
59 static void algorithm_changed (GtkRadioButton *button,
60 GtkWidget *area);
61 static gboolean offset_curves (GtkWidget *widget,
62 cairo_t *cr);
64 static gboolean offset_segments (GtkWidget *widget,
65 cairo_t *cr);
67 #ifdef GTK2_ENABLED
69 static void
70 browsing_gtk2(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
72 cairo_t *cr = gdk_cairo_create(widget->window);
73 browsing(widget, cr);
74 cairo_destroy(cr);
77 static void
78 arcs_gtk2(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
80 cairo_t *cr = gdk_cairo_create(widget->window);
81 arcs(widget, cr);
82 cairo_destroy(cr);
85 static void
86 intersections_gtk2(GtkWidget *widget)
88 cairo_t *cr = gdk_cairo_create(widget->window);
89 intersections(widget, cr);
90 cairo_destroy(cr);
93 static void
94 offset_curves_gtk2(GtkWidget *widget)
96 cairo_t *cr = gdk_cairo_create(widget->window);
97 offset_curves(widget, cr);
98 cairo_destroy(cr);
101 static void
102 offset_segments_gtk2(GtkWidget *widget)
104 cairo_t *cr = gdk_cairo_create(widget->window);
105 offset_segments(widget, cr);
106 cairo_destroy(cr);
109 #endif
111 static void circle_callback (cairo_t *cr);
112 static void piston_callback (cairo_t *cr);
113 static void curve1_callback (cairo_t *cr);
114 static void line1_callback (cairo_t *cr);
116 static struct {
117 GtkWidget *area;
118 cairo_path_t *cairo_path;
119 gboolean use_segment;
120 CpmlSegment segment;
121 CpmlPrimitive primitive;
122 } browsing_data = {
123 NULL,
126 static CpmlPair bezier_samples[][4] = {
127 { { 0, 0 }, { 0, 40 }, { 120, 40 }, { 120, 0 } }, /* Simmetric low */
128 { { 40, 0 }, { 40, 160 }, { 80, 160 }, { 80, 0 } }, /* Simmetric high */
129 { { 0, 0 }, { 33.1371, 33.1371 }, { 86.8629, 33.1371 }, { 120, 0 } },
130 /* Arc approximation */
131 { { 0, 0 }, { 70, 120 }, { 50, 120 }, { 120, 0 } }, /* Twisted controls */
133 { { 0, 0 }, { 0, 120 }, { 60, 120 }, { 120, 0 } }, /* Vertical p1-p2 */
134 { { 0, 0 }, { 60, 120 }, { 120, 120 }, { 120, 0 } },/* Vertical p3-p4 */
135 { { 0, 120 }, { 120, 120 }, { 120, 60 }, { 0, 0 } },/* Horizontal p1-p2 */
136 { { 0, 120 }, { 120, 60 }, { 120, 0 }, { 0, 0 } }, /* Horizontal p3-p4 */
138 { { 0, 0 }, { 0, 120 }, { 120, 120 }, { 120, 0 } }, /* Down */
139 { { 0, 120 }, { 120, 120 }, { 120, 0 }, { 0, 0 } }, /* Right */
140 { { 0, 120 }, { 0, 0 }, { 120, 0 }, { 120, 120 } }, /* Up */
141 { { 120, 120 }, { 0, 120 }, { 0, 0 }, { 120, 0 } }, /* Left */
143 { { 0, 60 }, { 60, 120 }, { 120, 60 }, { 60, 0 } }, /* Down-right */
144 { { 60, 120 }, { 120, 60 }, { 60, 0 }, { 0, 60 } }, /* Up-right */
145 { { 120, 60 }, { 60, 0 }, { 0, 60 }, { 60, 120 } }, /* Up-left */
146 { { 60, 0 }, { 0, 60 }, { 60, 120 }, { 120, 60 } }, /* Down-left*/
148 { { 0, 0 }, { 60, 0 }, { 60, 120 }, { 120, 120 } }, /* Step left */
149 { { 120, 0 }, { 60, 0 }, { 60, 120 }, { 0, 120 } }, /* Step right */
150 { { 0, 0 }, { 60, 90 }, { 90, 120 }, { 120, 90 } }, /* Unbalanced opened */
151 { { 0, 0 }, { 40, 120 }, { 120, 120 }, { 60, 80 } } /* Unbalanced closed */
154 static void (*path_samples[]) (cairo_t *cr) = {
155 circle_callback,
156 piston_callback,
157 curve1_callback,
158 line1_callback,
161 static void
162 switch_page(GtkTreeSelection *selection, GtkNotebook *notebook)
164 GList *list = gtk_tree_selection_get_selected_rows(selection, NULL);
165 if (list != NULL) {
166 GtkTreePath *path = g_list_first(list)->data;
167 gint page = path != NULL ? *gtk_tree_path_get_indices(path) : -1;
168 g_list_free_full(list, (GDestroyNotify) gtk_tree_path_free);
169 if (page >= 0)
170 gtk_notebook_set_current_page(notebook, page);
175 main(gint argc, gchar **argv)
177 gchar *path;
178 GtkBuilder *builder;
179 GError *error;
180 GtkWidget *window;
181 gchar *icons_dir;
183 _demo_init(argc, argv);
184 parse_args(&argc, &argv);
186 /* Prepend the package icons path */
187 if (is_installed) {
188 #ifdef G_OS_WIN32
189 icons_dir = g_build_filename(basedir, PKGDATADIR, "icons", NULL);
190 #else
191 icons_dir = g_strdup(PKGDATADIR "/icons");
192 #endif
193 } else {
194 icons_dir = g_strdup(SRCDIR "/icons");
197 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), icons_dir);
198 g_free(icons_dir);
200 path = _demo_file("cpml-demo.ui");
201 if (path == NULL) {
202 g_printerr(_("cpml-demo.ui not found!\n"));
203 return 1;
206 builder = gtk_builder_new();
207 error = NULL;
209 gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
210 gtk_builder_add_from_file(builder, path, &error);
211 g_free(path);
213 if (error != NULL) {
214 g_print("%s\n", error->message);
215 return 2;
218 window = (GtkWidget *) gtk_builder_get_object(builder, "wndMain");
220 /* Connect signals */
221 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
222 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_builder_get_object(builder, "tvPages"))),
223 "changed", G_CALLBACK(switch_page),
224 gtk_builder_get_object(builder, "nbPages"));
225 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingSegment"),
226 "toggled", G_CALLBACK(browsing_segment), NULL);
227 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingPrimitive"),
228 "toggled", G_CALLBACK(browsing_primitive), NULL);
229 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingReset"),
230 "clicked", G_CALLBACK(browsing_reset), NULL);
231 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingNext"),
232 "clicked", G_CALLBACK(browsing_next), NULL);
233 g_signal_connect(gtk_builder_get_object(builder, "optAlgorithmDefault"),
234 "toggled", G_CALLBACK(algorithm_changed),
235 gtk_builder_get_object(builder, "areaOffsetCurves"));
236 g_signal_connect(gtk_builder_get_object(builder, "optAlgorithmBaioca"),
237 "toggled", G_CALLBACK(algorithm_changed),
238 gtk_builder_get_object(builder, "areaOffsetCurves"));
239 g_signal_connect(gtk_builder_get_object(builder, "optAlgorithmHandcraft"),
240 "toggled", G_CALLBACK(algorithm_changed),
241 gtk_builder_get_object(builder, "areaOffsetCurves"));
242 g_signal_connect(gtk_builder_get_object(builder, "optAlgorithmGeometrical"),
243 "toggled", G_CALLBACK(algorithm_changed),
244 gtk_builder_get_object(builder, "areaOffsetCurves"));
245 #ifdef GTK2_ENABLED
246 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
247 "expose-event", G_CALLBACK(browsing_gtk2), NULL);
248 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
249 "expose-event", G_CALLBACK(arcs_gtk2), NULL);
250 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
251 "expose-event", G_CALLBACK(intersections_gtk2), NULL);
252 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
253 "expose-event", G_CALLBACK(offset_curves_gtk2), NULL);
254 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
255 "expose-event", G_CALLBACK(offset_segments_gtk2), NULL);
256 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
257 "clicked", G_CALLBACK(gtk_main_quit), NULL);
258 #else
259 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
260 "draw", G_CALLBACK(browsing), NULL);
261 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
262 "draw", G_CALLBACK(arcs), NULL);
263 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
264 "draw", G_CALLBACK(intersections), NULL);
265 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
266 "draw", G_CALLBACK(offset_curves), NULL);
267 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
268 "draw", G_CALLBACK(offset_segments), NULL);
269 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
270 "clicked", G_CALLBACK(gtk_main_quit), NULL);
271 #endif
273 g_object_unref(builder);
275 gtk_widget_show_all(window);
276 gtk_main();
278 return 0;
282 /**********************************************
283 * Command line options parser
284 **********************************************/
286 static void
287 version(void)
289 g_print("cpml-demo " PACKAGE_VERSION "\n");
290 exit(0);
293 static void
294 parse_args(gint *p_argc, gchar **p_argv[])
296 GError *error = NULL;
297 GOptionEntry entries[] = {
298 {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
299 (gpointer) version, _("Display version information"), NULL},
300 {NULL}
303 gtk_init_with_args(p_argc, p_argv, _("- CPML demonstration program"),
304 entries, GETTEXT_PACKAGE, &error);
306 if (error != NULL) {
307 gint error_code = error->code;
309 g_printerr("%s\n", error->message);
311 g_error_free(error);
312 exit(error_code);
317 static cairo_path_t *
318 duplicate_and_stroke(cairo_t *cr)
320 cairo_path_t *path = cairo_copy_path(cr);
322 cairo_set_line_width(cr, 2.);
323 cairo_stroke(cr);
325 return path;
328 static void
329 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
331 cairo_append_path(cr, path);
332 cairo_path_destroy(path);
334 cairo_set_line_width(cr, 1.);
335 cairo_stroke(cr);
339 static void
340 browsing(GtkWidget *widget, cairo_t *cr)
342 if (browsing_data.area == NULL) {
343 int n;
345 /* Initialize browsing_data */
346 browsing_data.area = widget;
347 browsing_data.use_segment = TRUE;
349 /* Append all the path samples to the cairo context */
350 cairo_save(cr);
351 cairo_translate(cr, 270.5, -120.5);
352 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
353 if ((n & 1) == 0) {
354 cairo_translate(cr, -270., 240.);
355 } else {
356 cairo_translate(cr, 270., 0.);
359 (path_samples[n]) (cr);
362 cairo_restore(cr);
363 browsing_data.cairo_path = cairo_copy_path(cr);
364 cpml_segment_from_cairo(&browsing_data.segment,
365 browsing_data.cairo_path);
366 cpml_primitive_from_segment(&browsing_data.primitive,
367 &browsing_data.segment);
368 } else {
369 cairo_append_path(cr, browsing_data.cairo_path);
372 cairo_set_line_width(cr, 2.);
373 cairo_stroke(cr);
375 cairo_set_source_rgb(cr, 1., 0.4, 0.);
376 cairo_set_line_width(cr, 5.);
378 if (browsing_data.use_segment)
379 cpml_segment_to_cairo(&browsing_data.segment, cr);
380 else
381 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
383 cairo_stroke(cr);
386 static void
387 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
389 if (!gtk_toggle_button_get_active(togglebutton))
390 return;
392 browsing_data.use_segment = TRUE;
393 gtk_widget_queue_draw(browsing_data.area);
396 static void
397 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
399 if (!gtk_toggle_button_get_active(togglebutton))
400 return;
402 browsing_data.use_segment = FALSE;
403 gtk_widget_queue_draw(browsing_data.area);
406 static void
407 browsing_reset(GtkButton *button, gpointer user_data)
409 if (browsing_data.use_segment) {
410 cpml_segment_reset(&browsing_data.segment);
411 cpml_primitive_from_segment(&browsing_data.primitive,
412 &browsing_data.segment);
413 } else {
414 cpml_primitive_reset(&browsing_data.primitive);
417 gtk_widget_queue_draw(browsing_data.area);
420 static void
421 browsing_next(GtkButton *button, gpointer user_data)
423 if (browsing_data.use_segment) {
424 cpml_segment_next(&browsing_data.segment);
425 cpml_primitive_from_segment(&browsing_data.primitive,
426 &browsing_data.segment);
427 } else {
428 cpml_primitive_next(&browsing_data.primitive);
431 gtk_widget_queue_draw(browsing_data.area);
435 static gboolean
436 arcs(GtkWidget *widget, cairo_t *cr)
438 cairo_translate(cr, 100.5, 100.5);
439 arc3p(cr, 0, 0, 0, 120, 120, 120);
441 cairo_translate(cr, 200, 0.);
442 arc3p(cr, 0, 0, 120, 0, 120, 120);
444 cairo_translate(cr, 200, 0.);
445 arc3p(cr, 60, 0, 0, 120, 120, 120);
447 cairo_translate(cr, -400, 200);
448 arc3p(cr, 0, 50, -2, 85, 120, 0);
450 cairo_translate(cr, 200, 0);
451 arc3p(cr, -2, 85, 0, 50, 120, 0);
453 return FALSE;
456 static void
457 arc3p(cairo_t *cr, double x1, double y1,
458 double x2, double y2, double x3, double y3)
460 CpmlPrimitive arc;
461 cairo_path_data_t p[4];
462 CpmlPair center;
463 double r, start, end;
465 arc.segment = NULL;
466 arc.org = &p[0];
467 arc.data = &p[1];
469 p[1].header.type = CPML_ARC;
470 p[1].header.length = 3;
472 p[0].point.x = x1;
473 p[0].point.y = y1;
474 p[2].point.x = x2;
475 p[2].point.y = y2;
476 p[3].point.x = x3;
477 p[3].point.y = y3;
479 cpml_primitive_to_cairo(&arc, cr);
481 cairo_set_line_width(cr, 1.);
482 cairo_stroke(cr);
484 /* Add an arc generated by cairo, just for reference */
485 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
486 g_warning(_("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n"),
487 x1, y1, x2, y2, x3, y3);
488 return;
491 if (start < end)
492 cairo_arc(cr, center.x, center.y, r-5., start, end);
493 else
494 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
496 /* Show the inscribed triangle */
497 cairo_move_to(cr, x1, y1);
498 cairo_line_to(cr, x2, y2);
499 cairo_line_to(cr, x3, y3);
501 cairo_set_line_width(cr, 0.5);
502 cairo_stroke(cr);
506 static gboolean
507 intersections(GtkWidget *widget, cairo_t *cr)
509 cairo_path_t *path;
510 CpmlSegment segment1, segment2;
511 CpmlPair intersection;
513 cairo_translate(cr, 10.5, 120.5);
515 line1_callback(cr);
517 path = cairo_copy_path(cr);
519 cairo_set_line_width(cr, 1.);
520 cairo_stroke(cr);
522 cpml_segment_from_cairo(&segment1, path);
523 cpml_segment_from_cairo(&segment2, path);
525 while (cpml_segment_next(&segment2)) {
526 cpml_segment_put_intersections(&segment1, &segment2, 1, &intersection);
528 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
529 cairo_fill(cr);
531 cpml_segment_next(&segment1);
534 cairo_path_destroy(path);
535 return FALSE;
539 static void
540 algorithm_changed(GtkRadioButton *button, GtkWidget *area)
542 const gchar *button_name;
543 CpmlCurveOffsetAlgorithm new_algorithm;
545 if (! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
546 return;
549 button_name = gtk_widget_get_name(GTK_WIDGET(button));
551 if (g_strcmp0(button_name, "DEFAULT") == 0) {
552 new_algorithm = CPML_CURVE_OFFSET_ALGORITHM_DEFAULT;
553 } else if (g_strcmp0(button_name, "BAIOCA") == 0) {
554 new_algorithm = CPML_CURVE_OFFSET_ALGORITHM_BAIOCA;
555 } else if (g_strcmp0(button_name, "HANDCRAFT") == 0) {
556 new_algorithm = CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT;
557 } else if (g_strcmp0(button_name, "GEOMETRICAL") == 0) {
558 new_algorithm = CPML_CURVE_OFFSET_ALGORITHM_GEOMETRICAL;
559 } else {
560 g_warning("Unknown offset algorithm name (%s)", button_name);
561 new_algorithm = CPML_CURVE_OFFSET_ALGORITHM_NONE;
564 cpml_curve_offset_algorithm(new_algorithm);
565 gtk_widget_queue_draw(area);
568 static gboolean
569 offset_curves(GtkWidget *widget, cairo_t *cr)
571 gint n;
572 CpmlPair *bezier;
573 cairo_path_t *path, *path_copy;
574 CpmlSegment segment;
575 CpmlPrimitive primitive;
576 CpmlPair pair;
577 double t;
579 /* Add the Bézier curve samples */
580 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
581 bezier = &bezier_samples[n][0];
583 /* The samples are arranged in a 4x? matrix of 200x150 cells */
584 if (n == 0)
585 cairo_translate(cr, 25., 25.);
586 else if (n % 4 == 0)
587 cairo_translate(cr, -600., 150.);
588 else
589 cairo_translate(cr, 200., 0.);
591 /* Draw the Bézier curve */
592 cairo_move_to(cr, bezier[0].x, bezier[0].y);
593 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
594 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
596 /* Create a copy, to be used after */
597 path_copy = cairo_copy_path(cr);
599 path = duplicate_and_stroke(cr);
600 cpml_segment_from_cairo(&segment, path);
601 cpml_segment_offset(&segment, 20.);
602 stroke_and_destroy(cr, path);
604 cpml_segment_from_cairo(&segment, path_copy);
605 cpml_primitive_from_segment(&primitive, &segment);
607 /* Draw the rays for visual debugging */
608 cairo_set_line_width(cr, 1.);
609 for (t = 0; t < 1; t += 0.1) {
610 cpml_curve_put_pair_at_time(&primitive, t, &pair);
612 cairo_new_sub_path(cr);
613 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
614 cairo_fill(cr);
616 cairo_move_to(cr, pair.x, pair.y);
617 cpml_curve_put_offset_at_time(&primitive, t, 20., &pair);
618 cairo_line_to(cr, pair.x, pair.y);
619 cairo_stroke(cr);
622 cairo_path_destroy(path_copy);
625 return FALSE;
629 static gboolean
630 offset_segments(GtkWidget *widget, cairo_t *cr)
632 cairo_path_t *path;
633 CpmlSegment segment;
634 int n;
636 cairo_translate(cr, 270.5, -120.5);
638 /* Offset the path samples */
639 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
640 if ((n & 1) == 0) {
641 cairo_translate(cr, -270., 240.);
642 } else {
643 cairo_translate(cr, 270., 0.);
646 /* Call the path callback */
647 (path_samples[n]) (cr);
649 path = duplicate_and_stroke(cr);
650 cpml_segment_from_cairo(&segment, path);
651 cpml_segment_offset(&segment, 15.);
652 stroke_and_destroy(cr, path);
655 return FALSE;
659 static void
660 circle_callback(cairo_t *cr)
662 cairo_new_sub_path(cr);
663 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
666 static void
667 piston_callback(cairo_t *cr)
669 cairo_path_t *old_path, *path;
670 cairo_matrix_t matrix;
671 CpmlSegment segment;
673 /* Save the previous path, if any */
674 old_path = cairo_copy_path(cr);
676 cairo_new_path(cr);
677 cairo_move_to(cr, 0., 46.5);
678 cairo_line_to(cr, 210., 46.5);
679 cairo_line_to(cr, 222.5, 35.);
680 cairo_line_to(cr, 270., 35.);
681 cairo_line_to(cr, 270., 56.);
682 cairo_line_to(cr, 273., 59.);
683 cairo_line_to(cr, 302., 59.);
684 cairo_line_to(cr, 305., 56.);
685 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
686 cairo_line_to(cr, 400., 32.5);
687 cairo_line_to(cr, 410., 22.5);
688 cairo_line_to(cr, 450., 22.5);
689 cairo_arc_negative(cr,
690 452., 34., 2., G_PI, G_PI_2);
691 cairo_line_to(cr, 460., 36.);
692 cairo_line_to(cr, 470., 30.);
693 cairo_line_to(cr, 472., 12.5);
695 /* Mirror a reversed copy of the current path on the y = 0 axis */
696 path = cairo_copy_path(cr);
697 cpml_segment_from_cairo(&segment, path);
699 cpml_segment_reverse(&segment);
700 cairo_matrix_init_scale(&matrix, 1., -1.);
701 cpml_segment_transform(&segment, &matrix);
703 /* Join the mirrored path to the old path... */
704 path->data[0].header.type = CPML_LINE;
705 cairo_append_path(cr, path);
706 cairo_path_destroy(path);
708 /* ...and close the shape */
709 cairo_close_path(cr);
711 /* Save the resulting path and clear the path memory */
712 path = cairo_copy_path(cr);
713 cairo_new_path(cr);
715 /* Restore the previous path and reappend the new path */
716 cairo_append_path(cr, old_path);
717 cairo_path_destroy(old_path);
718 cairo_append_path(cr, path);
719 cairo_path_destroy(path);
722 static void
723 curve1_callback(cairo_t *cr)
725 cairo_move_to(cr, 30., 0.);
726 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
727 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
728 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
729 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
732 static void
733 line1_callback(cairo_t *cr)
735 cairo_move_to(cr, 0, -50);
736 cairo_line_to(cr, 100, 50);
738 cairo_move_to(cr, 100, -50);
739 cairo_line_to(cr, 0, 50);
741 cairo_move_to(cr, 120, -50);
742 cairo_line_to(cr, 200, -10);
744 cairo_move_to(cr, 120, 50);
745 cairo_line_to(cr, 200, 10);
747 cairo_move_to(cr, 220, 0);
748 cairo_line_to(cr, 280, 0);
750 cairo_move_to(cr, 270, -40);
751 cairo_line_to(cr, 270, 20);
753 cairo_move_to(cr, 320, 60);
754 cairo_line_to(cr, 380, 60);
756 cairo_move_to(cr, 300, -40);
757 cairo_line_to(cr, 340, 0);
759 cairo_move_to(cr, 480, 10);
760 cairo_line_to(cr, 400, 40);
762 cairo_move_to(cr, 400, 40);
763 cairo_line_to(cr, 450, -40);