[CpmlPrimitive] Make cpml_primitive_from_segment() void
[adg.git] / demo / cpml-demo.c
blob0af5fd3ed22d0bdeb5fe29856d0302f0fdd5c8a8
1 #include <config.h>
2 #include <cpml/cpml.h>
3 #include <gtk/gtk.h>
4 #include <math.h>
6 #include "demo.h"
9 static void parse_args (gint *p_argc,
10 gchar **p_argv[]);
11 static cairo_path_t *
12 duplicate_and_stroke (cairo_t *cr);
13 static void stroke_and_destroy (cairo_t *cr,
14 cairo_path_t *path);
16 static void browsing (GtkWidget *widget,
17 GdkEventExpose *event,
18 gpointer user_data);
19 static void browsing_segment (GtkToggleButton*togglebutton,
20 gpointer user_data);
21 static void browsing_primitive (GtkToggleButton*togglebutton,
22 gpointer user_data);
23 static void browsing_reset (GtkButton *button,
24 gpointer user_data);
25 static void browsing_next (GtkButton *button,
26 gpointer user_data);
28 static void arcs (GtkWidget *widget,
29 GdkEventExpose *event,
30 gpointer user_data);
31 static void arc3p (cairo_t *cr,
32 double x1,
33 double y1,
34 double x2,
35 double y2,
36 double x3,
37 double y3);
39 static void intersections (GtkWidget *widget,
40 GdkEventExpose *event,
41 gpointer user_data);
43 static void offset_curves (GtkWidget *widget,
44 GdkEventExpose *event,
45 gpointer user_data);
47 static void offset_segments (GtkWidget *widget,
48 GdkEventExpose *event,
49 gpointer user_data);
51 static void circle_callback (cairo_t *cr);
52 static void piston_callback (cairo_t *cr);
53 static void curve1_callback (cairo_t *cr);
54 static void line1_callback (cairo_t *cr);
56 static struct {
57 GtkWidget *area;
58 cairo_path_t *cairo_path;
59 gboolean use_segment;
60 CpmlSegment segment;
61 CpmlPrimitive primitive;
62 } browsing_data = {
63 NULL,
66 static CpmlPair bezier_samples[][4] = {
67 { { 0, 0 }, { 0, 40 }, { 120, 40 }, { 120, 0 } }, /* Simmetric low */
68 { { 40, 0 }, { 40, 160 }, { 80, 160 }, { 80, 0 } }, /* Simmetric high */
69 { { 0, 0 }, { 33.1371, 33.1371 }, { 86.8629, 33.1371 }, { 120, 0 } },
70 /* Arc approximation */
71 { { 0, 0 }, { 70, 120 }, { 50, 120 }, { 120, 0 } }, /* Twisted controls */
73 { { 0, 0 }, { 0, 120 }, { 60, 120 }, { 120, 0 } }, /* Vertical p1-p2 */
74 { { 0, 0 }, { 60, 120 }, { 120, 120 }, { 120, 0 } },/* Vertical p3-p4 */
75 { { 0, 120 }, { 120, 120 }, { 120, 60 }, { 0, 0 } },/* Horizontal p1-p2 */
76 { { 0, 120 }, { 120, 60 }, { 120, 0 }, { 0, 0 } }, /* Horizontal p3-p4 */
78 { { 0, 0 }, { 0, 120 }, { 120, 120 }, { 120, 0 } }, /* Down */
79 { { 0, 120 }, { 120, 120 }, { 120, 0 }, { 0, 0 } }, /* Right */
80 { { 0, 120 }, { 0, 0 }, { 120, 0 }, { 120, 120 } }, /* Up */
81 { { 120, 120 }, { 0, 120 }, { 0, 0 }, { 120, 0 } }, /* Left */
83 { { 0, 60 }, { 60, 120 }, { 120, 60 }, { 60, 0 } }, /* Down-right */
84 { { 60, 120 }, { 120, 60 }, { 60, 0 }, { 0, 60 } }, /* Up-right */
85 { { 120, 60 }, { 60, 0 }, { 0, 60 }, { 60, 120 } }, /* Up-left */
86 { { 60, 0 }, { 0, 60 }, { 60, 120 }, { 120, 60 } }, /* Down-left*/
88 { { 0, 0 }, { 60, 0 }, { 60, 120 }, { 120, 120 } }, /* Step left */
89 { { 120, 0 }, { 60, 0 }, { 60, 120 }, { 0, 120 } }, /* Step right */
90 { { 0, 0 }, { 60, 90 }, { 90, 120 }, { 120, 90 } }, /* Unbalanced opened */
91 { { 0, 0 }, { 40, 120 }, { 120, 120 }, { 60, 80 } } /* Unbalanced closed */
94 static void (*path_samples[]) (cairo_t *cr) = {
95 circle_callback,
96 piston_callback,
97 curve1_callback,
98 line1_callback,
103 main(gint argc, gchar **argv)
105 gchar *path;
106 GtkBuilder *builder;
107 GError *error;
108 GtkWidget *window;
110 parse_args(&argc, &argv);
112 path = demo_find_data_file("cpml-demo.ui");
113 if (path == NULL) {
114 g_print("cpml-demo.ui not found!\n");
115 return 1;
118 builder = gtk_builder_new();
119 error = NULL;
121 gtk_builder_add_from_file(builder, path, &error);
122 if (error != NULL) {
123 g_print("%s\n", error->message);
124 return 2;
127 window = (GtkWidget *) gtk_builder_get_object(builder, "wndMain");
129 /* Connect signals */
130 g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
131 g_signal_connect(gtk_builder_get_object(builder, "areaBrowsing"),
132 "expose-event", G_CALLBACK(browsing), NULL);
133 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingSegment"),
134 "toggled", G_CALLBACK(browsing_segment), NULL);
135 g_signal_connect(gtk_builder_get_object(builder, "optBrowsingPrimitive"),
136 "toggled", G_CALLBACK(browsing_primitive), NULL);
137 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingReset"),
138 "clicked", G_CALLBACK(browsing_reset), NULL);
139 g_signal_connect(gtk_builder_get_object(builder, "btnBrowsingNext"),
140 "clicked", G_CALLBACK(browsing_next), NULL);
141 g_signal_connect(gtk_builder_get_object(builder, "areaArcs"),
142 "expose-event", G_CALLBACK(arcs), NULL);
143 g_signal_connect(gtk_builder_get_object(builder, "areaIntersections"),
144 "expose-event", G_CALLBACK(intersections), NULL);
145 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetCurves"),
146 "expose-event", G_CALLBACK(offset_curves), NULL);
147 g_signal_connect(gtk_builder_get_object(builder, "areaOffsetSegments"),
148 "expose-event", G_CALLBACK(offset_segments), NULL);
149 g_signal_connect(gtk_builder_get_object(builder, "btnQuit"),
150 "clicked", G_CALLBACK(gtk_main_quit), NULL);
152 g_object_unref(builder);
154 gtk_widget_show_all(window);
155 gtk_main();
157 return 0;
161 /**********************************************
162 * Command line options parser
163 **********************************************/
165 static void
166 version(void)
168 g_print("cpml-demo " PACKAGE_VERSION "\n");
169 exit(0);
172 static void
173 parse_args(gint *p_argc, gchar **p_argv[])
175 GError *error = NULL;
176 GOptionEntry entries[] = {
177 {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, version,
178 "Display version information", NULL},
179 {NULL}
182 gtk_init_with_args(p_argc, p_argv, "- CPML demonstration program",
183 entries, NULL, &error);
185 if (error != NULL) {
186 gint error_code = error->code;
188 g_printerr("%s\n", error->message);
190 g_error_free(error);
191 exit(error_code);
196 static cairo_path_t *
197 duplicate_and_stroke(cairo_t *cr)
199 cairo_path_t *path = cairo_copy_path(cr);
201 cairo_set_line_width(cr, 2.);
202 cairo_stroke(cr);
204 return path;
207 static void
208 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
210 cairo_append_path(cr, path);
211 cairo_path_destroy(path);
213 cairo_set_line_width(cr, 1.);
214 cairo_stroke(cr);
218 static void
219 browsing(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
221 cairo_t *cr;
222 int n;
224 cr = gdk_cairo_create(widget->window);
226 if (browsing_data.area == NULL) {
227 /* Initialize browsing_data */
228 browsing_data.area = widget;
229 browsing_data.use_segment = TRUE;
231 /* Append all the path samples to the cairo context */
232 cairo_save(cr);
233 cairo_translate(cr, 270.5, -120.5);
234 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
235 if ((n & 1) == 0) {
236 cairo_translate(cr, -270., 240.);
237 } else {
238 cairo_translate(cr, 270., 0.);
241 (path_samples[n]) (cr);
244 cairo_restore(cr);
245 browsing_data.cairo_path = cairo_copy_path(cr);
246 cpml_segment_from_cairo(&browsing_data.segment,
247 browsing_data.cairo_path);
248 cpml_primitive_from_segment(&browsing_data.primitive,
249 &browsing_data.segment);
250 } else {
251 cairo_append_path(cr, browsing_data.cairo_path);
254 cairo_set_line_width(cr, 2.);
255 cairo_stroke(cr);
257 cairo_set_source_rgb(cr, 1., 0.4, 0.);
258 cairo_set_line_width(cr, 5.);
260 if (browsing_data.use_segment)
261 cpml_segment_to_cairo(&browsing_data.segment, cr);
262 else
263 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
265 cairo_stroke(cr);
266 cairo_destroy(cr);
269 static void
270 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
272 if (!gtk_toggle_button_get_active(togglebutton))
273 return;
275 browsing_data.use_segment = TRUE;
276 gtk_widget_queue_draw(browsing_data.area);
279 static void
280 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
282 if (!gtk_toggle_button_get_active(togglebutton))
283 return;
285 browsing_data.use_segment = FALSE;
286 gtk_widget_queue_draw(browsing_data.area);
289 static void
290 browsing_reset(GtkButton *button, gpointer user_data)
292 if (browsing_data.use_segment) {
293 cpml_segment_reset(&browsing_data.segment);
294 cpml_primitive_from_segment(&browsing_data.primitive,
295 &browsing_data.segment);
296 } else {
297 cpml_primitive_reset(&browsing_data.primitive);
300 gtk_widget_queue_draw(browsing_data.area);
303 static void
304 browsing_next(GtkButton *button, gpointer user_data)
306 if (browsing_data.use_segment) {
307 cpml_segment_next(&browsing_data.segment);
308 cpml_primitive_from_segment(&browsing_data.primitive,
309 &browsing_data.segment);
310 } else {
311 cpml_primitive_next(&browsing_data.primitive);
314 gtk_widget_queue_draw(browsing_data.area);
318 static void
319 arcs(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
321 cairo_t *cr = gdk_cairo_create(widget->window);
323 cairo_translate(cr, 100.5, 100.5);
324 arc3p(cr, 0, 0, 0, 120, 120, 120);
326 cairo_translate(cr, 200, 0.);
327 arc3p(cr, 0, 0, 120, 0, 120, 120);
329 cairo_translate(cr, 200, 0.);
330 arc3p(cr, 60, 0, 0, 120, 120, 120);
332 cairo_translate(cr, -400, 200);
333 arc3p(cr, 0, 50, -2, 85, 120, 0);
335 cairo_translate(cr, 200, 0);
336 arc3p(cr, -2, 85, 0, 50, 120, 0);
338 cairo_destroy(cr);
341 static void
342 arc3p(cairo_t *cr, double x1, double y1,
343 double x2, double y2, double x3, double y3)
345 CpmlPrimitive arc;
346 cairo_path_data_t p[4];
347 CpmlPair center;
348 double r, start, end;
350 arc.segment = NULL;
351 arc.org = &p[0];
352 arc.data = &p[1];
354 p[1].header.type = CPML_ARC;
355 p[1].header.length = 3;
357 p[0].point.x = x1;
358 p[0].point.y = y1;
359 p[2].point.x = x2;
360 p[2].point.y = y2;
361 p[3].point.x = x3;
362 p[3].point.y = y3;
364 cpml_primitive_to_cairo(&arc, cr);
366 cairo_set_line_width(cr, 1.);
367 cairo_stroke(cr);
369 /* Add an arc generated by cairo, just for reference */
370 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
371 g_print("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n",
372 x1, y1, x2, y2, x3, y3);
373 return;
376 if (start < end)
377 cairo_arc(cr, center.x, center.y, r-5., start, end);
378 else
379 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
381 /* Show the inscribed triangle */
382 cairo_move_to(cr, x1, y1);
383 cairo_line_to(cr, x2, y2);
384 cairo_line_to(cr, x3, y3);
386 cairo_set_line_width(cr, 0.5);
387 cairo_stroke(cr);
391 static void
392 intersections(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
394 cairo_t *cr;
395 cairo_path_t *path;
396 CpmlSegment segment1, segment2;
397 CpmlPair intersection;
399 cr = gdk_cairo_create(widget->window);
400 cairo_translate(cr, 10.5, 120.5);
402 line1_callback(cr);
404 path = cairo_copy_path(cr);
406 cairo_set_line_width(cr, 1.);
407 cairo_stroke(cr);
409 cpml_segment_from_cairo(&segment1, path);
410 cpml_segment_from_cairo(&segment2, path);
412 while (cpml_segment_next(&segment2)) {
413 cpml_segment_put_intersections(&segment1, &segment2, 1, &intersection);
415 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
416 cairo_fill(cr);
418 cpml_segment_next(&segment1);
421 cairo_path_destroy(path);
422 cairo_destroy(cr);
426 static void
427 offset_curves(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
429 cairo_t *cr;
430 gint n;
431 CpmlPair *bezier;
432 cairo_path_t *path, *path_copy;
433 CpmlSegment segment;
434 CpmlPrimitive primitive;
435 CpmlPair pair;
436 CpmlVector vector;
437 double t;
439 cr = gdk_cairo_create(widget->window);
441 /* Add the Bézier curve samples */
442 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
443 bezier = &bezier_samples[n][0];
445 /* The samples are arranged in a 4x? matrix of 200x150 cells */
446 if (n == 0)
447 cairo_translate(cr, 25., 25.);
448 else if (n % 4 == 0)
449 cairo_translate(cr, -600., 150.);
450 else
451 cairo_translate(cr, 200., 0.);
453 /* Draw the Bézier curve */
454 cairo_move_to(cr, bezier[0].x, bezier[0].y);
455 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
456 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
458 /* Create a copy, to be used after */
459 path_copy = cairo_copy_path(cr);
461 path = duplicate_and_stroke(cr);
462 cpml_segment_from_cairo(&segment, path);
463 cpml_segment_offset(&segment, 20.);
464 stroke_and_destroy(cr, path);
466 cpml_segment_from_cairo(&segment, path_copy);
467 cpml_primitive_from_segment(&primitive, &segment);
469 /* Checking cpml_curve_put_pair_at_time and cpml_curve_put_vector_at_time */
470 cairo_set_line_width(cr, 1.);
471 for (t = 0; t < 1; t += 0.1) {
472 cpml_curve_put_pair_at_time(&primitive, t, &pair);
473 cpml_curve_put_vector_at_time(&primitive, t, &vector);
474 cpml_vector_set_length(&vector, 20.);
475 cpml_vector_normal(&vector);
477 cairo_new_sub_path(cr);
478 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
479 cairo_fill(cr);
481 cairo_move_to(cr, pair.x, pair.y);
482 cairo_line_to(cr, pair.x + vector.x, pair.y + vector.y);
483 cairo_stroke(cr);
486 cairo_path_destroy(path_copy);
489 cairo_destroy(cr);
493 static void
494 offset_segments(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
496 cairo_t *cr;
497 cairo_path_t *path;
498 CpmlSegment segment;
499 int n;
501 cr = gdk_cairo_create(widget->window);
502 cairo_translate(cr, 270.5, -120.5);
504 /* Offset the path samples */
505 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
506 if ((n & 1) == 0) {
507 cairo_translate(cr, -270., 240.);
508 } else {
509 cairo_translate(cr, 270., 0.);
512 /* Call the path callback */
513 (path_samples[n]) (cr);
515 path = duplicate_and_stroke(cr);
516 cpml_segment_from_cairo(&segment, path);
517 cpml_segment_offset(&segment, 15.);
518 stroke_and_destroy(cr, path);
521 cairo_destroy(cr);
525 static void
526 circle_callback(cairo_t *cr)
528 cairo_new_sub_path(cr);
529 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
532 static void
533 piston_callback(cairo_t *cr)
535 cairo_path_t *old_path, *path;
536 cairo_matrix_t matrix;
537 CpmlSegment segment;
539 /* Save the previous path, if any */
540 old_path = cairo_copy_path(cr);
542 cairo_new_path(cr);
543 cairo_move_to(cr, 0., 46.5);
544 cairo_line_to(cr, 210., 46.5);
545 cairo_line_to(cr, 222.5, 35.);
546 cairo_line_to(cr, 270., 35.);
547 cairo_line_to(cr, 270., 56.);
548 cairo_line_to(cr, 273., 59.);
549 cairo_line_to(cr, 302., 59.);
550 cairo_line_to(cr, 305., 56.);
551 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
552 cairo_line_to(cr, 400., 32.5);
553 cairo_line_to(cr, 410., 22.5);
554 cairo_line_to(cr, 450., 22.5);
555 cairo_arc_negative(cr,
556 452., 34., 2., G_PI, G_PI_2);
557 cairo_line_to(cr, 460., 36.);
558 cairo_line_to(cr, 470., 30.);
559 cairo_line_to(cr, 472., 12.5);
561 /* Mirror a reversed copy of the current path on the y = 0 axis */
562 path = cairo_copy_path(cr);
563 cpml_segment_from_cairo(&segment, path);
565 cpml_segment_reverse(&segment);
566 cairo_matrix_init_scale(&matrix, 1., -1.);
567 cpml_segment_transform(&segment, &matrix);
569 /* Join the mirrored path to the old path... */
570 path->data[0].header.type = CPML_LINE;
571 cairo_append_path(cr, path);
572 cairo_path_destroy(path);
574 /* ...and close the shape */
575 cairo_close_path(cr);
577 /* Save the resulting path and clear the path memory */
578 path = cairo_copy_path(cr);
579 cairo_new_path(cr);
581 /* Restore the previous path and reappend the new path */
582 cairo_append_path(cr, old_path);
583 cairo_path_destroy(old_path);
584 cairo_append_path(cr, path);
585 cairo_path_destroy(path);
588 static void
589 curve1_callback(cairo_t *cr)
591 cairo_move_to(cr, 30., 0.);
592 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
593 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
594 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
595 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
598 static void
599 line1_callback(cairo_t *cr)
601 cairo_move_to(cr, 0, -50);
602 cairo_line_to(cr, 100, 50);
604 cairo_move_to(cr, 100, -50);
605 cairo_line_to(cr, 0, 50);
607 cairo_move_to(cr, 120, -50);
608 cairo_line_to(cr, 200, -10);
610 cairo_move_to(cr, 120, 50);
611 cairo_line_to(cr, 200, 10);
613 cairo_move_to(cr, 220, 0);
614 cairo_line_to(cr, 280, 0);
616 cairo_move_to(cr, 270, -40);
617 cairo_line_to(cr, 270, 20);
619 cairo_move_to(cr, 320, 60);
620 cairo_line_to(cr, 380, 60);
622 cairo_move_to(cr, 300, -40);
623 cairo_line_to(cr, 340, 0);
625 cairo_move_to(cr, 480, 10);
626 cairo_line_to(cr, 400, 40);
628 cairo_move_to(cr, 400, 40);
629 cairo_line_to(cr, 450, -40);