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