1use std::fmt::Debug;
2
3use crate::{
4 drawme::{Annotation, BboxAnnotation, Stroke},
5 events::{Events, KeyCode},
6 history::History,
7 make_tool_transform,
8 tools::core::Manipulate,
9 tools_data::InstanceLabelDisplay,
10 types::ViewImage,
11 world::World,
12 GeoFig,
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 if let (Some(mps), Some(m)) = (self.mouse_pressed_start_pos, events.mouse_pos_on_orig) {
141 let bb = BbF::from_points(mps, m);
143 let white = [255, 255, 255];
144 let anno = BboxAnnotation {
145 geofig: GeoFig::BB(bb),
146 fill_color: None,
147 fill_alpha: 0,
148 outline: Stroke::from_color(white),
149 outline_alpha: 255,
150 label: None,
151 is_selected: None,
152 highlight_circles: vec![],
153 instance_label_display: InstanceLabelDisplay::None,
154 };
155 world.request_redraw_tmp_anno(Annotation::Bbox(anno));
156 }
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, image::DynamicImage, rvimage_domain::RvResult, std::path::Path,
200};
201#[cfg(test)]
202fn mk_z(x: TPtF, y: TPtF, w: TPtF, h: TPtF) -> Option<BbF> {
203 Some(BbF { x, y, w, h })
204}
205#[test]
206fn test_make_zoom() -> RvResult<()> {
207 fn test(mps: (TPtF, TPtF), mpr: (TPtF, TPtF), expected: Option<BbF>) {
208 assert_eq!(make_zoom_on_release(mps, mpr), expected);
209 }
210
211 test((0.0, 0.0), (10.0, 10.0), mk_z(0.0, 0.0, 10.0, 10.0));
212 test((0.0, 0.0), (100.0, 10.0), mk_z(0.0, 0.0, 100.0, 10.0));
213 test((13.0, 7.0), (33.0, 17.0), mk_z(13.0, 7.0, 20.0, 10.0));
214 test((5.0, 9.0), (6.0, 9.0), None);
215 test((5.0, 9.0), (17.0, 19.0), mk_z(5.0, 9.0, 12.0, 10.0));
216
217 Ok(())
218}
219#[test]
220fn test_move_zoom() -> RvResult<()> {
221 fn test(
222 mpp: (usize, usize),
223 mph: (usize, usize),
224 zoom_box: Option<BbF>,
225 expected: Option<BbF>,
226 ) {
227 let mpp = (mpp.0 as TPtF, mpp.1 as TPtF).into();
228 let mph = (mph.0 as TPtF, mph.1 as TPtF).into();
229 let shape_orig = ShapeI { w: 80, h: 80 };
230 assert_eq!(follow_zoom_box(mpp, mph, shape_orig, zoom_box), expected);
231 }
232 test(
233 (4, 4),
234 (12, 8),
235 mk_z(12.0, 16.0, 40.0, 40.0),
236 mk_z(4.0, 12.0, 40.0, 40.0),
237 );
238 Ok(())
239}
240#[test]
241fn test_on_mouse_pressed() -> RvResult<()> {
242 let mouse_pos = Some((30.0, 45.0).into());
243 let im_orig = DynamicImage::ImageRgb8(ViewImage::new(250, 500));
244 let mut z = Zoom::new();
245 let prj_path = Path::new("");
246 let world = World::from_real_im(im_orig, ToolsDataMap::new(), None, None, prj_path, None);
247 let history = History::default();
248 let im_orig_old = world.data.clone();
249 let event = Events::default().mousepos_orig(mouse_pos);
250 let (res, _) = z.mouse_pressed(&event, world, history);
251 assert_eq!(res.data, im_orig_old);
252 assert_eq!(z.mouse_pressed_start_pos, mouse_pos);
253 Ok(())
254}
255
256#[test]
257fn test_on_mouse_released() -> RvResult<()> {
258 let im_orig = DynamicImage::ImageRgb8(ViewImage::new(250, 500));
259 let mut z = Zoom::new();
260 let prj_path = Path::new("");
261 let world = World::from_real_im(im_orig, ToolsDataMap::new(), None, None, prj_path, None);
262
263 z.set_mouse_start_zoom((30.0, 70.0).into());
264
265 let world = z.mouse_released_left_btn(world, Some((40.0, 80.0).into()));
266 assert_eq!(
267 *world.zoom_box(),
268 Some(BbF {
269 x: 30.0,
270 y: 70.0,
271 w: 10.0,
272 h: 10.0
273 })
274 );
275 assert_eq!(z.mouse_pressed_start_pos, None);
276 Ok(())
277}
278
279#[test]
280fn test_on_mouse_held() {}