Skip to content

Commit 755ca30

Browse files
extend the demo so it shows off translation
1 parent acbefda commit 755ca30

File tree

1 file changed

+38
-12
lines changed

1 file changed

+38
-12
lines changed

egui_demo_lib/src/apps/demo/zoom_rotate.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
use egui::{
22
emath::{RectTransform, Rot2},
3-
vec2, Color32, Frame, Pos2, Rect, Sense, Stroke,
3+
vec2, Color32, Frame, Pos2, Rect, Sense, Stroke, Vec2,
44
};
55

66
pub struct ZoomRotate {
7+
previous_arrow_start_offset: Vec2,
78
rotation: f32,
9+
smoothed_velocity: Vec2,
10+
translation: Vec2,
811
zoom: f32,
912
}
1013

1114
impl Default for ZoomRotate {
1215
fn default() -> Self {
1316
Self {
17+
previous_arrow_start_offset: Vec2::ZERO,
1418
rotation: 0.,
19+
smoothed_velocity: Vec2::ZERO,
20+
translation: Vec2::ZERO,
1521
zoom: 1.,
1622
}
1723
}
@@ -64,6 +70,7 @@ impl super::View for ZoomRotate {
6470
Rect::from_min_size(Pos2::ZERO - painter_proportions, 2. * painter_proportions),
6571
response.rect,
6672
);
73+
let dt = ui.input().unstable_dt;
6774

6875
// check for touch input (or the lack thereof) and update zoom and scale factors, plus
6976
// color and width:
@@ -74,6 +81,9 @@ impl super::View for ZoomRotate {
7481
// change (for the current frame) of the touch gesture:
7582
self.zoom *= multi_touch.zoom_delta;
7683
self.rotation += multi_touch.rotation_delta;
84+
// the translation we get from `multi_touch` needs to be scaled down to the
85+
// normalized coordinates we use as the basis for painting:
86+
self.translation += to_screen.inverse().scale() * multi_touch.translation_delta;
7787
// touch pressure shall make the arrow thicker (not all touch devices support this):
7888
stroke_width += 10. * multi_touch.force;
7989
// the drawing color depends on the number of touches:
@@ -83,32 +93,48 @@ impl super::View for ZoomRotate {
8393
4 => Color32::YELLOW,
8494
_ => Color32::RED,
8595
};
86-
// for a smooth touch experience (not strictly required, but I had the impression
87-
// that it helps to reduce some lag, especially for the initial touch):
88-
ui.ctx().request_repaint();
8996
} else {
9097
// This has nothing to do with the touch gesture. It just smoothly brings the
9198
// painted arrow back into its original position, for a nice visual effect:
92-
let dt = ui.input().unstable_dt;
9399
const ZOOM_ROTATE_HALF_LIFE: f32 = 1.; // time[sec] after which half the amount of zoom/rotation will be reverted
94100
let half_life_factor = (-(2_f32.ln()) / ZOOM_ROTATE_HALF_LIFE * dt).exp();
95101
self.zoom = 1. + ((self.zoom - 1.) * half_life_factor);
96102
self.rotation *= half_life_factor;
97-
// this is an animation, so we want real-time UI updates:
98-
ui.ctx().request_repaint();
103+
self.translation *= half_life_factor;
99104
}
100-
101105
let zoom_and_rotate = self.zoom * Rot2::from_angle(self.rotation);
106+
let arrow_start_offset = self.translation + zoom_and_rotate * vec2(-0.5, 0.5);
107+
let current_velocity = (arrow_start_offset - self.previous_arrow_start_offset) / dt;
108+
self.previous_arrow_start_offset = arrow_start_offset;
109+
110+
// aggregate the average velocity of the arrow's start position from latest samples:
111+
const NUM_SMOOTHING_SAMPLES: f32 = 10.;
112+
self.smoothed_velocity = ((NUM_SMOOTHING_SAMPLES - 1.) * self.smoothed_velocity
113+
+ current_velocity)
114+
/ NUM_SMOOTHING_SAMPLES;
102115

103-
// Paints an arrow pointing from bottom-left (-0.5, 0.5) to top-right (0.5, -0.5),
104-
// but scaled and rotated according to the current translation:
105-
let arrow_start = zoom_and_rotate * vec2(-0.5, 0.5);
116+
// Paints an arrow pointing from bottom-left (-0.5, 0.5) to top-right (0.5, -0.5), but
117+
// scaled, rotated, and translated according to the current touch gesture:
118+
let arrow_start = Pos2::ZERO + arrow_start_offset;
106119
let arrow_direction = zoom_and_rotate * vec2(1., -1.);
107120
painter.arrow(
108-
to_screen * (Pos2::ZERO + arrow_start),
121+
to_screen * arrow_start,
109122
to_screen.scale() * arrow_direction,
110123
Stroke::new(stroke_width, color),
111124
);
125+
// Paints a circle at the origin of the arrow. The size and opacity of the circle
126+
// depend on the current velocity, and the circle is translated in the opposite
127+
// direction of the movement, so it follows the origin's movement. Constant factors
128+
// have been determined by trial and error.
129+
let speed = self.smoothed_velocity.length();
130+
painter.circle_filled(
131+
to_screen * (arrow_start - 0.2 * self.smoothed_velocity),
132+
2. + to_screen.scale().length() * 0.1 * speed,
133+
Color32::RED.linear_multiply(1. / (1. + (5. * speed).powi(2))),
134+
);
135+
136+
// we want continuous UI updates, so the circle can smoothly follow the arrow's origin:
137+
ui.ctx().request_repaint();
112138
});
113139
}
114140
}

0 commit comments

Comments
 (0)