Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
toy-framework-2.cpp
Go to the documentation of this file.
1#include <cstring>
2#include <cstdint>
3#include <typeinfo>
4#include <cairo.h>
5#include <gtk/gtk.h>
7
8#include <cairo-features.h>
9#if CAIRO_HAS_PDF_SURFACE
10#include <cairo-pdf.h>
11#endif
12#if CAIRO_HAS_SVG_SURFACE
13#include <cairo-svg.h>
14#endif
15
16GtkApplicationWindow* the_window = nullptr;
17static GtkWidget *the_canvas = nullptr;
18Toy* the_toy = nullptr;
21gchar **the_emulated_argv = nullptr;
22
23gchar *arg_spool_filename = nullptr;
24gchar *arg_handles_filename = nullptr;
25gchar *arg_screenshot_filename = nullptr;
26gchar **arg_extra_files = nullptr;
27
28//Utility functions
29
30double uniform() {
31 return double(rand()) / RAND_MAX;
32}
33
34colour colour::from_hsv( float H, // hue shift (in degrees)
35 float S, // saturation shift (scalar)
36 float V, // value multiplier (scalar)
37 float A
38 )
39{
40 double inr = 1;
41 double ing = 0;
42 double inb = 0;
43 float k = V/3;
44 float a = V*S*cos(H)/3;
45 float b = V*S*sin(H)/3;
46
47 return colour(
48 (k+2*a)*inr - 2*b*ing + (k-a-b)*inb,
49 (-k+a+3*b)*inr + (3*a-b)*ing + (-k+a+2*b)*inb,
50 (2*k-2*a)*inr + 2*b*ing + (2*k+a+b)*inb,
51 A);
52}
53
54 // Given H,S,L in range of 0-1
55
56 // Returns a Color (RGB struct) in range of 0-255
57
58colour colour::from_hsl(float h, float sl, float l, float a) {
59 h /= M_PI*2;
60 colour rgba(l,l,l,a); // default to gray
61
62 double v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
63
64 if (v > 0) {
65 double m;
66 double sv;
67 int sextant;
68 double fract, vsf, mid1, mid2;
69
70 m = l + l - v;
71 sv = (v - m ) / v;
72 h *= 6.0;
73 sextant = (int)h;
74 fract = h - sextant;
75 vsf = v * sv * fract;
76 mid1 = m + vsf;
77 mid2 = v - vsf;
78 switch (sextant%6) {
79 case 0:
80 rgba.r = v;
81 rgba.g = mid1;
82 rgba.b = m;
83 break;
84
85 case 1:
86 rgba.r = mid2;
87 rgba.g = v;
88 rgba.b = m;
89 break;
90
91 case 2:
92 rgba.r = m;
93 rgba.g = v;
94 rgba.b = mid1;
95 break;
96
97 case 3:
98 rgba.r = m;
99 rgba.g = mid2;
100 rgba.b = v;
101 break;
102
103 case 4:
104 rgba.r = mid1;
105 rgba.g = m;
106 rgba.b = v;
107 break;
108
109 case 5:
110 rgba.r = v;
111 rgba.g = m;
112 rgba.b = mid2;
113 break;
114 }
115 }
116 return rgba;
117}
118
120 cairo_set_source_rgba(cr, c.r, c.g, c.b, c.a);
121}
122
123void draw_text(cairo_t *cr, Geom::Point loc, const char* txt, bool bottom, const char* fontdesc) {
124 PangoLayout* layout = pango_cairo_create_layout (cr);
125 pango_layout_set_text(layout, txt, -1);
126 PangoFontDescription *font_desc = pango_font_description_from_string(fontdesc);
127 pango_layout_set_font_description(layout, font_desc);
128 pango_font_description_free (font_desc);
129 PangoRectangle logical_extent;
130 pango_layout_get_pixel_extents(layout, NULL, &logical_extent);
131 cairo_move_to(cr, loc - Geom::Point(0, bottom ? logical_extent.height : 0));
132 pango_cairo_show_layout(cr, layout);
133}
134
135void draw_text(cairo_t *cr, Geom::Point loc, const std::string& txt, bool bottom, const std::string& fontdesc) {
136 draw_text(cr, loc, txt.c_str(), bottom, fontdesc.c_str());
137}
138
139void draw_number(cairo_t *cr, Geom::Point pos, int num, std::string name, bool bottom) {
140 std::ostringstream number;
141 if (name.size())
142 number << name;
143 number << num;
144 draw_text(cr, pos, number.str().c_str(), bottom);
145}
146
147void draw_number(cairo_t *cr, Geom::Point pos, unsigned num, std::string name, bool bottom) {
148 std::ostringstream number;
149 if (name.size())
150 number << name;
151 number << num;
152 draw_text(cr, pos, number.str().c_str(), bottom);
153}
154
155void draw_number(cairo_t *cr, Geom::Point pos, double num, std::string name, bool bottom) {
156 std::ostringstream number;
157 if (name.size())
158 number << name;
159 number << num;
160 draw_text(cr, pos, number.str().c_str(), bottom);
161}
162
163//Framework Accessors
164void redraw() { gtk_widget_queue_draw(the_canvas); }
165
166void Toy::draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool /*save*/, std::ostringstream *timer_stream)
167{
168 if(should_draw_bounds() == 1) {
169 cairo_set_source_rgba (cr, 0., 0., 0, 0.8);
170 cairo_set_line_width (cr, 0.5);
171 for(unsigned i = 1; i < 4; i+=2) {
172 cairo_move_to(cr, 0, i*width/4);
173 cairo_line_to(cr, width, i*width/4);
174 cairo_move_to(cr, i*width/4, 0);
175 cairo_line_to(cr, i*width/4, height);
176 }
177 }
178 else if(should_draw_bounds() == 2) {
179 cairo_set_source_rgba (cr, 0., 0., 0, 0.8);
180 cairo_set_line_width (cr, 0.5);
181 cairo_move_to(cr, 0, width/2);
182 cairo_line_to(cr, width, width/2);
183 cairo_move_to(cr, width/2, 0);
184 cairo_line_to(cr, width/2, height);
185 }
186
187 cairo_set_line_width (cr, 1);
188 for(auto & handle : handles) {
189 cairo_set_source_rgb (cr, handle->rgb[0], handle->rgb[1], handle->rgb[2]);
190 handle->draw(cr, should_draw_numbers());
191 }
192
193 cairo_set_source_rgba (cr, 0.5, 0, 0, 1);
194 if(selected && mouse_down == true)
196
197 cairo_set_source_rgba (cr, 0.5, 0.25, 0, 1);
198 cairo_stroke(cr);
199
200 cairo_set_source_rgba (cr, 0., 0.5, 0, 0.8);
201 {
202 *notify << std::ends;
203 draw_text(cr, Geom::Point(0, height-notify_offset), notify->str().c_str(), true);
204 }
205 if(show_timings) {
206 *timer_stream << std::ends;
207 draw_text(cr, Geom::Point(0, notify_offset), timer_stream->str().c_str(), false);
208 }
209}
210
211void Toy::mouse_moved(Geom::Point const &pos, unsigned modifiers)
212{
213 if (modifiers & (GDK_BUTTON1_MASK | GDK_BUTTON3_MASK)) {
214 if (selected) {
216 }
217 }
218 old_mouse_point = pos;
219 redraw();
220}
221
222void Toy::mouse_pressed(Geom::Point const &pos, unsigned button, unsigned modifiers)
223{
224 selected = nullptr;
225 hit_data = nullptr;
226 canvas_click_button = button;
227 if (button == 1) {
228 for (auto &handle : handles) {
229 if (auto hit = handle->hit(pos)) {
230 selected = handle;
231 hit_data = hit;
232 }
233 }
234 mouse_down = true;
235 }
236 old_mouse_point = pos;
237 redraw();
238}
239
240void Toy::scroll(GdkScrollDirection dir, Geom::Point const &delta) {
241}
242
243void Toy::canvas_click(Geom::Point at, int button) {
244}
245
246void Toy::mouse_released(Geom::Point const &pos, unsigned button, unsigned modifiers)
247{
248 if (!selected) {
251 }
252 selected = nullptr;
253 hit_data = nullptr;
254 if (button == 1) {
255 mouse_down = false;
256 }
257 redraw();
258}
259
260void Toy::load(FILE* f) {
261 char data[1024];
262 if (fscanf(f, "%1023s", data)) {
263 name = data;
264 }
265 for(auto & handle : handles) {
266 handle->load(f);
267 }
268}
269
270void Toy::save(FILE* f) {
271 fprintf(f, "%s\n", name.c_str());
272 for(auto & handle : handles)
273 handle->save(f);
274}
275
276//Gui Event Callbacks
277
278void show_about_dialog(GSimpleAction *, GVariant *, gpointer) {
279 GtkWidget* about_window = gtk_window_new();
280 gtk_window_set_title(GTK_WINDOW(about_window), "About");
281 gtk_window_set_resizable(GTK_WINDOW(about_window), FALSE);
282
283 GtkWidget* about_text = gtk_text_view_new();
284 GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(about_text));
285 gtk_text_buffer_set_text(buf, "Toy lib2geom application", -1);
286 gtk_text_view_set_editable(GTK_TEXT_VIEW(about_text), FALSE);
287 gtk_window_set_child(GTK_WINDOW(about_window), about_text);
288
289 gtk_window_present(GTK_WINDOW(about_window));
290}
291
292void quit(GSimpleAction *, GVariant *, gpointer) {
293 g_application_quit(g_application_get_default());
294}
295
297 Geom::Point p;
298 for(unsigned i = 0; i < 2; i++)
299 assert(fscanf(f, " %lf ", &p[i]));
300 return p;
301}
302
303void get_clipboard_text(std::function<void (char const *)> &&on_completion)
304{
305 using Closure = std::function<void (char const *)>;
306 auto clipboard = gtk_widget_get_clipboard(the_canvas);
307 auto closure = new Closure(std::move(on_completion));
308 gdk_clipboard_read_text_async(clipboard, nullptr, +[] (GObject *source_object, GAsyncResult *res, gpointer data) {
309 auto clipboard = GDK_CLIPBOARD(source_object);
310 auto closure = reinterpret_cast<Closure *>(data);
311 auto str = gdk_clipboard_read_text_finish(clipboard, res, nullptr);
312 closure->operator()(str);
313 g_free(str);
314 delete closure;
315 }, closure);
316}
317
320 Geom::Coord a, b;
321 assert(fscanf(f, " %lf ", &a));
322 assert(fscanf(f, " %lf ", &b));
323 p.setEnds(a, b);
324 return p;
325}
326
327void open_handles_cb(GObject *source_object, GAsyncResult *res, gpointer data)
328{
329 auto d = GTK_FILE_DIALOG(source_object);
330 auto file = gtk_file_dialog_open_finish(d, res, nullptr);
331 g_object_unref(d);
332 if (!file) return;
333
334 auto filename = g_file_get_path(file);
335 g_object_unref(file);
336
337 auto f = fopen(filename, "r");
338 g_free(filename);
339
340 the_toy->load(f);
341 fclose(f);
342
343 redraw();
344}
345
346void open_handles(GSimpleAction *, GVariant *, gpointer)
347{
348 if (!the_toy) return;
349 auto d = gtk_file_dialog_new();
350 gtk_file_dialog_set_title(d, "Open handle configuration");
351 gtk_file_dialog_open(d, GTK_WINDOW(the_window), nullptr, open_handles_cb, nullptr);
352}
353
354void save_handles_cb(GObject *source_object, GAsyncResult *res, gpointer data)
355{
356 auto d = GTK_FILE_DIALOG(source_object);
357 auto file = gtk_file_dialog_save_finish(d, res, nullptr);
358 g_object_unref(d);
359 if (!file) return;
360
361 auto filename = g_file_get_path(file);
362 g_object_unref(file);
363
364 auto f = fopen(filename, "w");
365 g_free(filename);
366
367 the_toy->save(f);
368 fclose(f);
369}
370
371void save_handles(GSimpleAction *, GVariant *, gpointer)
372{
373 if (!the_toy) return;
374 auto d = gtk_file_dialog_new();
375 gtk_file_dialog_set_title(d, "Save handle configuration");
376 gtk_file_dialog_save(d, GTK_WINDOW(the_window), nullptr, save_handles_cb, nullptr);
377}
378
379void write_image(char const *filename)
380{
381 int width = gtk_widget_get_width(the_canvas);
382 int height = gtk_widget_get_height(the_canvas);
383
385 bool save_png = false;
386
387 unsigned l = strlen(filename);
388 if (l >= 4 && strcmp(filename + l - 4, ".png") == 0) {
389 s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
390 save_png = true;
391 }
392#if CAIRO_HAS_PDF_SURFACE
393 else if (l >= 4 && strcmp(filename + l - 4, ".pdf") == 0) {
394 s = cairo_pdf_surface_create(filename, width, height);
395 }
396#endif
397#if CAIRO_HAS_SVG_SURFACE
398 else if (true) {
399 s = cairo_svg_surface_create(filename, width, height);
400 }
401#endif
402 else {
403 return;
404 }
405
406 auto cr = cairo_create(s);
407
408 if (save_png) {
409 cairo_set_source_rgb(cr, 1, 1, 1);
410 cairo_paint(cr);
411 }
412
413 if (the_toy) {
414 std::ostringstream notify, timer_stream;
415 the_toy->draw(cr, &notify, width, height, true, &timer_stream);
416 }
417
418 cairo_show_page(cr);
419 cairo_destroy(cr);
420
421 if (save_png) {
422 cairo_surface_write_to_png(s, filename);
423 }
424
425 cairo_surface_destroy(s);
426}
427
428void save_cairo_cb(GObject *source_object, GAsyncResult *res, gpointer data)
429{
430 auto d = GTK_FILE_DIALOG(source_object);
431 auto file = gtk_file_dialog_save_finish(d, res, nullptr);
432 g_object_unref(d);
433 if (!file) return;
434
435 auto filename = g_file_get_path(file);
436 g_object_unref(file);
437
438 write_image(filename);
439 g_free(filename);
440}
441
442void save_cairo(GSimpleAction *, GVariant *, gpointer)
443{
444 auto d = gtk_file_dialog_new();
445 gtk_file_dialog_set_title(d, "Save file as svg, pdf or png");
446 gtk_file_dialog_save(d, GTK_WINDOW(the_window), nullptr, save_cairo_cb, nullptr);
447}
448
449static void toggle_action(GSimpleAction *action, GVariant *, gpointer) {
450 GVariant *state = g_action_get_state(G_ACTION(action));
451 g_action_change_state(G_ACTION(action), g_variant_new_boolean(!g_variant_get_boolean(state)));
452 g_variant_unref(state);
453}
454
455static void set_show_timings(GSimpleAction *action, GVariant *variant, gpointer) {
456 the_toy->show_timings = g_variant_get_boolean(variant);
457 g_simple_action_set_state(action, variant);
458}
459
460static void draw_callback(GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data)
461{
462 auto const size = Geom::IntPoint{width, height};
463
464 static std::optional<Geom::IntPoint> old_size;
465 if (size != old_size) {
466 old_size = size;
467 if (the_toy) {
468 the_toy->resize_canvas({{}, size});
469 }
470 }
471
472 cairo_rectangle(cr, 0, 0, width, height);
473 cairo_set_source_rgba(cr,1,1,1,1);
474 cairo_fill(cr);
475
476 if (the_toy) {
477 std::ostringstream notify, timer;
478
479 if (the_toy->spool_file) {
481 }
482
483 the_toy->draw(cr, &notify, width, height, false, &timer);
484 }
485}
486
487static void mouse_motion_event(GtkEventControllerMotion *self, double x, double y, gpointer user_data)
488{
489 if (the_toy) {
490 the_toy->mouse_moved({x, y},
491 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(self)));
492 }
493}
494
495static void mouse_press_event(GtkGestureClick *self, int n_press, double x, double y, gpointer user_data)
496{
497 if (the_toy) {
498 the_toy->mouse_pressed({x, y},
499 gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(self)),
500 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(self)));
501 }
502}
503
504static gboolean scroll_event(GtkEventControllerScroll *self, double dx, double dy, gpointer user_data)
505{
506 if (the_toy) {
507 auto ev = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(self));
508 auto dir = gdk_scroll_event_get_direction(ev);
509 the_toy->scroll(dir, {dx, dy});
510 }
511 return FALSE;
512}
513
514static void mouse_release_event(GtkGestureClick *self, int n_press, double x, double y, gpointer user_data)
515{
516 if (the_toy) {
517 the_toy->mouse_released({x, y},
518 gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(self)),
519 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(self)));
520 }
521}
522
523static gboolean key_press_event(GtkEventControllerKey *self, unsigned keyval, unsigned keycode, GdkModifierType state, gpointer user_data)
524{
525 if (the_toy) {
526 the_toy->key_hit(keyval, state);
527 }
528 return FALSE;
529}
530
531const char *the_builder_xml = R"xml(
532<?xml version="1.0" encoding="UTF-8"?>
533<interface>
534 <menu id="menu">
535 <submenu>
536 <attribute name="label">File</attribute>
537 <section>
538 <item>
539 <attribute name="label">Open Handles</attribute>
540 <attribute name="action">app.open-handles</attribute>
541 </item>
542 <item>
543 <attribute name="label">Save Handles</attribute>
544 <attribute name="action">app.save-handles</attribute>
545 </item>
546 </section>
547 <section>
548 <item>
549 <attribute name="label">Save as PNG, SVG or PDF</attribute>
550 <attribute name="action">app.save-image</attribute>
551 </item>
552 </section>
553 <section>
554 <item>
555 <attribute name="label">Show Timings</attribute>
556 <attribute name="action">app.show-timings</attribute>
557 </item>
558 <item>
559 <attribute name="label">Quit</attribute>
560 <attribute name="action">app.quit</attribute>
561 </item>
562 </section>
563 </submenu>
564 <submenu>
565 <attribute name="label">Help</attribute>
566 <item>
567 <attribute name="label">About</attribute>
568 <attribute name="action">app.about</attribute>
569 </item>
570 </submenu>
571 </menu>
572</interface>
573)xml";
574
575static GActionEntry the_actions[] =
576{
577 {"open-handles", open_handles, nullptr, nullptr, nullptr},
578 {"save-handles", save_handles, nullptr, nullptr, nullptr},
579 {"save-image", save_cairo, nullptr, nullptr, nullptr},
580 {"show-timings", toggle_action, nullptr, "false", set_show_timings},
581 {"quit", quit, nullptr, nullptr, nullptr},
582 {"about", show_about_dialog, nullptr, nullptr, nullptr},
583};
584
585static GOptionEntry const the_options[] = {
586 {"handles", 'h', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &arg_handles_filename,
587 "Load handle positions from given file", "FILE"},
588 {"spool", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &arg_spool_filename,
589 "Record all interaction to the given file", "FILE"},
590 {"screenshot", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &arg_screenshot_filename,
591 "Take screenshot and exit", nullptr},
592 {G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY, &arg_extra_files,
593 "Additional data files", "FILES..."},
594 {nullptr, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr},
595};
596
597static void activate(GApplication *app, gpointer);
598static void startup(GApplication *app, gpointer);
599
600void init(int argc, char **argv, Toy* t, int width, int height) {
601 the_toy = t;
604
605 std::string app_name = "org.inkscape.lib2geom.toy.";
606 char const *dir_pos = strrchr(argv[0], G_DIR_SEPARATOR);
607 std::string argv_name = dir_pos ? dir_pos + 1 : argv[0];
608
609 // Erase extension for Windows
610 size_t dot_pos = argv_name.rfind('.');
611 if (dot_pos != std::string::npos) {
612 argv_name.erase(dot_pos);
613 }
614 the_toy->name = argv_name;
615 app_name += argv_name;
616
617 GtkApplication* app = gtk_application_new(app_name.c_str(), G_APPLICATION_DEFAULT_FLAGS);
618 g_application_add_main_option_entries(G_APPLICATION(app), the_options);
619 g_action_map_add_action_entries(G_ACTION_MAP(app), the_actions, G_N_ELEMENTS(the_actions), nullptr);
620 g_signal_connect(G_OBJECT(app), "startup", G_CALLBACK(startup), nullptr);
621 g_signal_connect(G_OBJECT(app), "activate", G_CALLBACK(activate), nullptr);
622
623 g_application_run(G_APPLICATION(app), argc, argv);
624 g_object_unref(app);
625}
626
627static void startup(GApplication *app, gpointer) {
628 GtkBuilder *builder = gtk_builder_new_from_string(the_builder_xml, -1);
629 GMenuModel *menu = G_MENU_MODEL(gtk_builder_get_object(builder, "menu"));
630 gtk_application_set_menubar(GTK_APPLICATION(app), menu);
631 g_object_unref(builder);
632}
633
634static void activate(GApplication *app, gpointer) {
635 if (arg_spool_filename) {
637 }
638
639 int const emulated_argc = arg_extra_files ? g_strv_length(arg_extra_files) + 1 : 1;
640 gchar const **emulated_argv = new gchar const*[emulated_argc];
641 emulated_argv[0] = the_toy->name.c_str();
642 for (int i = 1; i < emulated_argc; ++i) {
643 emulated_argv[i] = arg_extra_files[i-1];
644 }
645 the_toy->first_time(emulated_argc, const_cast<char**>(emulated_argv));
646 delete[] emulated_argv;
647
649 FILE *handles_file = fopen(arg_handles_filename, "r");
650 the_toy->load(handles_file);
651 fclose(handles_file);
652 }
653
656 g_application_quit(app);
657 return;
658 }
659
660 the_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(g_application_get_default())));
661 gtk_window_set_title(GTK_WINDOW(the_window), the_toy->name.c_str());
662 gtk_application_window_set_show_menubar(the_window, TRUE);
663
664 the_canvas = gtk_drawing_area_new();
665 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(the_canvas), draw_callback, nullptr, nullptr);
666
667 auto scroll = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
668 g_signal_connect(scroll, "scroll", G_CALLBACK(scroll_event), nullptr);
669 gtk_widget_add_controller(the_canvas, scroll);
670
671 auto click = gtk_gesture_click_new();
672 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click), 0);
673 g_signal_connect(click, "pressed", G_CALLBACK(mouse_press_event), nullptr);
674 g_signal_connect(click, "released", G_CALLBACK(mouse_release_event), nullptr);
675 gtk_widget_add_controller(the_canvas, GTK_EVENT_CONTROLLER(click));
676
677 auto motion = gtk_event_controller_motion_new();
678 g_signal_connect(motion, "motion", G_CALLBACK(mouse_motion_event), nullptr);
679 gtk_widget_add_controller(the_canvas, motion);
680
681 auto key = gtk_event_controller_key_new();
682 g_signal_connect(key, "key-pressed", G_CALLBACK(key_press_event), nullptr);
683 gtk_widget_add_controller(GTK_WIDGET(the_window), key);
684
685 gtk_window_set_child(GTK_WINDOW(the_window), the_canvas);
686 gtk_window_set_default_size(GTK_WINDOW(the_window), the_requested_width, the_requested_height);
687
688 gtk_window_present(GTK_WINDOW(the_window));
689}
690
691void Toggle::draw(cairo_t *cr, bool /*annotes*/) {
692 cairo_pattern_t* source = cairo_get_source(cr);
693 double rc, gc, bc, aa;
694 cairo_pattern_get_rgba(source, &rc, &gc, &bc, &aa);
695 cairo_set_source_rgba(cr,0,0,0,1);
698 if(on) {
699 cairo_fill(cr);
700 cairo_set_source_rgba(cr,1,1,1,1);
701 } //else cairo_stroke(cr);
702 cairo_stroke(cr);
703 draw_text(cr, bounds.corner(0) + Geom::Point(5,2), text);
704 cairo_set_source_rgba(cr, rc, gc, bc, aa);
705}
706
708 on = !on;
709}
710void Toggle::set(bool state) {
711 on = state;
712}
713
714
715void Toggle::handle_click(Geom::Point const &pos, unsigned button)
716{
717 if (bounds.contains(pos) && button == 1) {
718 toggle();
719 }
720}
721
723{
724 if (bounds.contains(mouse))
725 {
726 toggle();
727 return this;
728 }
729 return 0;
730}
731
732void toggle_events(std::vector<Toggle> &ts, Geom::Point const &pos, unsigned button)
733{
734 for (auto &t : ts) {
735 t.handle_click(pos, button);
736 }
737}
738
739void draw_toggles(cairo_t *cr, std::vector<Toggle> &ts) {
740 for(auto & t : ts) t.draw(cr);
741}
742
744{
746 v = ((m_max - m_min) / m_length) * v;
747 if (m_step != 0)
748 {
749 int k = std::floor(v / m_step);
750 v = k * m_step;
751 }
752 v = v + m_min;
753 return v;
754}
755
757{
758 if ( _value < m_min ) _value = m_min;
759 if ( _value > m_max ) _value = m_max;
760 if (m_step != 0)
761 {
762 _value = _value - m_min;
763 int k = std::floor(_value / m_step);
764 _value = k * m_step + m_min;
765 }
767 = (m_length / (m_max - m_min)) * (_value - m_min) + m_pos[m_dir];
768}
769
771{
773 m_max = _value;
774 value(v);
775}
776
778{
780 m_min = _value;
781 value(v);
782}
783
784// dir = X horizontal slider dir = Y vertical slider
786 Slider::value_type _length,
787 Geom::Dim2 _dir )
788{
790 m_pos = _pos;
791 m_length = _length;
792 m_dir = _dir;
793 Geom::Dim2 fix_dir = static_cast<Geom::Dim2>( (m_dir + 1) % 2 );
794 m_handle.pos[fix_dir] = m_pos[fix_dir];
795 value(v);
796}
797
798void Slider::draw(cairo_t* cr, bool annotate)
799{
800 cairo_pattern_t* source = cairo_get_source(cr);
801 double rc, gc, bc, aa;
802 cairo_pattern_get_rgba(source, &rc, &gc, &bc, &aa);
803 double lw = cairo_get_line_width(cr);
804 std::ostringstream os;
805 os << m_label << ": " << (*m_formatter)(value());
806 cairo_set_source_rgba(cr, 0.1, 0.1, 0.7, 1.0);
807 cairo_set_line_width(cr, 0.7);
808 m_handle.draw(cr, annotate);
809 cairo_stroke(cr);
810 cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 1.0);
811 cairo_set_line_width(cr, 0.4);
812 m_handle.draw(cr, annotate);
815 if ( m_dir == Geom::X )
816 {
817 cairo_rel_line_to(cr, m_length, 0);
818 offset = Geom::Point(0,5);
819 }
820 else
821 {
822 cairo_rel_line_to(cr, 0, m_length);
823 offset = Geom::Point(5,0);
824 }
825 cairo_stroke(cr);
826 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
827 draw_text(cr, m_pos + offset, os.str().c_str());
828 cairo_set_source_rgba(cr, rc, gc, bc, aa);
829 cairo_set_line_width(cr, lw);
830}
831
833{
834 // fix_dir == ! m_dir
835 Geom::Dim2 fix_dir = static_cast<Geom::Dim2>( (m_dir + 1) % 2 );
836 m[fix_dir] = m_pos[fix_dir];
837 double diff = m[m_dir] - m_pos[m_dir];
838// if (m_step != 0)
839// {
840// double step = (m_step * m_length) / (m_max - m_min) ;
841// int k = std::floor(diff / step);
842// double v = k * step;
843// m[m_dir] = v + m_pos[m_dir];
844// }
845 if ( diff < 0 ) m[m_dir] = m_pos[m_dir];
846 if ( diff > m_length ) m[m_dir] = m_pos[m_dir] + m_length;
847 m_handle.move_to(hit, om, m);
848}
849
850
851
852void PointHandle::draw(cairo_t *cr, bool /*annotes*/) {
853 draw_circ(cr, pos);
854}
855
857 if(Geom::distance(mouse, pos) < 5)
858 return this;
859 return 0;
860}
861
862void PointHandle::move_to(void* /*hit*/, Geom::Point /*om*/, Geom::Point m) {
863 pos = m;
864}
865
866void PointHandle::load(FILE* f) {
867 pos = read_point(f);
868}
869
870void PointHandle::save(FILE* f) {
871 fprintf(f, "%lf %lf\n", pos[0], pos[1]);
872}
873
874void PointSetHandle::draw(cairo_t *cr, bool annotes) {
875 for(unsigned i = 0; i < pts.size(); i++) {
876 draw_circ(cr, pts[i]);
877 if(annotes) draw_number(cr, pts[i], i, name);
878 }
879}
880
882 for(auto & pt : pts) {
883 if(Geom::distance(mouse, pt) < 5)
884 return (void*)(&pt);
885 }
886 return 0;
887}
888
890 if(hit) {
891 *(Geom::Point*)hit = m;
892 }
893}
894
895void PointSetHandle::load(FILE* f) {
896 int n = 0;
897 assert(1 == fscanf(f, "%d\n", &n));
898 pts.clear();
899 for(int i = 0; i < n; i++) {
900 pts.push_back(read_point(f));
901 }
902}
903
904void PointSetHandle::save(FILE* f) {
905 fprintf(f, "%d\n", (int)pts.size());
906 for(auto & pt : pts) {
907 fprintf(f, "%lf %lf\n", pt[0], pt[1]);
908 }
909}
910
912
914 return handles_to_sbasis(pts.begin(), size()-1);
915}
916
917void RectHandle::draw(cairo_t *cr, bool /*annotes*/) {
918 cairo_rectangle(cr, pos);
919 cairo_stroke(cr);
921 draw_circ(cr, pos.midpoint());
922 }
923 draw_text(cr, pos.corner(0), name);
924}
925
928 if(Geom::distance(mouse, pos.midpoint()) < 5)
929 return (void*)(intptr_t)1;
930 }
931 for(int i = 0; i < 4; i++) {
932 if(Geom::distance(mouse, pos.corner(i)) < 5)
933 return (void*)(intptr_t)(2+i);
934 }
935 for(int i = 0; i < 4; i++) {
937 if(Geom::distance(ls.pointAt(ls.nearestTime(mouse)),mouse) < 5)
938 return (void*)(intptr_t)(6+i);
939 }
940 return 0;
941
942}
943
945 using Geom::X;
946 using Geom::Y;
947
948 unsigned h = (unsigned)(uintptr_t)(hit);
949 if(h == 1)
950 pos += (m-om);
951 else if(h >= 2 and h <= 5) {// corners
952 int xi = (h-2)& 1;
953 int yi = (h-2)&2;
954 if(yi)
955 xi = 1-xi; // clockwise
956 if (xi) {
957 pos[X].setMax(m[0]);
958 } else {
959 pos[X].setMin(m[0]);
960 }
961 if (yi/2) {
962 pos[Y].setMax(m[1]);
963 } else {
964 pos[Y].setMax(m[1]);
965 }
966 } else if(h >= 6 and h <= 9) {// edges
967 int side, d;
968 switch(h-6) {
969 case 0: d = 1; side = 0; break;
970 case 1: d = 0; side = 1; break;
971 case 2: d = 1; side = 1; break;
972 case 3: d = 0; side = 0; break;
973 default: assert(false);
974 }
975 if (side) {
976 pos[d].setMax(m[d]);
977 } else {
978 pos[d].setMin(m[d]);
979 }
980 }
981}
982
983void RectHandle::load(FILE* f) {
984 assert(0 == fscanf(f, "r\n"));
985 for(int i = 0; i < 2; i++) {
986 pos[i] = read_interval(f);
987 }
988
989}
990
991void RectHandle::save(FILE* f) {
992 fprintf(f, "r\n");
993 for(unsigned i = 0; i < 2; i++) {
994 fprintf(f, "%lf %lf\n", pos[i].min(), pos[i].max());
995 }
996}
997
998
999
1000/*
1001 Local Variables:
1002 mode:c++
1003 c-file-style:"stroustrup"
1004 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1005 indent-tabs-mode:nil
1006 fill-column:99
1007 End:
1008 */
1009// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4:fileencoding=utf-8:textwidth=99 :
_PangoFontDescription PangoFontDescription
Definition Layout-TNG.h:44
Conversion between Bezier control points and SBasis curves.
struct _PangoLayout PangoLayout
Coord nearestTime(Point const &p, Coord from=0, Coord to=1) const override
Compute a time value at which the curve comes closest to a specified point.
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr void setEnds(C a, C b)
Set both ends of the interval simultaneously.
void setMin(CPoint const &p)
Set the upper left point of the rectangle.
bool contains(GenericRect< C > const &r) const
Check whether the rectangle includes all points in the given rectangle.
C top() const
Return top coordinate of the rectangle (+Y is downwards).
void setMax(CPoint const &p)
Set the lower right point of the rectangle.
CPoint midpoint() const
Get the point in the geometric center of the rectangle.
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Two-dimensional point with integer coordinates.
Definition int-point.h:57
Range of real numbers that is never empty.
Definition interval.h:59
Two-dimensional point that doubles as a vector.
Definition point.h:66
virtual void move_to(void *hit, Geom::Point om, Geom::Point m)=0
std::string name
virtual void draw(cairo_t *cr, bool annotes=false)=0
void draw(cairo_t *cr, bool annotes=false) override
Geom::Point pos
void load(FILE *f) override
void move_to(void *hit, Geom::Point om, Geom::Point m) override
void save(FILE *f) override
void * hit(Geom::Point mouse) override
void load(FILE *f) override
void move_to(void *hit, Geom::Point om, Geom::Point m) override
void save(FILE *f) override
void draw(cairo_t *cr, bool annotes=false) override
void * hit(Geom::Point mouse) override
Geom::D2< Geom::SBasis > asBezier()
std::vector< Geom::Point > pts
void move_to(void *hit, Geom::Point om, Geom::Point m) override
void draw(cairo_t *cr, bool annotes=false) override
void load(FILE *f) override
Geom::Rect pos
void * hit(Geom::Point mouse) override
void save(FILE *f) override
bool show_center_handle
value_type m_max
value_type max_value() const
void draw(cairo_t *cr, bool annotate=false) override
std::string m_label
value_type min_value() const
void * hit(Geom::Point pos) override
value_type value() const
void geometry(Geom::Point _pos, value_type _length, Geom::Dim2 _dir=Geom::X)
double value_type
void move_to(void *hit, Geom::Point om, Geom::Point m) override
value_type m_min
PointHandle m_handle
value_type m_length
Geom::Point m_pos
value_type m_step
void set(bool state)
Geom::Rect bounds
void handle_click(Geom::Point const &pos, unsigned button)
const char * text
void draw(cairo_t *cr, bool annotes=false) override
void * hit(Geom::Point pos) override
virtual void first_time(int, char **)
virtual void canvas_click(Geom::Point at, int button)
virtual void load(FILE *f)
virtual void mouse_pressed(Geom::Point const &pos, unsigned button, unsigned modifiers)
Geom::Point old_mouse_point
virtual int should_draw_bounds()
virtual void mouse_moved(Geom::Point const &pos, unsigned modifiers)
bool mouse_down
virtual bool should_draw_numbers()
double notify_offset
vector< Handle * > handles
Handle * selected
virtual void mouse_released(Geom::Point const &pos, unsigned button, unsigned modifiers)
virtual void save(FILE *f)
int canvas_click_button
virtual void resize_canvas(Geom::Rect const &)
void * hit_data
bool show_timings
std::string name
virtual void key_hit(unsigned keyval, unsigned modifiers)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
FILE * spool_file
virtual void scroll(GdkScrollDirection dir, Geom::Point const &delta)
RectangularCluster rc
Geom::IntPoint size
double c[8][4]
_cairo_pattern cairo_pattern_t
struct _cairo_surface cairo_surface_t
int _pos
Dim2
2D axis enumeration (X or Y).
Definition coord.h:48
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
double offset
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
static cairo_user_data_key_t key
void cairo_rectangle(cairo_t *cr, Geom::Rect const &r)
void cairo_line_to(cairo_t *cr, Geom::Point p1)
struct _cairo cairo_t
Definition path-cairo.h:16
void draw_circ(cairo_t *cr, Geom::Point h)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
int buf
int num
Definition scribble.cpp:47
static const Point data[]
static colour from_hsl(float H, float S, float L, float A)
static colour from_hsv(float H, float S, float V, float A)
int delta
double height
double width
Glib::ustring name
Definition toolbars.cpp:55
static void mouse_motion_event(GtkEventControllerMotion *self, double x, double y, gpointer user_data)
static void mouse_press_event(GtkGestureClick *self, int n_press, double x, double y, gpointer user_data)
const char * the_builder_xml
static void mouse_release_event(GtkGestureClick *self, int n_press, double x, double y, gpointer user_data)
gchar ** the_emulated_argv
void save_cairo_cb(GObject *source_object, GAsyncResult *res, gpointer data)
gchar * arg_spool_filename
void write_image(char const *filename)
double uniform()
static void draw_callback(GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data)
void open_handles_cb(GObject *source_object, GAsyncResult *res, gpointer data)
void init(int argc, char **argv, Toy *t, int width, int height)
void draw_number(cairo_t *cr, Geom::Point pos, int num, std::string name, bool bottom)
gchar * arg_screenshot_filename
gchar * arg_handles_filename
void toggle_events(std::vector< Toggle > &ts, Geom::Point const &pos, unsigned button)
static gboolean key_press_event(GtkEventControllerKey *self, unsigned keyval, unsigned keycode, GdkModifierType state, gpointer user_data)
Geom::Point read_point(FILE *f)
void cairo_set_source_rgba(cairo_t *cr, colour c)
static void activate(GApplication *app, gpointer)
void get_clipboard_text(std::function< void(char const *)> &&on_completion)
static GActionEntry the_actions[]
void open_handles(GSimpleAction *, GVariant *, gpointer)
int the_requested_width
static gboolean scroll_event(GtkEventControllerScroll *self, double dx, double dy, gpointer user_data)
void quit(GSimpleAction *, GVariant *, gpointer)
void save_handles_cb(GObject *source_object, GAsyncResult *res, gpointer data)
void show_about_dialog(GSimpleAction *, GVariant *, gpointer)
static void startup(GApplication *app, gpointer)
void save_cairo(GSimpleAction *, GVariant *, gpointer)
void draw_toggles(cairo_t *cr, std::vector< Toggle > &ts)
static void set_show_timings(GSimpleAction *action, GVariant *variant, gpointer)
Toy * the_toy
GtkApplicationWindow * the_window
void save_handles(GSimpleAction *, GVariant *, gpointer)
static GtkWidget * the_canvas
void redraw()
static void toggle_action(GSimpleAction *action, GVariant *, gpointer)
static GOptionEntry const the_options[]
Geom::Interval read_interval(FILE *f)
void draw_text(cairo_t *cr, Geom::Point loc, const char *txt, bool bottom, const char *fontdesc)
gchar ** arg_extra_files
int the_requested_height
void draw_text(cairo_t *cr, Geom::Point pos, const char *txt, bool bottom=false, const char *fontdesc="Sans")
Geom::Point read_point(FILE *f)
void cairo_set_source_rgba(cairo_t *cr, colour c)
void draw_number(cairo_t *cr, Geom::Point pos, int num, std::string name=std::string(), bool bottom=true)
void redraw()
Glib::RefPtr< Gtk::Builder > builder
struct _GObject GObject