[AdgPath] Cosmetic changes on main docblock
[adg.git] / demo / cpml-demo.c
blob63204d5db88dd743ceefbea96d2ec0b308b63c95
1 #include <cpml/cpml.h>
2 #include <gtk/gtk.h>
3 #include <math.h>
5 #include "demo.h"
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 gtk_init(&argc, &argv);
109 path = demo_find_data_file("cpml-demo.ui");
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 static cairo_path_t *
159 duplicate_and_stroke(cairo_t *cr)
161 cairo_path_t *path = cairo_copy_path(cr);
163 cairo_set_line_width(cr, 2.);
164 cairo_stroke(cr);
166 return path;
169 static void
170 stroke_and_destroy(cairo_t *cr, cairo_path_t *path)
172 cairo_append_path(cr, path);
173 cairo_path_destroy(path);
175 cairo_set_line_width(cr, 1.);
176 cairo_stroke(cr);
180 static void
181 browsing(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
183 cairo_t *cr;
184 int n;
186 cr = gdk_cairo_create(widget->window);
188 if (browsing_data.area == NULL) {
189 /* Initialize browsing_data */
190 browsing_data.area = widget;
191 browsing_data.use_segment = TRUE;
193 /* Append all the path samples to the cairo context */
194 cairo_save(cr);
195 cairo_translate(cr, 270.5, -120.5);
196 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
197 if ((n & 1) == 0) {
198 cairo_translate(cr, -270., 240.);
199 } else {
200 cairo_translate(cr, 270., 0.);
203 (path_samples[n]) (cr);
206 cairo_restore(cr);
207 browsing_data.cairo_path = cairo_copy_path(cr);
208 cpml_segment_from_cairo(&browsing_data.segment,
209 browsing_data.cairo_path);
210 cpml_primitive_from_segment(&browsing_data.primitive,
211 &browsing_data.segment);
212 } else {
213 cairo_append_path(cr, browsing_data.cairo_path);
216 cairo_set_line_width(cr, 2.);
217 cairo_stroke(cr);
219 cairo_set_source_rgb(cr, 1., 0.4, 0.);
220 cairo_set_line_width(cr, 5.);
222 if (browsing_data.use_segment)
223 cpml_segment_to_cairo(&browsing_data.segment, cr);
224 else
225 cpml_primitive_to_cairo(&browsing_data.primitive, cr);
227 cairo_stroke(cr);
228 cairo_destroy(cr);
231 static void
232 browsing_segment(GtkToggleButton *togglebutton, gpointer user_data)
234 if (!gtk_toggle_button_get_active(togglebutton))
235 return;
237 browsing_data.use_segment = TRUE;
238 gtk_widget_queue_draw(browsing_data.area);
241 static void
242 browsing_primitive(GtkToggleButton*togglebutton, gpointer user_data)
244 if (!gtk_toggle_button_get_active(togglebutton))
245 return;
247 browsing_data.use_segment = FALSE;
248 gtk_widget_queue_draw(browsing_data.area);
251 static void
252 browsing_reset(GtkButton *button, gpointer user_data)
254 if (browsing_data.use_segment) {
255 cpml_segment_reset(&browsing_data.segment);
256 cpml_primitive_from_segment(&browsing_data.primitive,
257 &browsing_data.segment);
258 } else {
259 cpml_primitive_reset(&browsing_data.primitive);
262 gtk_widget_queue_draw(browsing_data.area);
265 static void
266 browsing_next(GtkButton *button, gpointer user_data)
268 if (browsing_data.use_segment) {
269 cpml_segment_next(&browsing_data.segment);
270 cpml_primitive_from_segment(&browsing_data.primitive,
271 &browsing_data.segment);
272 } else {
273 cpml_primitive_next(&browsing_data.primitive);
276 gtk_widget_queue_draw(browsing_data.area);
280 static void
281 arcs(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
283 cairo_t *cr = gdk_cairo_create(widget->window);
285 cairo_translate(cr, 100.5, 100.5);
286 arc3p(cr, 0, 0, 0, 120, 120, 120);
288 cairo_translate(cr, 200, 0.);
289 arc3p(cr, 0, 0, 120, 0, 120, 120);
291 cairo_translate(cr, 200, 0.);
292 arc3p(cr, 60, 0, 0, 120, 120, 120);
294 cairo_translate(cr, -400, 200);
295 arc3p(cr, 0, 50, -2, 85, 120, 0);
297 cairo_translate(cr, 200, 0);
298 arc3p(cr, -2, 85, 0, 50, 120, 0);
300 cairo_destroy(cr);
303 static void
304 arc3p(cairo_t *cr, double x1, double y1,
305 double x2, double y2, double x3, double y3)
307 CpmlPrimitive arc;
308 cairo_path_data_t p[4];
309 CpmlPair center;
310 double r, start, end;
312 arc.segment = NULL;
313 arc.org = &p[0];
314 arc.data = &p[1];
316 p[1].header.type = CAIRO_PATH_ARC_TO;
317 p[1].header.length = 3;
319 p[0].point.x = x1;
320 p[0].point.y = y1;
321 p[2].point.x = x2;
322 p[2].point.y = y2;
323 p[3].point.x = x3;
324 p[3].point.y = y3;
326 cpml_primitive_to_cairo(&arc, cr);
328 cairo_set_line_width(cr, 1.);
329 cairo_stroke(cr);
331 /* Add an arc generated by cairo, just for reference */
332 if (!cpml_arc_info(&arc, &center, &r, &start, &end)) {
333 g_print("Unable to get arc info (%lf, %lf) (%lf, %lf) (%lf, %lf)\n",
334 x1, y1, x2, y2, x3, y3);
335 return;
338 if (start < end)
339 cairo_arc(cr, center.x, center.y, r-5., start, end);
340 else
341 cairo_arc_negative(cr, center.x, center.y, r-5., start, end);
343 /* Show the inscribed triangle */
344 cairo_move_to(cr, x1, y1);
345 cairo_line_to(cr, x2, y2);
346 cairo_line_to(cr, x3, y3);
348 cairo_set_line_width(cr, 0.5);
349 cairo_stroke(cr);
353 static void
354 intersections(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
356 cairo_t *cr;
357 cairo_path_t *path;
358 CpmlSegment segment1, segment2;
359 CpmlPair intersection;
361 cr = gdk_cairo_create(widget->window);
362 cairo_translate(cr, 10.5, 120.5);
364 line1_callback(cr);
366 path = cairo_copy_path(cr);
368 cairo_set_line_width(cr, 1.);
369 cairo_stroke(cr);
371 cpml_segment_from_cairo(&segment1, path);
372 cpml_segment_from_cairo(&segment2, path);
374 while (cpml_segment_next(&segment2)) {
375 cpml_segment_intersection(&segment1, &segment2, &intersection, 1);
377 cairo_arc(cr, intersection.x, intersection.y, 2.5, 0, 2 * M_PI);
378 cairo_fill(cr);
380 cpml_segment_next(&segment1);
383 cairo_path_destroy(path);
384 cairo_destroy(cr);
388 static void
389 offset_curves(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
391 cairo_t *cr;
392 gint n;
393 CpmlPair *bezier;
394 cairo_path_t *path, *path_copy;
395 CpmlSegment segment;
396 CpmlPrimitive primitive;
397 CpmlPair pair;
398 CpmlVector vector;
399 double t;
401 cr = gdk_cairo_create(widget->window);
403 /* Add the Bézier curve samples */
404 for (n = 0; n < G_N_ELEMENTS(bezier_samples); ++n) {
405 bezier = &bezier_samples[n][0];
407 /* The samples are arranged in a 4x? matrix of 200x150 cells */
408 if (n == 0)
409 cairo_translate(cr, 25., 25.);
410 else if (n % 4 == 0)
411 cairo_translate(cr, -600., 150.);
412 else
413 cairo_translate(cr, 200., 0.);
415 /* Draw the Bézier curve */
416 cairo_move_to(cr, bezier[0].x, bezier[0].y);
417 cairo_curve_to(cr, bezier[1].x, bezier[1].y,
418 bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
420 /* Create a copy, to be used after */
421 path_copy = cairo_copy_path(cr);
423 path = duplicate_and_stroke(cr);
424 cpml_segment_from_cairo(&segment, path);
425 cpml_segment_offset(&segment, 20.);
426 stroke_and_destroy(cr, path);
428 cpml_segment_from_cairo(&segment, path_copy);
429 cpml_primitive_from_segment(&primitive, &segment);
431 /* Checking cpml_curve_pair_at_time and cpml_curve_vector_at_time */
432 cairo_set_line_width(cr, 1.);
433 for (t = 0; t < 1; t += 0.1) {
434 cpml_curve_pair_at_time(&primitive, &pair, t);
435 cpml_curve_vector_at_time(&primitive, &vector, t);
436 cpml_vector_set_length(&vector, 20.);
437 cpml_vector_normal(&vector);
439 cairo_new_sub_path(cr);
440 cairo_arc(cr, pair.x, pair.y, 2.5, 0, M_PI*2);
441 cairo_fill(cr);
443 cairo_move_to(cr, pair.x, pair.y);
444 cairo_line_to(cr, pair.x + vector.x, pair.y + vector.y);
445 cairo_stroke(cr);
448 cairo_path_destroy(path_copy);
451 cairo_destroy(cr);
455 static void
456 offset_segments(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
458 cairo_t *cr;
459 cairo_path_t *path;
460 CpmlSegment segment;
461 int n;
463 cr = gdk_cairo_create(widget->window);
464 cairo_translate(cr, 270.5, -120.5);
466 /* Offset the path samples */
467 for (n = 0; n < G_N_ELEMENTS(path_samples); ++n) {
468 if ((n & 1) == 0) {
469 cairo_translate(cr, -270., 240.);
470 } else {
471 cairo_translate(cr, 270., 0.);
474 /* Call the path callback */
475 (path_samples[n]) (cr);
477 path = duplicate_and_stroke(cr);
478 cpml_segment_from_cairo(&segment, path);
479 cpml_segment_offset(&segment, 15.);
480 stroke_and_destroy(cr, path);
483 cairo_destroy(cr);
487 static void
488 circle_callback(cairo_t *cr)
490 cairo_new_sub_path(cr);
491 cairo_arc(cr, 120., 0., 100., 0., M_PI*2);
494 static void
495 piston_callback(cairo_t *cr)
497 cairo_path_t *old_path, *path;
498 cairo_matrix_t matrix;
499 CpmlSegment segment;
501 /* Save the previous path, if any */
502 old_path = cairo_copy_path(cr);
504 cairo_new_path(cr);
505 cairo_move_to(cr, 0., 46.5);
506 cairo_line_to(cr, 210., 46.5);
507 cairo_line_to(cr, 222.5, 35.);
508 cairo_line_to(cr, 270., 35.);
509 cairo_line_to(cr, 270., 56.);
510 cairo_line_to(cr, 273., 59.);
511 cairo_line_to(cr, 302., 59.);
512 cairo_line_to(cr, 305., 56.);
513 cairo_arc(cr, 325., 52.5, 20., G_PI, 3. * G_PI_2);
514 cairo_line_to(cr, 400., 32.5);
515 cairo_line_to(cr, 410., 22.5);
516 cairo_line_to(cr, 450., 22.5);
517 cairo_arc_negative(cr,
518 452., 34., 2., G_PI, G_PI_2);
519 cairo_line_to(cr, 460., 36.);
520 cairo_line_to(cr, 470., 30.);
521 cairo_line_to(cr, 472., 12.5);
523 /* Mirror a reversed copy of the current path on the y = 0 axis */
524 path = cairo_copy_path(cr);
525 cpml_segment_from_cairo(&segment, path);
527 cpml_segment_reverse(&segment);
528 cairo_matrix_init_scale(&matrix, 1., -1.);
529 cpml_segment_transform(&segment, &matrix);
531 /* Join the mirrored path to the old path... */
532 path->data[0].header.type = CAIRO_PATH_LINE_TO;
533 cairo_append_path(cr, path);
534 cairo_path_destroy(path);
536 /* ...and close the shape */
537 cairo_close_path(cr);
539 /* Save the resulting path and clear the path memory */
540 path = cairo_copy_path(cr);
541 cairo_new_path(cr);
543 /* Restore the previous path and reappend the new path */
544 cairo_append_path(cr, old_path);
545 cairo_path_destroy(old_path);
546 cairo_append_path(cr, path);
547 cairo_path_destroy(path);
550 static void
551 curve1_callback(cairo_t *cr)
553 cairo_move_to(cr, 30., 0.);
554 cairo_curve_to(cr, 120., 120., 180., 100., 180., 20.);
555 cairo_curve_to(cr, 180., -20., 50., 40., 150., 40.);
556 cairo_curve_to(cr, 220., 40., 190., -60., 150., -60.);
557 cairo_curve_to(cr, 100., -60., 80., -40., 60., -60.);
560 static void
561 line1_callback(cairo_t *cr)
563 cairo_move_to(cr, 0, -50);
564 cairo_line_to(cr, 100, 50);
566 cairo_move_to(cr, 100, -50);
567 cairo_line_to(cr, 0, 50);
569 cairo_move_to(cr, 120, -50);
570 cairo_line_to(cr, 200, -10);
572 cairo_move_to(cr, 120, 50);
573 cairo_line_to(cr, 200, 10);
575 cairo_move_to(cr, 220, 0);
576 cairo_line_to(cr, 280, 0);
578 cairo_move_to(cr, 270, -40);
579 cairo_line_to(cr, 270, 20);
581 cairo_move_to(cr, 320, 60);
582 cairo_line_to(cr, 380, 60);
584 cairo_move_to(cr, 300, -40);
585 cairo_line_to(cr, 340, 0);
587 cairo_move_to(cr, 480, 10);
588 cairo_line_to(cr, 400, 40);
590 cairo_move_to(cr, 400, 40);
591 cairo_line_to(cr, 450, -40);