1use std::fmt::Debug;
2
3use rvimage_domain::{BbF, BbI, PtF, ShapeF, ShapeI};
4
5use crate::{
6 events::{Events, KeyCode, ZoomAmount},
7 history::History,
8 make_tool_transform,
9 tools::core::Manipulate,
10 world::World,
11};
12
13use super::{core::Mover, zoom::move_zoom_box};
14
15fn event_move_zoom_box(events: &Events) -> bool {
16 events.held_ctrl() && (events.pressed(KeyCode::MouseLeft) || events.held(KeyCode::MouseLeft))
17}
18fn zoom_box_mouse_wheel(
19 zoom_box: Option<BbF>,
20 shape_orig: ShapeI,
21 ui_image_rect: Option<ShapeF>,
22 amount: ZoomAmount,
23 mouse_pos: Option<PtF>,
24) -> BbF {
25 let current_zb = if let Some(zb) = zoom_box {
26 zb
27 } else {
28 BbI::from_arr(&[0, 0, shape_orig.w, shape_orig.h]).into()
29 };
30 let clip_val = 1.0;
31 let factor = match amount {
32 ZoomAmount::Delta(y_delta) => {
33 let y_delta_clipped = if y_delta > 0.0 {
34 y_delta.min(clip_val)
35 } else {
36 y_delta.max(-clip_val)
37 };
38 1.0 - y_delta_clipped * 0.1
39 }
40 ZoomAmount::Factor(factor) => factor,
41 };
42 let vis_im_shape = current_zb.shape();
43 let ar = |s: ShapeF| s.h / s.w;
44 let (x_factor, y_factor) = match ui_image_rect {
45 Some(uir) => {
46 let diff2off = |ar_diff: f64| (ar_diff / 5.0).abs().min(0.1);
47 let ar_diff = ar(uir) - ar(vis_im_shape);
48 let ar_diff_tolerance = 0.0001;
49 let (xoff, yoff) = if ar_diff > ar_diff_tolerance {
50 let off = diff2off(ar_diff);
51 (off, -off)
52 } else if ar_diff < -ar_diff_tolerance {
53 let off = diff2off(ar_diff);
54 (-off, off)
55 } else {
56 (0.0, 0.0)
57 };
58 if factor < 1.0 {
59 ((factor - xoff).max(0.02), (factor - yoff).max(0.02))
60 } else {
61 (factor, factor)
62 }
63 }
64 None => (factor, factor),
65 };
66 current_zb.center_scale(x_factor, y_factor, shape_orig, mouse_pos)
67}
68
69#[derive(Clone, Debug)]
70pub struct AlwaysActiveZoom {
71 mover: Mover,
72}
73impl AlwaysActiveZoom {
74 fn mouse_pressed(
75 &mut self,
76 events: &Events,
77 world: World,
78 history: History,
79 ) -> (World, History) {
80 if event_move_zoom_box(events) {
81 self.mover.move_mouse_pressed(events.mouse_pos_on_view);
82 }
83 (world, history)
84 }
85
86 fn mouse_held(
87 &mut self,
88 events: &Events,
89 mut world: World,
90 history: History,
91 ) -> (World, History) {
92 if event_move_zoom_box(events) {
93 (self.mover, world) = move_zoom_box(self.mover, world, events.mouse_pos_on_view);
94 (world, history)
95 } else {
96 (world, history)
97 }
98 }
99
100 fn key_released(
101 &mut self,
102 events: &Events,
103 mut world: World,
104 history: History,
105 ) -> (World, History) {
106 if events.held_ctrl() {
107 let zb = if events.released(KeyCode::Key0) {
108 None
109 } else if events.released(KeyCode::PlusEquals) {
110 Some(zoom_box_mouse_wheel(
111 *world.zoom_box(),
112 world.shape_orig(),
113 world.ui_image_rect(),
114 ZoomAmount::Delta(1.0),
115 None,
116 ))
117 } else if events.released(KeyCode::Minus) {
118 Some(zoom_box_mouse_wheel(
119 *world.zoom_box(),
120 world.shape_orig(),
121 world.ui_image_rect(),
122 ZoomAmount::Delta(-1.0),
123 None,
124 ))
125 } else {
126 *world.zoom_box()
127 };
128 world.set_zoom_box(zb);
129 }
130 (world, history)
131 }
132}
133impl Manipulate for AlwaysActiveZoom {
134 fn new() -> AlwaysActiveZoom {
135 AlwaysActiveZoom {
136 mover: Mover::new(),
137 }
138 }
139 fn has_been_used(&self, events: &Events) -> Option<bool> {
140 let zoomed = events.zoom().is_some()
141 || events.held_ctrl()
142 && (events.released(KeyCode::Key0)
143 || events.released(KeyCode::PlusEquals)
144 || events.released(KeyCode::Minus));
145 Some(zoomed || event_move_zoom_box(events))
146 }
147 fn events_tf(
148 &mut self,
149 mut world: World,
150 history: History,
151 events: &Events,
152 ) -> (World, History) {
153 let zoom_factor = events.zoom();
154 if let Some(z) = zoom_factor {
155 let zb = zoom_box_mouse_wheel(
156 *world.zoom_box(),
157 world.shape_orig(),
158 world.ui_image_rect(),
159 z,
160 events.mouse_pos_on_orig,
161 );
162 world.set_zoom_box(Some(zb));
163 }
164 make_tool_transform!(
165 self,
166 world,
167 history,
168 events,
169 [
170 (pressed, KeyCode::MouseLeft, mouse_pressed),
171 (held, KeyCode::MouseLeft, mouse_held),
172 (released, KeyCode::Key0, key_released),
173 (released, KeyCode::PlusEquals, key_released), (released, KeyCode::Minus, key_released)
175 ]
176 )
177 }
178}
179
180#[test]
181fn test_zb() {
182 fn test(zb: Option<BbF>, y_delta: f64, reference_coords: &[u32; 4]) {
183 println!("y_delta {}", y_delta);
184 let shape = ShapeI::new(200, 100);
185 let zb_new = zoom_box_mouse_wheel(zb, shape, None, ZoomAmount::Delta(y_delta), None);
186 assert_eq!(zb_new, BbI::from_arr(reference_coords).into());
187 }
188 test(None, 1.0, &[10, 5, 180, 90]);
189 test(None, -1.0, &[0, 0, 200, 100]);
190}