adg: corrected AdgToyText test unit
[adg.git] / demo / cpml-demo.c
blob4f300141b75c34663d78e07e85cffe93dbc3aa3d
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 cairo_t *cr);
35 static void browsing_segment (GtkToggleButton*togglebutton,
36 gpointer user_data);
37 static void browsing_primitive (GtkToggleButton*togglebutton,
38 gpointer user_data);
39 static void browsing_reset (GtkButton *button,
40 gpointer user_data);
41 static void browsing_next (GtkButton *button,
42 gpointer user_data);
44 static gboolean arcs (GtkWidget *widget,
45 cairo_t *cr);
46 static void arc3p (cairo_t *cr,
47 double x1,
48 double y1,
49 double x2,
50 double y2,
51 double x3,
52 double y3);
54 static gboolean intersections (GtkWidget *widget,
55 cairo_t *cr);
56 static gboolean offset_curves (GtkWidget *widget,
57 cairo_t *cr);
58 static gboolean offset_segments (GtkWidget *widget,
59 cairo_t *cr);
61 static void circle_callback (cairo_t *cr);
62 static void piston_callback (cairo_t *cr);
63 static void curve1_callback (cairo_t *cr);
64 static void line1_callback (cairo_t *cr);
66 static struct {
67 GtkWidget *area;
68 cairo_path_t *cairo_path;
69 gboolean use_segment;
70 CpmlSegment segment;
71 CpmlPrimitive primitive;
72 } browsing_data = {
73 NULL,
76 static CpmlPair bezier_samples[][4] = {
77 { { 0, 0 }, { 0, 40 }, { 120, 40 }, { 120, 0 } }, /* Simmetric low */
78 { { 40, 0 }, { 40, 160 }, { 80, 160 }, { 80, 0 } }, /* Simmetric high */
79 { { 0, 0 }, { 33.1371, 33.1371 }, { 86.8629, 33.1371 }, { 120, 0 } },
80 /* Arc approximation */
81 { { 0, 0 }, { 70, 120 }, { 50, 120 }, { 120, 0 } }, /* Twisted controls */
83 { { 0, 0 }, { 0, 120 }, { 60, 120 }, { 120, 0 } }, /* Vertical p1-p2 */
84 { { 0, 0 }, { 60, 120 }, { 120, 120 }, { 120, 0 } },/* Vertical p3-p4 */
85 { { 0, 120 }, { 120, 120 }, { 120, 60 }, { 0, 0 } },/* Horizontal p1-p2 */
86 { { 0, 120 }, { 120, 60 }, { 120, 0 }, { 0, 0 } }, /* Horizontal p3-p4 */
88 { { 0, 0 }, { 0, 120 }, { 120, 120 }, { 120, 0 } }, /* Down */
89 { { 0, 120 }, { 120, 120 }, { 120, 0 }, { 0, 0 } }, /* Right */
90 { { 0, 120 }, { 0, 0 }, { 120, 0 }, { 120, 120 } }, /* Up */
91 { { 120, 120 }, { 0, 120 }, { 0, 0 }, { 120, 0 } }, /* Left */
93 { { 0, 60 }, { 60, 120 }, { 120, 60 }, { 60, 0 } }, /* Down-right */
94 { { 60, 120 }, { 120, 60 }, { 60, 0 }, { 0, 60 } }, /* Up-right */
95 { { 120, 60 }, { 60, 0 }, { 0, 60 }, { 60, 120 } }, /* Up-left */
96 { { 60, 0 }, { 0, 60 }, { 60, 120 }, { 120, 60 } }, /* Down-left*/
98 { { 0, 0 }, { 60, 0 }, { 60, 120 }, { 120, 120 } }, /* Step left */
99 { { 120, 0 }, { 60, 0 }, { 60, 120 }, { 0, 120 } }, /* Step right */
100 { { 0, 0 }, { 60, 90 }, { 90, 120 }, { 120, 90 } }, /* Unbalanced opened */
101 { { 0, 0 }, { 40, 120 }, { 120, 120 }, { 60, 80 } } /* Unbalanced closed */
104 static void (*path_samples[]) (cairo_t *cr) = {
105 circle_callback,
106 piston_callback,
107 curve1_callback,
108 line1_callback,
113 main(gint argc, gchar **argv)
115 gchar *path;
116 GtkBuilder *builder;
117 GError *error;
118 GtkWidget *window;
120 _demo_init(argc, argv);
121 parse_args(&argc, &argv);
123 path = _demo_file("cpml-demo.ui");
124 if (path == NULL) {
125 g_printerr(_("cpml-demo.ui not found!\n"));
126 return 1;
129 builder = gtk_builder_new();
130 error = NULL;
132 gtk_builder_set_translation_domain(builder, GETTEXT_PACKAGE);
133 gtk_builder_add_from_file(builder, path, &error);
134 g_free(path);
136 if (error != NULL) {
137 g_print("%s\n", error->message);
138 return 2;
141 window = (GtkWidget *) gtk_builder_get_object(builder, "wndMain");
143 /* Connect signals */
144 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
145 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
146 "draw", G_CALLBACK(browsing), NULL);
147 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingSegment"),
148 "toggled", G_CALLBACK(browsing_segment), NULL);
149 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingPrimitive"),
150 "toggled", G_CALLBACK(browsing_primitive), NULL);
151 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingReset"),
152 "clicked", G_CALLBACK(browsing_reset), NULL);
153 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingNext"),
154 "clicked", G_CALLBACK(browsing_next), NULL);
155 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
156 "draw", G_CALLBACK(arcs), NULL);
157 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
158 "draw", G_CALLBACK(intersections), NULL);
159 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
160 "draw", G_CALLBACK(offset_curves), NULL);
161 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
162 "draw", G_CALLBACK(offset_segments), NULL);
163 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
164 "clicked", G_CALLBACK(gtk_main_quit), NULL);
166 g_object_unref(builder);
168 gtk_widget_show_all(window);
169 gtk_main();
171 return 0;
175 /**********************************************
176 * Command line options parser
177 **********************************************/
179 static void
180 version(void)
182 g_print("cpml-demo " PACKAGE_VERSION "\n");
183 exit(0);
186 static void
187 parse_args(gint *p_argc, gchar **p_argv[])
189 GError *error = NULL;
190 GOptionEntry entries[] = {
191 {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
192 (gpointer) version, _("Display version information"), NULL},
193 {NULL}
196 gtk_init_with_args(p_argc, p_argv, _("- CPML demonstration program"),
197 entries, GETTEXT_PACKAGE, &error);
199 if (error != NULL) {
200 gint error_code = error->code;
202 g_printerr("%s\n", error->message);
204 g_error_free(error);
205 exit(error_code);
210 static cairo_path_t *
211 duplicate_and_stroke(cairo_t *cr)
213 cairo_path_t *path = cairo_copy_path(cr);
215 cairo_set_line_width(cr, 2.);
216 cairo_stroke(cr);
218 return path;
221 static void
222 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
224 cairo_append_path(cr, path);
225 cairo_path_destroy(path);
227 cairo_set_line_width(cr, 1.);
228 cairo_stroke(cr);
232 static void
233 browsing(GtkWidget *widget, cairo_t *cr)
235 if (browsing_data.area == NULL) {
236 int n;
238 /* Initialize browsing_data */
239 browsing_data.area = widget;
240 browsing_data.use_segment = TRUE;
242 /* Append all the path samples to the cairo context */
243 cairo_save(cr);
244 cairo_translate(cr, 270.5, -120.5);
245 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
246 if ((n & 1) == 0) {
247 cairo_translate(cr, -270., 240.);
248 } else {
249 cairo_translate(cr, 270., 0.);
252 (path_samples[n]) (cr);
255 cairo_restore(cr);
256 browsing_data.cairo_path = cairo_copy_path(cr);
257 cpml_segment_from_cairo(&browsing_data.segment,
258 browsing_data.cairo_path);
259 cpml_primitive_from_segment(&browsing_data.primitive,
260 &browsing_data.segment);
261 } else {
262 cairo_append_path(cr, browsing_data.cairo_path);
265 cairo_set_line_width(cr, 2.);
266 cairo_stroke(cr);
268 cairo_set_source_rgb(cr, 1., 0.4, 0.);
269 cairo_set_line_width(cr, 5.);
271 if (browsing_data.use_segment)
272 cpml_segment_to_cairo(&browsing_data.segment, cr);
273 else
274 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
276 cairo_stroke(cr);
279 static void
280 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
282 if (!gtk_toggle_button_get_active(togglebutton))
283 return;
285 browsing_data.use_segment = TRUE;
286 gtk_widget_queue_draw(browsing_data.area);
289 static void
290 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
292 if (!gtk_toggle_button_get_active(togglebutton))
293 return;
295 browsing_data.use_segment = FALSE;
296 gtk_widget_queue_draw(browsing_data.area);
299 static void
300 browsing_reset(GtkButton *button, gpointer user_data)
302 if (browsing_data.use_segment) {
303 cpml_segment_reset(&browsing_data.segment);
304 cpml_primitive_from_segment(&browsing_data.primitive,
305 &browsing_data.segment);
306 } else {
307 cpml_primitive_reset(&browsing_data.primitive);
310 gtk_widget_queue_draw(browsing_data.area);
313 static void
314 browsing_next(GtkButton *button, gpointer user_data)
316 if (browsing_data.use_segment) {
317 cpml_segment_next(&browsing_data.segment);
318 cpml_primitive_from_segment(&browsing_data.primitive,
319 &browsing_data.segment);
320 } else {
321 cpml_primitive_next(&browsing_data.primitive);
324 gtk_widget_queue_draw(browsing_data.area);
328 static gboolean
329 arcs(GtkWidget *widget, cairo_t *cr)
331 cairo_translate(cr, 100.5, 100.5);
332 arc3p(cr, 0, 0, 0, 120, 120, 120);
334 cairo_translate(cr, 200, 0.);
335 arc3p(cr, 0, 0, 120, 0, 120, 120);
337 cairo_translate(cr, 200, 0.);
338 arc3p(cr, 60, 0, 0, 120, 120, 120);
340 cairo_translate(cr, -400, 200);
341 arc3p(cr, 0, 50, -2, 85, 120, 0);
343 cairo_translate(cr, 200, 0);
344 arc3p(cr, -2, 85, 0, 50, 120, 0);
346 return FALSE;
349 static void
350 arc3p(cairo_t *cr, double x1, double y1,
351 double x2, double y2, double x3, double y3)
353 CpmlPrimitive arc;
354 cairo_path_data_t p[4];
355 CpmlPair center;
356 double r, start, end;
358 arc.segment = NULL;
359 arc.org = &p[0];
360 arc.data = &p[1];
362 p[1].header.type = CPML_ARC;
363 p[1].header.length = 3;
365 p[0].point.x = x1;
366 p[0].point.y = y1;
367 p[2].point.x = x2;
368 p[2].point.y = y2;
369 p[3].point.x = x3;
370 p[3].point.y = y3;
372 cpml_primitive_to_cairo(&arc, cr);
374 cairo_set_line_width(cr, 1.);
375 cairo_stroke(cr);
377 /* Add an arc generated by cairo, just for reference */
378 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
379 g_warning(_("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n"),
380 x1, y1, x2, y2, x3, y3);
381 return;
384 if (start < end)
385 cairo_arc(cr, center.x, center.y, r-5., start, end);
386 else
387 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
389 /* Show the inscribed triangle */
390 cairo_move_to(cr, x1, y1);
391 cairo_line_to(cr, x2, y2);
392 cairo_line_to(cr, x3, y3);
394 cairo_set_line_width(cr, 0.5);
395 cairo_stroke(cr);
399 static gboolean
400 intersections(GtkWidget *widget, cairo_t *cr)
402 cairo_path_t *path;
403 CpmlSegment segment1, segment2;
404 CpmlPair intersection;
406 cairo_translate(cr, 10.5, 120.5);
408 line1_callback(cr);
410 path = cairo_copy_path(cr);
412 cairo_set_line_width(cr, 1.);
413 cairo_stroke(cr);
415 cpml_segment_from_cairo(&segment1, path);
416 cpml_segment_from_cairo(&segment2, path);
418 while (cpml_segment_next(&segment2)) {
419 cpml_segment_put_intersections(&segment1, &segment2, 1, &intersection);
421 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
422 cairo_fill(cr);
424 cpml_segment_next(&segment1);
427 cairo_path_destroy(path);
428 return FALSE;
432 static gboolean
433 offset_curves(GtkWidget *widget, cairo_t *cr)
435 gint n;
436 CpmlPair *bezier;
437 cairo_path_t *path, *path_copy;
438 CpmlSegment segment;
439 CpmlPrimitive primitive;
440 CpmlPair pair;
441 CpmlVector vector;
442 double t;
444 /* Add the Bézier curve samples */
445 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
446 bezier = &bezier_samples[n][0];
448 /* The samples are arranged in a 4x? matrix of 200x150 cells */
449 if (n == 0)
450 cairo_translate(cr, 25., 25.);
451 else if (n % 4 == 0)
452 cairo_translate(cr, -600., 150.);
453 else
454 cairo_translate(cr, 200., 0.);
456 /* Draw the Bézier curve */
457 cairo_move_to(cr, bezier[0].x, bezier[0].y);
458 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
459 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
461 /* Create a copy, to be used after */
462 path_copy = cairo_copy_path(cr);
464 path = duplicate_and_stroke(cr);
465 cpml_segment_from_cairo(&segment, path);
466 cpml_segment_offset(&segment, 20.);
467 stroke_and_destroy(cr, path);
469 cpml_segment_from_cairo(&segment, path_copy);
470 cpml_primitive_from_segment(&primitive, &segment);
472 /* Checking cpml_curve_put_pair_at_time and cpml_curve_put_vector_at_time */
473 cairo_set_line_width(cr, 1.);
474 for (t = 0; t < 1; t += 0.1) {
475 cpml_curve_put_pair_at_time(&primitive, t, &pair);
476 cpml_curve_put_vector_at_time(&primitive, t, &vector);
477 cpml_vector_set_length(&vector, 20.);
478 cpml_vector_normal(&vector);
480 cairo_new_sub_path(cr);
481 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
482 cairo_fill(cr);
484 cairo_move_to(cr, pair.x, pair.y);
485 cairo_line_to(cr, pair.x + vector.x, pair.y + vector.y);
486 cairo_stroke(cr);
489 cairo_path_destroy(path_copy);
492 return FALSE;
496 static gboolean
497 offset_segments(GtkWidget *widget, cairo_t *cr)
499 cairo_path_t *path;
500 CpmlSegment segment;
501 int n;
503 cairo_translate(cr, 270.5, -120.5);
505 /* Offset the path samples */
506 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
507 if ((n & 1) == 0) {
508 cairo_translate(cr, -270., 240.);
509 } else {
510 cairo_translate(cr, 270., 0.);
513 /* Call the path callback */
514 (path_samples[n]) (cr);
516 path = duplicate_and_stroke(cr);
517 cpml_segment_from_cairo(&segment, path);
518 cpml_segment_offset(&segment, 15.);
519 stroke_and_destroy(cr, path);
522 return FALSE;
526 static void
527 circle_callback(cairo_t *cr)
529 cairo_new_sub_path(cr);
530 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
533 static void
534 piston_callback(cairo_t *cr)
536 cairo_path_t *old_path, *path;
537 cairo_matrix_t matrix;
538 CpmlSegment segment;
540 /* Save the previous path, if any */
541 old_path = cairo_copy_path(cr);
543 cairo_new_path(cr);
544 cairo_move_to(cr, 0., 46.5);
545 cairo_line_to(cr, 210., 46.5);
546 cairo_line_to(cr, 222.5, 35.);
547 cairo_line_to(cr, 270., 35.);
548 cairo_line_to(cr, 270., 56.);
549 cairo_line_to(cr, 273., 59.);
550 cairo_line_to(cr, 302., 59.);
551 cairo_line_to(cr, 305., 56.);
552 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
553 cairo_line_to(cr, 400., 32.5);
554 cairo_line_to(cr, 410., 22.5);
555 cairo_line_to(cr, 450., 22.5);
556 cairo_arc_negative(cr,
557 452., 34., 2., G_PI, G_PI_2);
558 cairo_line_to(cr, 460., 36.);
559 cairo_line_to(cr, 470., 30.);
560 cairo_line_to(cr, 472., 12.5);
562 /* Mirror a reversed copy of the current path on the y = 0 axis */
563 path = cairo_copy_path(cr);
564 cpml_segment_from_cairo(&segment, path);
566 cpml_segment_reverse(&segment);
567 cairo_matrix_init_scale(&matrix, 1., -1.);
568 cpml_segment_transform(&segment, &matrix);
570 /* Join the mirrored path to the old path... */
571 path->data[0].header.type = CPML_LINE;
572 cairo_append_path(cr, path);
573 cairo_path_destroy(path);
575 /* ...and close the shape */
576 cairo_close_path(cr);
578 /* Save the resulting path and clear the path memory */
579 path = cairo_copy_path(cr);
580 cairo_new_path(cr);
582 /* Restore the previous path and reappend the new path */
583 cairo_append_path(cr, old_path);
584 cairo_path_destroy(old_path);
585 cairo_append_path(cr, path);
586 cairo_path_destroy(path);
589 static void
590 curve1_callback(cairo_t *cr)
592 cairo_move_to(cr, 30., 0.);
593 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
594 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
595 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
596 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
599 static void
600 line1_callback(cairo_t *cr)
602 cairo_move_to(cr, 0, -50);
603 cairo_line_to(cr, 100, 50);
605 cairo_move_to(cr, 100, -50);
606 cairo_line_to(cr, 0, 50);
608 cairo_move_to(cr, 120, -50);
609 cairo_line_to(cr, 200, -10);
611 cairo_move_to(cr, 120, 50);
612 cairo_line_to(cr, 200, 10);
614 cairo_move_to(cr, 220, 0);
615 cairo_line_to(cr, 280, 0);
617 cairo_move_to(cr, 270, -40);
618 cairo_line_to(cr, 270, 20);
620 cairo_move_to(cr, 320, 60);
621 cairo_line_to(cr, 380, 60);
623 cairo_move_to(cr, 300, -40);
624 cairo_line_to(cr, 340, 0);
626 cairo_move_to(cr, 480, 10);
627 cairo_line_to(cr, 400, 40);
629 cairo_move_to(cr, 400, 40);
630 cairo_line_to(cr, 450, -40);