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