[demo] Improved data file resolution
[adg.git] / demo / cpml-demo.c
blob087e8ca2d7b111ecde95efcf72e78d9b22290ddb
1 #include "demo.h"
2 #include <cpml.h>
3 #include <math.h>
6 static void parse_args (gint *p_argc,
7 gchar **p_argv[]);
8 static cairo_path_t *
9 duplicate_and_stroke (cairo_t *cr);
10 static void stroke_and_destroy (cairo_t *cr,
11 cairo_path_t *path);
13 static void browsing (GtkWidget *widget,
14 GdkEventExpose *event,
15 gpointer user_data);
16 static void browsing_segment (GtkToggleButton*togglebutton,
17 gpointer user_data);
18 static void browsing_primitive (GtkToggleButton*togglebutton,
19 gpointer user_data);
20 static void browsing_reset (GtkButton *button,
21 gpointer user_data);
22 static void browsing_next (GtkButton *button,
23 gpointer user_data);
25 static void arcs (GtkWidget *widget,
26 GdkEventExpose *event,
27 gpointer user_data);
28 static void arc3p (cairo_t *cr,
29 double x1,
30 double y1,
31 double x2,
32 double y2,
33 double x3,
34 double y3);
36 static void intersections (GtkWidget *widget,
37 GdkEventExpose *event,
38 gpointer user_data);
40 static void offset_curves (GtkWidget *widget,
41 GdkEventExpose *event,
42 gpointer user_data);
44 static void offset_segments (GtkWidget *widget,
45 GdkEventExpose *event,
46 gpointer user_data);
48 static void circle_callback (cairo_t *cr);
49 static void piston_callback (cairo_t *cr);
50 static void curve1_callback (cairo_t *cr);
51 static void line1_callback (cairo_t *cr);
53 static struct {
54 GtkWidget *area;
55 cairo_path_t *cairo_path;
56 gboolean use_segment;
57 CpmlSegment segment;
58 CpmlPrimitive primitive;
59 } browsing_data = {
60 NULL,
63 static CpmlPair bezier_samples[][4] = {
64 { { 0, 0 }, { 0, 40 }, { 120, 40 }, { 120, 0 } }, /* Simmetric low */
65 { { 40, 0 }, { 40, 160 }, { 80, 160 }, { 80, 0 } }, /* Simmetric high */
66 { { 0, 0 }, { 33.1371, 33.1371 }, { 86.8629, 33.1371 }, { 120, 0 } },
67 /* Arc approximation */
68 { { 0, 0 }, { 70, 120 }, { 50, 120 }, { 120, 0 } }, /* Twisted controls */
70 { { 0, 0 }, { 0, 120 }, { 60, 120 }, { 120, 0 } }, /* Vertical p1-p2 */
71 { { 0, 0 }, { 60, 120 }, { 120, 120 }, { 120, 0 } },/* Vertical p3-p4 */
72 { { 0, 120 }, { 120, 120 }, { 120, 60 }, { 0, 0 } },/* Horizontal p1-p2 */
73 { { 0, 120 }, { 120, 60 }, { 120, 0 }, { 0, 0 } }, /* Horizontal p3-p4 */
75 { { 0, 0 }, { 0, 120 }, { 120, 120 }, { 120, 0 } }, /* Down */
76 { { 0, 120 }, { 120, 120 }, { 120, 0 }, { 0, 0 } }, /* Right */
77 { { 0, 120 }, { 0, 0 }, { 120, 0 }, { 120, 120 } }, /* Up */
78 { { 120, 120 }, { 0, 120 }, { 0, 0 }, { 120, 0 } }, /* Left */
80 { { 0, 60 }, { 60, 120 }, { 120, 60 }, { 60, 0 } }, /* Down-right */
81 { { 60, 120 }, { 120, 60 }, { 60, 0 }, { 0, 60 } }, /* Up-right */
82 { { 120, 60 }, { 60, 0 }, { 0, 60 }, { 60, 120 } }, /* Up-left */
83 { { 60, 0 }, { 0, 60 }, { 60, 120 }, { 120, 60 } }, /* Down-left*/
85 { { 0, 0 }, { 60, 0 }, { 60, 120 }, { 120, 120 } }, /* Step left */
86 { { 120, 0 }, { 60, 0 }, { 60, 120 }, { 0, 120 } }, /* Step right */
87 { { 0, 0 }, { 60, 90 }, { 90, 120 }, { 120, 90 } }, /* Unbalanced opened */
88 { { 0, 0 }, { 40, 120 }, { 120, 120 }, { 60, 80 } } /* Unbalanced closed */
91 static void (*path_samples[]) (cairo_t *cr) = {
92 circle_callback,
93 piston_callback,
94 curve1_callback,
95 line1_callback,
99 int
100 main(gint argc, gchar **argv)
102 gchar *path;
103 GtkBuilder *builder;
104 GError *error;
105 GtkWidget *window;
107 parse_args(&argc, &argv);
109 path = demo_find_data_file("cpml-demo.ui", argv[0]);
110 if (path == NULL) {
111 g_print("cpml-demo.ui not found!\n");
112 return 1;
115 builder = gtk_builder_new();
116 error = NULL;
118 gtk_builder_add_from_file(builder, path, &error);
119 if (error != NULL) {
120 g_print("%s\n", error->message);
121 return 2;
124 window = (GtkWidget *) gtk_builder_get_object(builder, "wndMain");
126 /* Connect signals */
127 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
128 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
129 "expose-event", G_CALLBACK(browsing), NULL);
130 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingSegment"),
131 "toggled", G_CALLBACK(browsing_segment), NULL);
132 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingPrimitive"),
133 "toggled", G_CALLBACK(browsing_primitive), NULL);
134 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingReset"),
135 "clicked", G_CALLBACK(browsing_reset), NULL);
136 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingNext"),
137 "clicked", G_CALLBACK(browsing_next), NULL);
138 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
139 "expose-event", G_CALLBACK(arcs), NULL);
140 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
141 "expose-event", G_CALLBACK(intersections), NULL);
142 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
143 "expose-event", G_CALLBACK(offset_curves), NULL);
144 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
145 "expose-event", G_CALLBACK(offset_segments), NULL);
146 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
147 "clicked", G_CALLBACK(gtk_main_quit), NULL);
149 g_object_unref(builder);
151 gtk_widget_show_all(window);
152 gtk_main();
154 return 0;
158 /**********************************************
159 * Command line options parser
160 **********************************************/
162 static void
163 version(void)
165 g_print("cpml-demo " PACKAGE_VERSION "\n");
166 exit(0);
169 static void
170 parse_args(gint *p_argc, gchar **p_argv[])
172 GError *error = NULL;
173 GOptionEntry entries[] = {
174 {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, version,
175 "Display version information", NULL},
176 {NULL}
179 gtk_init_with_args(p_argc, p_argv, "- CPML demonstration program",
180 entries, NULL, &error);
182 if (error != NULL) {
183 gint error_code = error->code;
185 g_printerr("%s\n", error->message);
187 g_error_free(error);
188 exit(error_code);
193 static cairo_path_t *
194 duplicate_and_stroke(cairo_t *cr)
196 cairo_path_t *path = cairo_copy_path(cr);
198 cairo_set_line_width(cr, 2.);
199 cairo_stroke(cr);
201 return path;
204 static void
205 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
207 cairo_append_path(cr, path);
208 cairo_path_destroy(path);
210 cairo_set_line_width(cr, 1.);
211 cairo_stroke(cr);
215 static void
216 browsing(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
218 cairo_t *cr;
219 int n;
221 cr = gdk_cairo_create(widget->window);
223 if (browsing_data.area == NULL) {
224 /* Initialize browsing_data */
225 browsing_data.area = widget;
226 browsing_data.use_segment = TRUE;
228 /* Append all the path samples to the cairo context */
229 cairo_save(cr);
230 cairo_translate(cr, 270.5, -120.5);
231 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
232 if ((n & 1) == 0) {
233 cairo_translate(cr, -270., 240.);
234 } else {
235 cairo_translate(cr, 270., 0.);
238 (path_samples[n]) (cr);
241 cairo_restore(cr);
242 browsing_data.cairo_path = cairo_copy_path(cr);
243 cpml_segment_from_cairo(&browsing_data.segment,
244 browsing_data.cairo_path);
245 cpml_primitive_from_segment(&browsing_data.primitive,
246 &browsing_data.segment);
247 } else {
248 cairo_append_path(cr, browsing_data.cairo_path);
251 cairo_set_line_width(cr, 2.);
252 cairo_stroke(cr);
254 cairo_set_source_rgb(cr, 1., 0.4, 0.);
255 cairo_set_line_width(cr, 5.);
257 if (browsing_data.use_segment)
258 cpml_segment_to_cairo(&browsing_data.segment, cr);
259 else
260 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
262 cairo_stroke(cr);
263 cairo_destroy(cr);
266 static void
267 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
269 if (!gtk_toggle_button_get_active(togglebutton))
270 return;
272 browsing_data.use_segment = TRUE;
273 gtk_widget_queue_draw(browsing_data.area);
276 static void
277 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
279 if (!gtk_toggle_button_get_active(togglebutton))
280 return;
282 browsing_data.use_segment = FALSE;
283 gtk_widget_queue_draw(browsing_data.area);
286 static void
287 browsing_reset(GtkButton *button, gpointer user_data)
289 if (browsing_data.use_segment) {
290 cpml_segment_reset(&browsing_data.segment);
291 cpml_primitive_from_segment(&browsing_data.primitive,
292 &browsing_data.segment);
293 } else {
294 cpml_primitive_reset(&browsing_data.primitive);
297 gtk_widget_queue_draw(browsing_data.area);
300 static void
301 browsing_next(GtkButton *button, gpointer user_data)
303 if (browsing_data.use_segment) {
304 cpml_segment_next(&browsing_data.segment);
305 cpml_primitive_from_segment(&browsing_data.primitive,
306 &browsing_data.segment);
307 } else {
308 cpml_primitive_next(&browsing_data.primitive);
311 gtk_widget_queue_draw(browsing_data.area);
315 static void
316 arcs(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
318 cairo_t *cr = gdk_cairo_create(widget->window);
320 cairo_translate(cr, 100.5, 100.5);
321 arc3p(cr, 0, 0, 0, 120, 120, 120);
323 cairo_translate(cr, 200, 0.);
324 arc3p(cr, 0, 0, 120, 0, 120, 120);
326 cairo_translate(cr, 200, 0.);
327 arc3p(cr, 60, 0, 0, 120, 120, 120);
329 cairo_translate(cr, -400, 200);
330 arc3p(cr, 0, 50, -2, 85, 120, 0);
332 cairo_translate(cr, 200, 0);
333 arc3p(cr, -2, 85, 0, 50, 120, 0);
335 cairo_destroy(cr);
338 static void
339 arc3p(cairo_t *cr, double x1, double y1,
340 double x2, double y2, double x3, double y3)
342 CpmlPrimitive arc;
343 cairo_path_data_t p[4];
344 CpmlPair center;
345 double r, start, end;
347 arc.segment = NULL;
348 arc.org = &p[0];
349 arc.data = &p[1];
351 p[1].header.type = CPML_ARC;
352 p[1].header.length = 3;
354 p[0].point.x = x1;
355 p[0].point.y = y1;
356 p[2].point.x = x2;
357 p[2].point.y = y2;
358 p[3].point.x = x3;
359 p[3].point.y = y3;
361 cpml_primitive_to_cairo(&arc, cr);
363 cairo_set_line_width(cr, 1.);
364 cairo_stroke(cr);
366 /* Add an arc generated by cairo, just for reference */
367 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
368 g_print("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n",
369 x1, y1, x2, y2, x3, y3);
370 return;
373 if (start < end)
374 cairo_arc(cr, center.x, center.y, r-5., start, end);
375 else
376 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
378 /* Show the inscribed triangle */
379 cairo_move_to(cr, x1, y1);
380 cairo_line_to(cr, x2, y2);
381 cairo_line_to(cr, x3, y3);
383 cairo_set_line_width(cr, 0.5);
384 cairo_stroke(cr);
388 static void
389 intersections(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
391 cairo_t *cr;
392 cairo_path_t *path;
393 CpmlSegment segment1, segment2;
394 CpmlPair intersection;
396 cr = gdk_cairo_create(widget->window);
397 cairo_translate(cr, 10.5, 120.5);
399 line1_callback(cr);
401 path = cairo_copy_path(cr);
403 cairo_set_line_width(cr, 1.);
404 cairo_stroke(cr);
406 cpml_segment_from_cairo(&segment1, path);
407 cpml_segment_from_cairo(&segment2, path);
409 while (cpml_segment_next(&segment2)) {
410 cpml_segment_put_intersections(&segment1, &segment2, 1, &intersection);
412 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
413 cairo_fill(cr);
415 cpml_segment_next(&segment1);
418 cairo_path_destroy(path);
419 cairo_destroy(cr);
423 static void
424 offset_curves(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
426 cairo_t *cr;
427 gint n;
428 CpmlPair *bezier;
429 cairo_path_t *path, *path_copy;
430 CpmlSegment segment;
431 CpmlPrimitive primitive;
432 CpmlPair pair;
433 CpmlVector vector;
434 double t;
436 cr = gdk_cairo_create(widget->window);
438 /* Add the Bézier curve samples */
439 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
440 bezier = &bezier_samples[n][0];
442 /* The samples are arranged in a 4x? matrix of 200x150 cells */
443 if (n == 0)
444 cairo_translate(cr, 25., 25.);
445 else if (n % 4 == 0)
446 cairo_translate(cr, -600., 150.);
447 else
448 cairo_translate(cr, 200., 0.);
450 /* Draw the Bézier curve */
451 cairo_move_to(cr, bezier[0].x, bezier[0].y);
452 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
453 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
455 /* Create a copy, to be used after */
456 path_copy = cairo_copy_path(cr);
458 path = duplicate_and_stroke(cr);
459 cpml_segment_from_cairo(&segment, path);
460 cpml_segment_offset(&segment, 20.);
461 stroke_and_destroy(cr, path);
463 cpml_segment_from_cairo(&segment, path_copy);
464 cpml_primitive_from_segment(&primitive, &segment);
466 /* Checking cpml_curve_put_pair_at_time and cpml_curve_put_vector_at_time */
467 cairo_set_line_width(cr, 1.);
468 for (t = 0; t < 1; t += 0.1) {
469 cpml_curve_put_pair_at_time(&primitive, t, &pair);
470 cpml_curve_put_vector_at_time(&primitive, t, &vector);
471 cpml_vector_set_length(&vector, 20.);
472 cpml_vector_normal(&vector);
474 cairo_new_sub_path(cr);
475 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
476 cairo_fill(cr);
478 cairo_move_to(cr, pair.x, pair.y);
479 cairo_line_to(cr, pair.x + vector.x, pair.y + vector.y);
480 cairo_stroke(cr);
483 cairo_path_destroy(path_copy);
486 cairo_destroy(cr);
490 static void
491 offset_segments(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
493 cairo_t *cr;
494 cairo_path_t *path;
495 CpmlSegment segment;
496 int n;
498 cr = gdk_cairo_create(widget->window);
499 cairo_translate(cr, 270.5, -120.5);
501 /* Offset the path samples */
502 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
503 if ((n & 1) == 0) {
504 cairo_translate(cr, -270., 240.);
505 } else {
506 cairo_translate(cr, 270., 0.);
509 /* Call the path callback */
510 (path_samples[n]) (cr);
512 path = duplicate_and_stroke(cr);
513 cpml_segment_from_cairo(&segment, path);
514 cpml_segment_offset(&segment, 15.);
515 stroke_and_destroy(cr, path);
518 cairo_destroy(cr);
522 static void
523 circle_callback(cairo_t *cr)
525 cairo_new_sub_path(cr);
526 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
529 static void
530 piston_callback(cairo_t *cr)
532 cairo_path_t *old_path, *path;
533 cairo_matrix_t matrix;
534 CpmlSegment segment;
536 /* Save the previous path, if any */
537 old_path = cairo_copy_path(cr);
539 cairo_new_path(cr);
540 cairo_move_to(cr, 0., 46.5);
541 cairo_line_to(cr, 210., 46.5);
542 cairo_line_to(cr, 222.5, 35.);
543 cairo_line_to(cr, 270., 35.);
544 cairo_line_to(cr, 270., 56.);
545 cairo_line_to(cr, 273., 59.);
546 cairo_line_to(cr, 302., 59.);
547 cairo_line_to(cr, 305., 56.);
548 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
549 cairo_line_to(cr, 400., 32.5);
550 cairo_line_to(cr, 410., 22.5);
551 cairo_line_to(cr, 450., 22.5);
552 cairo_arc_negative(cr,
553 452., 34., 2., G_PI, G_PI_2);
554 cairo_line_to(cr, 460., 36.);
555 cairo_line_to(cr, 470., 30.);
556 cairo_line_to(cr, 472., 12.5);
558 /* Mirror a reversed copy of the current path on the y = 0 axis */
559 path = cairo_copy_path(cr);
560 cpml_segment_from_cairo(&segment, path);
562 cpml_segment_reverse(&segment);
563 cairo_matrix_init_scale(&matrix, 1., -1.);
564 cpml_segment_transform(&segment, &matrix);
566 /* Join the mirrored path to the old path... */
567 path->data[0].header.type = CPML_LINE;
568 cairo_append_path(cr, path);
569 cairo_path_destroy(path);
571 /* ...and close the shape */
572 cairo_close_path(cr);
574 /* Save the resulting path and clear the path memory */
575 path = cairo_copy_path(cr);
576 cairo_new_path(cr);
578 /* Restore the previous path and reappend the new path */
579 cairo_append_path(cr, old_path);
580 cairo_path_destroy(old_path);
581 cairo_append_path(cr, path);
582 cairo_path_destroy(path);
585 static void
586 curve1_callback(cairo_t *cr)
588 cairo_move_to(cr, 30., 0.);
589 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
590 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
591 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
592 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
595 static void
596 line1_callback(cairo_t *cr)
598 cairo_move_to(cr, 0, -50);
599 cairo_line_to(cr, 100, 50);
601 cairo_move_to(cr, 100, -50);
602 cairo_line_to(cr, 0, 50);
604 cairo_move_to(cr, 120, -50);
605 cairo_line_to(cr, 200, -10);
607 cairo_move_to(cr, 120, 50);
608 cairo_line_to(cr, 200, 10);
610 cairo_move_to(cr, 220, 0);
611 cairo_line_to(cr, 280, 0);
613 cairo_move_to(cr, 270, -40);
614 cairo_line_to(cr, 270, 20);
616 cairo_move_to(cr, 320, 60);
617 cairo_line_to(cr, 380, 60);
619 cairo_move_to(cr, 300, -40);
620 cairo_line_to(cr, 340, 0);
622 cairo_move_to(cr, 480, 10);
623 cairo_line_to(cr, 400, 40);
625 cairo_move_to(cr, 400, 40);
626 cairo_line_to(cr, 450, -40);