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.
26 static void parse_args (gint
*p_argc
,
29 duplicate_and_stroke (cairo_t
*cr
);
30 static void stroke_and_destroy (cairo_t
*cr
,
33 static void browsing (GtkWidget
*widget
,
34 GdkEventExpose
*event
,
36 static void browsing_segment (GtkToggleButton
*togglebutton
,
38 static void browsing_primitive (GtkToggleButton
*togglebutton
,
40 static void browsing_reset (GtkButton
*button
,
42 static void browsing_next (GtkButton
*button
,
45 static void arcs (GtkWidget
*widget
,
46 GdkEventExpose
*event
,
48 static void arc3p (cairo_t
*cr
,
56 static void intersections (GtkWidget
*widget
,
57 GdkEventExpose
*event
,
60 static void offset_curves (GtkWidget
*widget
,
61 GdkEventExpose
*event
,
64 static void offset_segments (GtkWidget
*widget
,
65 GdkEventExpose
*event
,
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
);
75 cairo_path_t
*cairo_path
;
78 CpmlPrimitive primitive
;
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
) = {
120 main(gint argc
, gchar
**argv
)
127 _demo_init(argc
, argv
);
128 parse_args(&argc
, &argv
);
130 path
= _demo_file("cpml-demo.ui");
132 g_printerr(_("cpml-demo.ui not found!\n"));
136 builder
= gtk_builder_new();
139 gtk_builder_set_translation_domain(builder
, GETTEXT_PACKAGE
);
140 gtk_builder_add_from_file(builder
, path
, &error
);
142 g_print("%s\n", error
->message
);
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
);
180 /**********************************************
181 * Command line options parser
182 **********************************************/
187 g_print("cpml-demo " PACKAGE_VERSION
"\n");
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
},
201 gtk_init_with_args(p_argc
, p_argv
, _("- CPML demonstration program"),
202 entries
, GETTEXT_PACKAGE
, &error
);
205 gint error_code
= error
->code
;
207 g_printerr("%s\n", error
->message
);
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.);
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.);
238 browsing(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
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 */
252 cairo_translate(cr
, 270.5, -120.5);
253 for (n
= 0; n
< G_N_ELEMENTS(path_samples
); ++n
) {
255 cairo_translate(cr
, -270., 240.);
257 cairo_translate(cr
, 270., 0.);
260 (path_samples
[n
]) (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
);
270 cairo_append_path(cr
, browsing_data
.cairo_path
);
273 cairo_set_line_width(cr
, 2.);
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
);
282 cpml_primitive_to_cairo(&browsing_data
.primitive
, cr
);
289 browsing_segment(GtkToggleButton
*togglebutton
, gpointer user_data
)
291 if (!gtk_toggle_button_get_active(togglebutton
))
294 browsing_data
.use_segment
= TRUE
;
295 gtk_widget_queue_draw(browsing_data
.area
);
299 browsing_primitive(GtkToggleButton
*togglebutton
, gpointer user_data
)
301 if (!gtk_toggle_button_get_active(togglebutton
))
304 browsing_data
.use_segment
= FALSE
;
305 gtk_widget_queue_draw(browsing_data
.area
);
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
);
316 cpml_primitive_reset(&browsing_data
.primitive
);
319 gtk_widget_queue_draw(browsing_data
.area
);
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
);
330 cpml_primitive_next(&browsing_data
.primitive
);
333 gtk_widget_queue_draw(browsing_data
.area
);
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);
361 arc3p(cairo_t
*cr
, double x1
, double y1
,
362 double x2
, double y2
, double x3
, double y3
)
365 cairo_path_data_t p
[4];
367 double r
, start
, end
;
373 p
[1].header
.type
= CPML_ARC
;
374 p
[1].header
.length
= 3;
383 cpml_primitive_to_cairo(&arc
, cr
);
385 cairo_set_line_width(cr
, 1.);
388 /* Add an arc generated by cairo, just for reference */
389 if (!cpml_arc_info(&arc
, ¢er
, &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
);
396 cairo_arc(cr
, center
.x
, center
.y
, r
-5., start
, end
);
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);
411 intersections(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
415 CpmlSegment segment1
, segment2
;
416 CpmlPair intersection
;
418 cr
= gdk_cairo_create(widget
->window
);
419 cairo_translate(cr
, 10.5, 120.5);
423 path
= cairo_copy_path(cr
);
425 cairo_set_line_width(cr
, 1.);
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
);
437 cpml_segment_next(&segment1
);
440 cairo_path_destroy(path
);
446 offset_curves(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
451 cairo_path_t
*path
, *path_copy
;
453 CpmlPrimitive primitive
;
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 */
466 cairo_translate(cr
, 25., 25.);
468 cairo_translate(cr
, -600., 150.);
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);
500 cairo_move_to(cr
, pair
.x
, pair
.y
);
501 cairo_line_to(cr
, pair
.x
+ vector
.x
, pair
.y
+ vector
.y
);
505 cairo_path_destroy(path_copy
);
513 offset_segments(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer user_data
)
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
) {
526 cairo_translate(cr
, -270., 240.);
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
);
545 circle_callback(cairo_t
*cr
)
547 cairo_new_sub_path(cr
);
548 cairo_arc(cr
, 120., 0., 100., 0., M_PI
*2);
552 piston_callback(cairo_t
*cr
)
554 cairo_path_t
*old_path
, *path
;
555 cairo_matrix_t matrix
;
558 /* Save the previous path, if any */
559 old_path
= cairo_copy_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
);
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
);
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.);
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);