1use std::fmt::Debug;
2
3use crate::{
4 GeoFig,
5 drawme::{Annotation, BboxAnnotation, Stroke},
6 events::{Events, KeyCode},
7 history::History,
8 make_tool_transform,
9 tools::core::Manipulate,
10 tools_data::InstanceLabelDisplay,
11 types::ViewImage,
12 world::World,
13};
14use rvimage_domain::{BbF, OutOfBoundsMode, PtF, ShapeI, TPtF};
15
16use super::core::Mover;
17const MIN_ZOOM: TPtF = 2.0;
18
19pub fn move_zoom_box(mut mover: Mover, mut world: World, mouse_pos: Option<PtF>) -> (Mover, World) {
20 let shape_orig = world.data.shape();
21 let zoom_box = *world.zoom_box();
22 let f_move = |mp_from, mp_to| follow_zoom_box(mp_from, mp_to, shape_orig, zoom_box);
23 let opt_opt_zoom_box = mover.move_mouse_held(f_move, mouse_pos);
24 if let Some(zoom_box) = opt_opt_zoom_box {
25 world.set_zoom_box(zoom_box);
26 }
27 (mover, world)
28}
29
30fn make_zoom_on_release<P>(mp_start: P, mp_release: P) -> Option<BbF>
31where
32 P: Into<PtF>,
33{
34 let mp_start = mp_start.into();
35 let mp_release = mp_release.into();
36 let x_min = mp_start.x.min(mp_release.x);
37 let y_min = mp_start.y.min(mp_release.y);
38 let x_max = mp_start.x.max(mp_release.x);
39 let y_max = mp_start.y.max(mp_release.y);
40
41 let w = x_max - x_min;
42 let h = y_max - y_min;
43 if w >= MIN_ZOOM && h >= MIN_ZOOM {
44 Some(BbF {
45 x: x_min,
46 y: y_min,
47 w,
48 h,
49 })
50 } else {
51 None
52 }
53}
54
55fn follow_zoom_box(
56 mp_from: PtF,
57 mp_to: PtF,
58 shape_orig: ShapeI,
59 zoom_box: Option<BbF>,
60) -> Option<BbF> {
61 match zoom_box {
62 Some(zb) => match zb.follow_movement(mp_to, mp_from, shape_orig, OutOfBoundsMode::Deny) {
65 Some(zb) => Some(zb),
66 None => Some(zb),
67 },
68 _ => zoom_box,
69 }
70}
71
72#[derive(Clone, Debug)]
73pub struct Zoom {
74 mouse_pressed_start_pos: Option<PtF>,
75 mover: Mover,
76 initial_view: Option<ViewImage>,
77}
78impl Zoom {
79 fn set_mouse_start_zoom(&mut self, mp: PtF) {
80 self.mouse_pressed_start_pos = Some(mp);
81 }
82
83 fn unset_mouse_start_zoom(&mut self) {
84 self.mouse_pressed_start_pos = None;
85 self.initial_view = None;
86 }
87
88 fn mouse_pressed(
89 &mut self,
90 events: &Events,
91 world: World,
92 history: History,
93 ) -> (World, History) {
94 if events.pressed(KeyCode::MouseRight) {
95 self.mover.move_mouse_pressed(events.mouse_pos_on_view);
96 } else if let Some(mp) = events.mouse_pos_on_orig {
97 self.set_mouse_start_zoom(mp);
98 }
99 (world, history)
100 }
101
102 fn mouse_released_left_btn(&mut self, mut world: World, mouse_pos: Option<PtF>) -> World {
103 let bx = if let (Some(mps), Some(mr)) = (self.mouse_pressed_start_pos, mouse_pos) {
104 make_zoom_on_release(mps, mr).or(*world.zoom_box())
105 } else {
106 *world.zoom_box()
107 };
108 world.set_zoom_box(bx);
109 world.stop_tmp_anno();
110 self.unset_mouse_start_zoom();
111 world
112 }
113
114 fn mouse_released(
115 &mut self,
116 events: &Events,
117 mut world: World,
118 history: History,
119 ) -> (World, History) {
120 if events.released(KeyCode::MouseRight) || events.held_ctrl() {
121 self.unset_mouse_start_zoom();
122 (world, history)
123 } else if events.released(KeyCode::MouseLeft) {
124 world = self.mouse_released_left_btn(world, events.mouse_pos_on_orig);
125 (world, history)
126 } else {
127 (world, history)
128 }
129 }
130
131 fn mouse_held(
132 &mut self,
133 events: &Events,
134 mut world: World,
135 history: History,
136 ) -> (World, History) {
137 if events.held(KeyCode::MouseRight) || events.held_ctrl() {
138 (self.mover, world) = move_zoom_box(self.mover, world, events.mouse_pos_on_view);
139 } else if events.held(KeyCode::MouseLeft)
140 && let (Some(mps), Some(m)) = (self.mouse_pressed_start_pos, events.mouse_pos_on_orig)
141 {
142 let bb = BbF::from_points(mps, m);
144 let white = [255, 255, 255];
145 let anno = BboxAnnotation {
146 geofig: GeoFig::BB(bb),
147 fill_color: None,
148 fill_alpha: 0,
149 outline: Stroke::from_color(white),
150 outline_alpha: 255,
151 label: None,
152 is_selected: None,
153 highlight_circles: vec![],
154 instance_label_display: InstanceLabelDisplay::None,
155 };
156 world.request_redraw_tmp_anno(Annotation::Bbox(anno));
157 }
158 (world, history)
159 }
160
161 fn key_pressed(
162 &mut self,
163 _event: &Events,
164 mut world: World,
165 history: History,
166 ) -> (World, History) {
167 world.set_zoom_box(None);
168 (world, history)
169 }
170}
171impl Manipulate for Zoom {
172 fn new() -> Zoom {
173 Zoom {
174 mouse_pressed_start_pos: None,
175 initial_view: None,
176 mover: Mover::new(),
177 }
178 }
179 fn events_tf(&mut self, world: World, history: History, events: &Events) -> (World, History) {
180 make_tool_transform!(
181 self,
182 world,
183 history,
184 events,
185 [
186 (pressed, KeyCode::MouseLeft, mouse_pressed),
187 (pressed, KeyCode::MouseRight, mouse_pressed),
188 (released, KeyCode::MouseLeft, mouse_released),
189 (held, KeyCode::MouseLeft, mouse_held),
190 (held, KeyCode::MouseRight, mouse_held),
191 (pressed, KeyCode::Back, key_pressed)
192 ]
193 )
194 }
195}
196
197#[cfg(test)]
198use {
199 crate::{tools_data::ToolsDataMap, types::ExtraIms},
200 image::DynamicImage,
201 rvimage_domain::RvResult,
202 std::path::Path,
203};
204#[cfg(test)]
205fn mk_z(x: TPtF, y: TPtF, w: TPtF, h: TPtF) -> Option<BbF> {
206 Some(BbF { x, y, w, h })
207}
208#[test]
209fn test_make_zoom() -> RvResult<()> {
210 fn test(mps: (TPtF, TPtF), mpr: (TPtF, TPtF), expected: Option<BbF>) {
211 assert_eq!(make_zoom_on_release(mps, mpr), expected);
212 }
213
214 test((0.0, 0.0), (10.0, 10.0), mk_z(0.0, 0.0, 10.0, 10.0));
215 test((0.0, 0.0), (100.0, 10.0), mk_z(0.0, 0.0, 100.0, 10.0));
216 test((13.0, 7.0), (33.0, 17.0), mk_z(13.0, 7.0, 20.0, 10.0));
217 test((5.0, 9.0), (6.0, 9.0), None);
218 test((5.0, 9.0), (17.0, 19.0), mk_z(5.0, 9.0, 12.0, 10.0));
219
220 Ok(())
221}
222#[test]
223fn test_move_zoom() -> RvResult<()> {
224 fn test(
225 mpp: (usize, usize),
226 mph: (usize, usize),
227 zoom_box: Option<BbF>,
228 expected: Option<BbF>,
229 ) {
230 let mpp = (mpp.0 as TPtF, mpp.1 as TPtF).into();
231 let mph = (mph.0 as TPtF, mph.1 as TPtF).into();
232 let shape_orig = ShapeI { w: 80, h: 80 };
233 assert_eq!(follow_zoom_box(mpp, mph, shape_orig, zoom_box), expected);
234 }
235 test(
236 (4, 4),
237 (12, 8),
238 mk_z(12.0, 16.0, 40.0, 40.0),
239 mk_z(4.0, 12.0, 40.0, 40.0),
240 );
241 Ok(())
242}
243#[test]
244fn test_on_mouse_pressed() -> RvResult<()> {
245 let mouse_pos = Some((30.0, 45.0).into());
246 let im_orig = DynamicImage::ImageRgb8(ViewImage::new(250, 500));
247 let mut z = Zoom::new();
248 let prj_path = Path::new("");
249 let world = World::from_real_im(
250 im_orig,
251 ExtraIms::default(),
252 ToolsDataMap::new(),
253 None,
254 None,
255 prj_path,
256 None,
257 );
258 let history = History::default();
259 let im_orig_old = world.data.clone();
260 let event = Events::default().mousepos_orig(mouse_pos);
261 let (res, _) = z.mouse_pressed(&event, world, history);
262 assert_eq!(res.data, im_orig_old);
263 assert_eq!(z.mouse_pressed_start_pos, mouse_pos);
264 Ok(())
265}
266
267#[test]
268fn test_on_mouse_released() -> RvResult<()> {
269 let im_orig = DynamicImage::ImageRgb8(ViewImage::new(250, 500));
270 let mut z = Zoom::new();
271 let prj_path = Path::new("");
272 let world = World::from_real_im(
273 im_orig,
274 ExtraIms::default(),
275 ToolsDataMap::new(),
276 None,
277 None,
278 prj_path,
279 None,
280 );
281
282 z.set_mouse_start_zoom((30.0, 70.0).into());
283
284 let world = z.mouse_released_left_btn(world, Some((40.0, 80.0).into()));
285 assert_eq!(
286 *world.zoom_box(),
287 Some(BbF {
288 x: 30.0,
289 y: 70.0,
290 w: 10.0,
291 h: 10.0
292 })
293 );
294 assert_eq!(z.mouse_pressed_start_pos, None);
295 Ok(())
296}
297
298#[test]
299fn test_on_mouse_held() {}