1 #include "canvas_gl.hpp"
2 #include <algorithm>
3 #include <epoxy/gl.h>
4 #include <iostream>
5 #include "util/warp_cursor.hpp"
6
7 namespace horizon {
pan_drag_begin(GdkEventButton * button_event)8 void CanvasGL::pan_drag_begin(GdkEventButton *button_event)
9 {
10 if (gesture_zoom->is_recognized() || gesture_drag->is_recognized())
11 return;
12
13 gdouble x, y;
14 gdk_event_get_coords((GdkEvent *)button_event, &x, &y);
15 if (button_event->button == 2 || (button_event->button == 1 && (button_event->state & Gdk::SHIFT_MASK))) {
16 pan_dragging = true;
17 pan_pointer_pos_orig = {(float)x, (float)y};
18 pan_offset_orig = offset;
19 }
20 }
21
pan_drag_end(GdkEventButton * button_event)22 void CanvasGL::pan_drag_end(GdkEventButton *button_event)
23 {
24 pan_dragging = false;
25 }
26
pan_drag_move(GdkEventMotion * motion_event)27 void CanvasGL::pan_drag_move(GdkEventMotion *motion_event)
28 {
29 gdouble x, y;
30 gdk_event_get_coords((GdkEvent *)motion_event, &x, &y);
31
32 if (pan_dragging) {
33 const Coordi warp_distance = warp_cursor((GdkEvent *)motion_event, *this);
34 offset = pan_offset_orig + Coordf(x, y) - pan_pointer_pos_orig;
35 update_viewmat();
36 pan_pointer_pos_orig += warp_distance;
37 }
38 }
39
pan_drag_move(GdkEventScroll * scroll_event)40 void CanvasGL::pan_drag_move(GdkEventScroll *scroll_event)
41 {
42 gdouble dx, dy;
43 gdk_event_get_scroll_deltas((GdkEvent *)scroll_event, &dx, &dy);
44 offset.x -= dx * 50;
45 offset.y -= dy * 50;
46 update_viewmat();
47 }
48
set_scale(float x,float y,float scale_new)49 void CanvasGL::set_scale(float x, float y, float scale_new)
50 {
51 float sc = scale;
52 if (scale_new < 1e-7 || scale_new > 1e-2) {
53 return;
54 }
55 this->scale = scale_new;
56 float xp = (x - offset.x) / sc;
57 float yp = -(y - offset.y) / sc;
58 float xi = xp * (sc - scale_new);
59 float yi = yp * (scale_new - sc);
60 offset.x += xi;
61 offset.y += yi;
62 if (pan_dragging) {
63 pan_offset_orig.x += xi;
64 pan_offset_orig.y += yi;
65 }
66 if (gesture_zoom->is_recognized()) {
67 gesture_zoom_offset_orig.x += xi;
68 gesture_zoom_offset_orig.y += yi;
69 }
70 update_viewmat();
71 s_signal_scale_changed.emit();
72 }
73
_animate_step(GdkFrameClock * frame_clock)74 int CanvasGL::_animate_step(GdkFrameClock *frame_clock)
75 {
76 auto r = zoom_animator.step(gdk_frame_clock_get_frame_time(frame_clock) / 1e6);
77 if (!r) { // should stop
78 return G_SOURCE_REMOVE;
79 }
80 auto s = zoom_animator.get_s();
81
82 set_scale(zoom_animation_pos.x, zoom_animation_pos.y, zoom_animation_scale_orig * pow(zoom_base, s));
83
84 if (std::abs((s - zoom_animator.target) / std::max(std::abs(zoom_animator.target), 1.f)) < .005) {
85 set_scale(zoom_animation_pos.x, zoom_animation_pos.y,
86 zoom_animation_scale_orig * pow(zoom_base, zoom_animator.target));
87 zoom_animator.stop();
88 return G_SOURCE_REMOVE;
89 }
90
91 return G_SOURCE_CONTINUE;
92 }
93
tick_cb(GtkWidget * cwidget,GdkFrameClock * frame_clock,gpointer user_data)94 static int tick_cb(GtkWidget *cwidget, GdkFrameClock *frame_clock, gpointer user_data)
95 {
96 Gtk::Widget *widget = Glib::wrap(cwidget);
97 auto canvas = dynamic_cast<CanvasGL *>(widget);
98 return canvas->_animate_step(frame_clock);
99 }
100
101
pan_zoom(GdkEventScroll * scroll_event,bool to_cursor)102 void CanvasGL::pan_zoom(GdkEventScroll *scroll_event, bool to_cursor)
103 {
104 if (gesture_zoom->is_recognized() || gesture_drag->is_recognized())
105 return;
106 gdouble x, y;
107 if (to_cursor) {
108 gdk_event_get_coords((GdkEvent *)scroll_event, &x, &y);
109 }
110 else {
111 x = m_width / 2;
112 y = m_height / 2;
113 }
114
115 float inc = 0;
116 float factor = 1;
117 if (scroll_event->state & Gdk::SHIFT_MASK)
118 factor = .5;
119 if (scroll_event->direction == GDK_SCROLL_UP) {
120 inc = 1;
121 }
122 else if (scroll_event->direction == GDK_SCROLL_DOWN) {
123 inc = -1;
124 }
125 else if (scroll_event->direction == GDK_SCROLL_SMOOTH) {
126 gdouble sx, sy;
127 gdk_event_get_scroll_deltas((GdkEvent *)scroll_event, &sx, &sy);
128 inc = -sy;
129 }
130 inc *= factor;
131 if (smooth_zoom) {
132 start_smooth_zoom(Coordf(x, y), inc);
133 }
134 else {
135 set_scale(x, y, scale * pow(zoom_base, inc));
136 }
137 }
138
start_smooth_zoom(const Coordf & c,float inc)139 void CanvasGL::start_smooth_zoom(const Coordf &c, float inc)
140 {
141 if (inc == 0)
142 return;
143 if (!zoom_animator.is_running()) {
144 zoom_animator.start();
145 zoom_animation_scale_orig = scale;
146 gtk_widget_add_tick_callback(GTK_WIDGET(gobj()), &tick_cb, nullptr, nullptr);
147 }
148 zoom_animator.target += inc;
149 zoom_animation_pos = c;
150 }
151
152
153 } // namespace horizon
154