build: depends on cairo-gobject if introspection is enabled
[adg.git] / demo / cpml-demo-gtk2.c
blobb9ece871d36497b6d101db6abd34d0594437a89c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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 GdkEventExpose *event,
35 gpointer user_data);
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 void arcs (GtkWidget *widget,
46 GdkEventExpose *event,
47 gpointer user_data);
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 void intersections (GtkWidget *widget,
57 GdkEventExpose *event,
58 gpointer user_data);
60 static void offset_curves (GtkWidget *widget,
61 GdkEventExpose *event,
62 gpointer user_data);
64 static void offset_segments (GtkWidget *widget,
65 GdkEventExpose *event,
66 gpointer user_data);
68 static void circle_callback (cairo_t *cr);
69 static void piston_callback (cairo_t *cr);
70 static void curve1_callback (cairo_t *cr);
71 static void line1_callback (cairo_t *cr);
73 static struct {
74 GtkWidget *area;
75 cairo_path_t *cairo_path;
76 gboolean use_segment;
77 CpmlSegment segment;
78 CpmlPrimitive primitive;
79 } browsing_data = {
80 NULL,
83 static CpmlPair bezier_samples[][4] = {
84 { { 0, 0 }, { 0, 40 }, { 120, 40 }, { 120, 0 } }, /* Simmetric low */
85 { { 40, 0 }, { 40, 160 }, { 80, 160 }, { 80, 0 } }, /* Simmetric high */
86 { { 0, 0 }, { 33.1371, 33.1371 }, { 86.8629, 33.1371 }, { 120, 0 } },
87 /* Arc approximation */
88 { { 0, 0 }, { 70, 120 }, { 50, 120 }, { 120, 0 } }, /* Twisted controls */
90 { { 0, 0 }, { 0, 120 }, { 60, 120 }, { 120, 0 } }, /* Vertical p1-p2 */
91 { { 0, 0 }, { 60, 120 }, { 120, 120 }, { 120, 0 } },/* Vertical p3-p4 */
92 { { 0, 120 }, { 120, 120 }, { 120, 60 }, { 0, 0 } },/* Horizontal p1-p2 */
93 { { 0, 120 }, { 120, 60 }, { 120, 0 }, { 0, 0 } }, /* Horizontal p3-p4 */
95 { { 0, 0 }, { 0, 120 }, { 120, 120 }, { 120, 0 } }, /* Down */
96 { { 0, 120 }, { 120, 120 }, { 120, 0 }, { 0, 0 } }, /* Right */
97 { { 0, 120 }, { 0, 0 }, { 120, 0 }, { 120, 120 } }, /* Up */
98 { { 120, 120 }, { 0, 120 }, { 0, 0 }, { 120, 0 } }, /* Left */
100 { { 0, 60 }, { 60, 120 }, { 120, 60 }, { 60, 0 } }, /* Down-right */
101 { { 60, 120 }, { 120, 60 }, { 60, 0 }, { 0, 60 } }, /* Up-right */
102 { { 120, 60 }, { 60, 0 }, { 0, 60 }, { 60, 120 } }, /* Up-left */
103 { { 60, 0 }, { 0, 60 }, { 60, 120 }, { 120, 60 } }, /* Down-left*/
105 { { 0, 0 }, { 60, 0 }, { 60, 120 }, { 120, 120 } }, /* Step left */
106 { { 120, 0 }, { 60, 0 }, { 60, 120 }, { 0, 120 } }, /* Step right */
107 { { 0, 0 }, { 60, 90 }, { 90, 120 }, { 120, 90 } }, /* Unbalanced opened */
108 { { 0, 0 }, { 40, 120 }, { 120, 120 }, { 60, 80 } } /* Unbalanced closed */
111 static void (*path_samples[]) (cairo_t *cr) = {
112 circle_callback,
113 piston_callback,
114 curve1_callback,
115 line1_callback,
120 main(gint argc, gchar **argv)
122 gchar *path;
123 GtkBuilder *builder;
124 GError *error;
125 GtkWidget *window;
127 _demo_init(argc, argv);
128 parse_args(&argc, &argv);
130 path = _demo_file("cpml-demo.ui");
131 if (path == NULL) {
132 g_printerr(_("cpml-demo.ui not found!\n"));
133 return 1;
136 builder = gtk_builder_new();
137 error = NULL;
139 gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
140 gtk_builder_add_from_file(builder, path, &error);
141 if (error != NULL) {
142 g_print("%s\n", error->message);
143 return 2;
146 window = (GtkWidget *) gtk_builder_get_object(builder, "wndMain");
148 /* Connect signals */
149 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
150 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
151 "expose-event", G_CALLBACK(browsing), NULL);
152 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingSegment"),
153 "toggled", G_CALLBACK(browsing_segment), NULL);
154 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingPrimitive"),
155 "toggled", G_CALLBACK(browsing_primitive), NULL);
156 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingReset"),
157 "clicked", G_CALLBACK(browsing_reset), NULL);
158 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingNext"),
159 "clicked", G_CALLBACK(browsing_next), NULL);
160 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
161 "expose-event", G_CALLBACK(arcs), NULL);
162 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
163 "expose-event", G_CALLBACK(intersections), NULL);
164 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
165 "expose-event", G_CALLBACK(offset_curves), NULL);
166 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
167 "expose-event", G_CALLBACK(offset_segments), NULL);
168 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
169 "clicked", G_CALLBACK(gtk_main_quit), NULL);
171 g_object_unref(builder);
173 gtk_widget_show_all(window);
174 gtk_main();
176 return 0;
180 /**********************************************
181 * Command line options parser
182 **********************************************/
184 static void
185 version(void)
187 g_print("cpml-demo " PACKAGE_VERSION "\n");
188 exit(0);
191 static void
192 parse_args(gint *p_argc, gchar **p_argv[])
194 GError *error = NULL;
195 GOptionEntry entries[] = {
196 {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
197 (gpointer) version, _("Display version information"), NULL},
198 {NULL}
201 gtk_init_with_args(p_argc, p_argv, _("- CPML demonstration program"),
202 entries, GETTEXT_PACKAGE, &error);
204 if (error != NULL) {
205 gint error_code = error->code;
207 g_printerr("%s\n", error->message);
209 g_error_free(error);
210 exit(error_code);
215 static cairo_path_t *
216 duplicate_and_stroke(cairo_t *cr)
218 cairo_path_t *path = cairo_copy_path(cr);
220 cairo_set_line_width(cr, 2.);
221 cairo_stroke(cr);
223 return path;
226 static void
227 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
229 cairo_append_path(cr, path);
230 cairo_path_destroy(path);
232 cairo_set_line_width(cr, 1.);
233 cairo_stroke(cr);
237 static void
238 browsing(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
240 cairo_t *cr;
241 int n;
243 cr = gdk_cairo_create(widget->window);
245 if (browsing_data.area == NULL) {
246 /* Initialize browsing_data */
247 browsing_data.area = widget;
248 browsing_data.use_segment = TRUE;
250 /* Append all the path samples to the cairo context */
251 cairo_save(cr);
252 cairo_translate(cr, 270.5, -120.5);
253 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
254 if ((n & 1) == 0) {
255 cairo_translate(cr, -270., 240.);
256 } else {
257 cairo_translate(cr, 270., 0.);
260 (path_samples[n]) (cr);
263 cairo_restore(cr);
264 browsing_data.cairo_path = cairo_copy_path(cr);
265 cpml_segment_from_cairo(&browsing_data.segment,
266 browsing_data.cairo_path);
267 cpml_primitive_from_segment(&browsing_data.primitive,
268 &browsing_data.segment);
269 } else {
270 cairo_append_path(cr, browsing_data.cairo_path);
273 cairo_set_line_width(cr, 2.);
274 cairo_stroke(cr);
276 cairo_set_source_rgb(cr, 1., 0.4, 0.);
277 cairo_set_line_width(cr, 5.);
279 if (browsing_data.use_segment)
280 cpml_segment_to_cairo(&browsing_data.segment, cr);
281 else
282 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
284 cairo_stroke(cr);
285 cairo_destroy(cr);
288 static void
289 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
291 if (!gtk_toggle_button_get_active(togglebutton))
292 return;
294 browsing_data.use_segment = TRUE;
295 gtk_widget_queue_draw(browsing_data.area);
298 static void
299 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
301 if (!gtk_toggle_button_get_active(togglebutton))
302 return;
304 browsing_data.use_segment = FALSE;
305 gtk_widget_queue_draw(browsing_data.area);
308 static void
309 browsing_reset(GtkButton *button, gpointer user_data)
311 if (browsing_data.use_segment) {
312 cpml_segment_reset(&browsing_data.segment);
313 cpml_primitive_from_segment(&browsing_data.primitive,
314 &browsing_data.segment);
315 } else {
316 cpml_primitive_reset(&browsing_data.primitive);
319 gtk_widget_queue_draw(browsing_data.area);
322 static void
323 browsing_next(GtkButton *button, gpointer user_data)
325 if (browsing_data.use_segment) {
326 cpml_segment_next(&browsing_data.segment);
327 cpml_primitive_from_segment(&browsing_data.primitive,
328 &browsing_data.segment);
329 } else {
330 cpml_primitive_next(&browsing_data.primitive);
333 gtk_widget_queue_draw(browsing_data.area);
337 static void
338 arcs(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
340 cairo_t *cr = gdk_cairo_create(widget->window);
342 cairo_translate(cr, 100.5, 100.5);
343 arc3p(cr, 0, 0, 0, 120, 120, 120);
345 cairo_translate(cr, 200, 0.);
346 arc3p(cr, 0, 0, 120, 0, 120, 120);
348 cairo_translate(cr, 200, 0.);
349 arc3p(cr, 60, 0, 0, 120, 120, 120);
351 cairo_translate(cr, -400, 200);
352 arc3p(cr, 0, 50, -2, 85, 120, 0);
354 cairo_translate(cr, 200, 0);
355 arc3p(cr, -2, 85, 0, 50, 120, 0);
357 cairo_destroy(cr);
360 static void
361 arc3p(cairo_t *cr, double x1, double y1,
362 double x2, double y2, double x3, double y3)
364 CpmlPrimitive arc;
365 cairo_path_data_t p[4];
366 CpmlPair center;
367 double r, start, end;
369 arc.segment = NULL;
370 arc.org = &p[0];
371 arc.data = &p[1];
373 p[1].header.type = CPML_ARC;
374 p[1].header.length = 3;
376 p[0].point.x = x1;
377 p[0].point.y = y1;
378 p[2].point.x = x2;
379 p[2].point.y = y2;
380 p[3].point.x = x3;
381 p[3].point.y = y3;
383 cpml_primitive_to_cairo(&arc, cr);
385 cairo_set_line_width(cr, 1.);
386 cairo_stroke(cr);
388 /* Add an arc generated by cairo, just for reference */
389 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
390 g_warning(_("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n"),
391 x1, y1, x2, y2, x3, y3);
392 return;
395 if (start < end)
396 cairo_arc(cr, center.x, center.y, r-5., start, end);
397 else
398 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
400 /* Show the inscribed triangle */
401 cairo_move_to(cr, x1, y1);
402 cairo_line_to(cr, x2, y2);
403 cairo_line_to(cr, x3, y3);
405 cairo_set_line_width(cr, 0.5);
406 cairo_stroke(cr);
410 static void
411 intersections(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
413 cairo_t *cr;
414 cairo_path_t *path;
415 CpmlSegment segment1, segment2;
416 CpmlPair intersection;
418 cr = gdk_cairo_create(widget->window);
419 cairo_translate(cr, 10.5, 120.5);
421 line1_callback(cr);
423 path = cairo_copy_path(cr);
425 cairo_set_line_width(cr, 1.);
426 cairo_stroke(cr);
428 cpml_segment_from_cairo(&segment1, path);
429 cpml_segment_from_cairo(&segment2, path);
431 while (cpml_segment_next(&segment2)) {
432 cpml_segment_put_intersections(&segment1, &segment2, 1, &intersection);
434 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
435 cairo_fill(cr);
437 cpml_segment_next(&segment1);
440 cairo_path_destroy(path);
441 cairo_destroy(cr);
445 static void
446 offset_curves(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
448 cairo_t *cr;
449 gint n;
450 CpmlPair *bezier;
451 cairo_path_t *path, *path_copy;
452 CpmlSegment segment;
453 CpmlPrimitive primitive;
454 CpmlPair pair;
455 CpmlVector vector;
456 double t;
458 cr = gdk_cairo_create(widget->window);
460 /* Add the Bézier curve samples */
461 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
462 bezier = &bezier_samples[n][0];
464 /* The samples are arranged in a 4x? matrix of 200x150 cells */
465 if (n == 0)
466 cairo_translate(cr, 25., 25.);
467 else if (n % 4 == 0)
468 cairo_translate(cr, -600., 150.);
469 else
470 cairo_translate(cr, 200., 0.);
472 /* Draw the Bézier curve */
473 cairo_move_to(cr, bezier[0].x, bezier[0].y);
474 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
475 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
477 /* Create a copy, to be used after */
478 path_copy = cairo_copy_path(cr);
480 path = duplicate_and_stroke(cr);
481 cpml_segment_from_cairo(&segment, path);
482 cpml_segment_offset(&segment, 20.);
483 stroke_and_destroy(cr, path);
485 cpml_segment_from_cairo(&segment, path_copy);
486 cpml_primitive_from_segment(&primitive, &segment);
488 /* Checking cpml_curve_put_pair_at_time and cpml_curve_put_vector_at_time */
489 cairo_set_line_width(cr, 1.);
490 for (t = 0; t < 1; t += 0.1) {
491 cpml_curve_put_pair_at_time(&primitive, t, &pair);
492 cpml_curve_put_vector_at_time(&primitive, t, &vector);
493 cpml_vector_set_length(&vector, 20.);
494 cpml_vector_normal(&vector);
496 cairo_new_sub_path(cr);
497 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
498 cairo_fill(cr);
500 cairo_move_to(cr, pair.x, pair.y);
501 cairo_line_to(cr, pair.x + vector.x, pair.y + vector.y);
502 cairo_stroke(cr);
505 cairo_path_destroy(path_copy);
508 cairo_destroy(cr);
512 static void
513 offset_segments(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
515 cairo_t *cr;
516 cairo_path_t *path;
517 CpmlSegment segment;
518 int n;
520 cr = gdk_cairo_create(widget->window);
521 cairo_translate(cr, 270.5, -120.5);
523 /* Offset the path samples */
524 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
525 if ((n & 1) == 0) {
526 cairo_translate(cr, -270., 240.);
527 } else {
528 cairo_translate(cr, 270., 0.);
531 /* Call the path callback */
532 (path_samples[n]) (cr);
534 path = duplicate_and_stroke(cr);
535 cpml_segment_from_cairo(&segment, path);
536 cpml_segment_offset(&segment, 15.);
537 stroke_and_destroy(cr, path);
540 cairo_destroy(cr);
544 static void
545 circle_callback(cairo_t *cr)
547 cairo_new_sub_path(cr);
548 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
551 static void
552 piston_callback(cairo_t *cr)
554 cairo_path_t *old_path, *path;
555 cairo_matrix_t matrix;
556 CpmlSegment segment;
558 /* Save the previous path, if any */
559 old_path = cairo_copy_path(cr);
561 cairo_new_path(cr);
562 cairo_move_to(cr, 0., 46.5);
563 cairo_line_to(cr, 210., 46.5);
564 cairo_line_to(cr, 222.5, 35.);
565 cairo_line_to(cr, 270., 35.);
566 cairo_line_to(cr, 270., 56.);
567 cairo_line_to(cr, 273., 59.);
568 cairo_line_to(cr, 302., 59.);
569 cairo_line_to(cr, 305., 56.);
570 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
571 cairo_line_to(cr, 400., 32.5);
572 cairo_line_to(cr, 410., 22.5);
573 cairo_line_to(cr, 450., 22.5);
574 cairo_arc_negative(cr,
575 452., 34., 2., G_PI, G_PI_2);
576 cairo_line_to(cr, 460., 36.);
577 cairo_line_to(cr, 470., 30.);
578 cairo_line_to(cr, 472., 12.5);
580 /* Mirror a reversed copy of the current path on the y = 0 axis */
581 path = cairo_copy_path(cr);
582 cpml_segment_from_cairo(&segment, path);
584 cpml_segment_reverse(&segment);
585 cairo_matrix_init_scale(&matrix, 1., -1.);
586 cpml_segment_transform(&segment, &matrix);
588 /* Join the mirrored path to the old path... */
589 path->data[0].header.type = CPML_LINE;
590 cairo_append_path(cr, path);
591 cairo_path_destroy(path);
593 /* ...and close the shape */
594 cairo_close_path(cr);
596 /* Save the resulting path and clear the path memory */
597 path = cairo_copy_path(cr);
598 cairo_new_path(cr);
600 /* Restore the previous path and reappend the new path */
601 cairo_append_path(cr, old_path);
602 cairo_path_destroy(old_path);
603 cairo_append_path(cr, path);
604 cairo_path_destroy(path);
607 static void
608 curve1_callback(cairo_t *cr)
610 cairo_move_to(cr, 30., 0.);
611 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
612 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
613 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
614 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
617 static void
618 line1_callback(cairo_t *cr)
620 cairo_move_to(cr, 0, -50);
621 cairo_line_to(cr, 100, 50);
623 cairo_move_to(cr, 100, -50);
624 cairo_line_to(cr, 0, 50);
626 cairo_move_to(cr, 120, -50);
627 cairo_line_to(cr, 200, -10);
629 cairo_move_to(cr, 120, 50);
630 cairo_line_to(cr, 200, 10);
632 cairo_move_to(cr, 220, 0);
633 cairo_line_to(cr, 280, 0);
635 cairo_move_to(cr, 270, -40);
636 cairo_line_to(cr, 270, 20);
638 cairo_move_to(cr, 320, 60);
639 cairo_line_to(cr, 380, 60);
641 cairo_move_to(cr, 300, -40);
642 cairo_line_to(cr, 340, 0);
644 cairo_move_to(cr, 480, 10);
645 cairo_line_to(cr, 400, 40);
647 cairo_move_to(cr, 400, 40);
648 cairo_line_to(cr, 450, -40);