1use crate::drawme::{Annotation, UpdateImage, UpdateTmpAnno};
2use crate::meta_data::MetaData;
3use crate::result::trace_ok_err;
4use crate::tools::{add_tools_initial_data, get_visible_inactive_names};
5use crate::tools_data::annotations::{ClipboardData, InstanceAnnotations};
6use crate::tools_data::{
7 self, vis_from_lfoption, AccessInstanceData, LabelInfo, ToolSpecifics, ToolsData, ToolsDataMap,
8};
9use crate::types::ViewImage;
10use crate::util::Visibility;
11use crate::{image_util, InstanceAnnotate, UpdatePermAnnos, UpdateView};
12use image::DynamicImage;
13use rvimage_domain::{BbF, RvError, RvResult, ShapeF, ShapeI};
14use std::path::Path;
15use std::{fmt::Debug, mem};
16
17pub(super) fn get<'a>(
18 world: &'a World,
19 actor: &'static str,
20 error_msg: &'a str,
21) -> RvResult<&'a ToolsData> {
22 world
23 .data
24 .tools_data_map
25 .get(actor)
26 .ok_or_else(|| RvError::new(error_msg))
27}
28pub fn get_specific<T>(
29 f: impl Fn(&ToolSpecifics) -> RvResult<&T>,
30 data: RvResult<&ToolsData>,
31) -> Option<&T> {
32 trace_ok_err(data.map(|d| &d.specifics).and_then(f))
33}
34pub(super) fn get_mut<'a>(
35 world: &'a mut World,
36 actor: &'static str,
37 error_msg: &'a str,
38) -> RvResult<&'a mut ToolsData> {
39 world
40 .data
41 .tools_data_map
42 .get_mut(actor)
43 .ok_or_else(|| RvError::new(error_msg))
44}
45pub fn get_specific_mut<T>(
46 f_data_access: impl FnMut(&mut ToolSpecifics) -> RvResult<&mut T>,
47 data: RvResult<&mut ToolsData>,
48) -> Option<&mut T> {
49 trace_ok_err(data.map(|d| &mut d.specifics).and_then(f_data_access))
50}
51
52pub trait MetaDataAccess {
54 fn get_core_options(world: &World) -> Option<&tools_data::Options>;
55 fn get_core_options_mut(world: &mut World) -> Option<&mut tools_data::Options>;
56 fn get_track_changes_str(world: &World) -> Option<&'static str>;
57 fn get_label_info(world: &World) -> Option<&LabelInfo>;
58 fn get_label_info_mut(world: &mut World) -> Option<&mut LabelInfo>;
59}
60
61#[macro_export]
62macro_rules! tools_data_accessors {
63 ($actor_name:expr, $missing_data_msg:expr, $data_module:ident, $data_type:ident, $data_func:ident, $data_func_mut:ident) => {
64 #[allow(unused)]
65 pub(super) fn get_data(
66 world: &World,
67 ) -> rvimage_domain::RvResult<&$crate::tools_data::ToolsData> {
68 $crate::world::get(world, $actor_name, $missing_data_msg)
69 }
70 #[allow(unused)]
71 pub(super) fn get_specific(
72 world: &World,
73 ) -> Option<&$crate::tools_data::$data_module::$data_type> {
74 $crate::world::get_specific($crate::tools_data::$data_func, get_data(world))
75 }
76 pub(super) fn get_data_mut(
77 world: &mut World,
78 ) -> rvimage_domain::RvResult<&mut $crate::tools_data::ToolsData> {
79 $crate::world::get_mut(world, $actor_name, $missing_data_msg)
80 }
81 pub(super) fn get_specific_mut(
82 world: &mut World,
83 ) -> Option<&mut $crate::tools_data::$data_module::$data_type> {
84 $crate::world::get_specific_mut($crate::tools_data::$data_func_mut, get_data_mut(world))
85 }
86 };
87}
88#[macro_export]
89macro_rules! tools_data_accessors_objects {
90 ($actor_name:expr, $missing_data_msg:expr, $data_module:ident, $data_type:ident, $data_func:ident, $data_func_mut:ident) => {
91 pub(super) fn get_options(world: &World) -> Option<&$crate::tools_data::$data_module::Options> {
92 get_specific(world).map(|d| &d.options)
93 }
94 pub fn get_options_mut(world: &mut World) -> Option<&mut $crate::tools_data::$data_module::Options> {
95 get_specific_mut(world).map(|d| &mut d.options)
96 }
97 pub(super) fn get_track_changes_str(world: &World) -> Option<&'static str> {
98 lazy_static::lazy_static! {
99 static ref TRACK_CHANGE_STR: String = $crate::tools::core::make_track_changes_str($actor_name);
100 };
101 let track_changes =
102 get_options(world).map(|o| o.core.track_changes) == Some(true);
103 $crate::util::wrap_if(&TRACK_CHANGE_STR, track_changes)
104 }
105
106 pub(super) fn get_label_info(world: &World) -> Option<&LabelInfo> {
107 get_specific(world).map(|d| &d.label_info)
108 }
109 pub(super) fn get_instance_label_display(world: &World) -> $crate::tools_data::InstanceLabelDisplay {
110 get_options(world).map(|d| d.core.instance_label_display).unwrap_or_default()
111 }
112
113 pub(super) struct DataAccessors;
115 impl $crate::world::MetaDataAccess for DataAccessors {
116 fn get_core_options(world: &World) -> Option<&$crate::tools_data::Options> {
117 get_options(world).map(|o| &o.core)
118 }
119 fn get_core_options_mut(world: &mut World) -> Option<&mut $crate::tools_data::Options> {
120 get_options_mut(world).map(|o| &mut o.core)
121 }
122 fn get_track_changes_str(world: &World) -> Option<&'static str> {
123 get_track_changes_str(world)
124 }
125 fn get_label_info(world: &World) -> Option<&LabelInfo> {
126 get_label_info(world)
127 }
128 fn get_label_info_mut(world: &mut World) -> Option<&mut LabelInfo> {
129 get_specific_mut(world).map(|d| &mut d.label_info)
130 }
131 }
132
133 pub(super) fn get_visible(world: &World) -> Visibility {
134 let visible = get_options(world).map(|o| o.core.visible) == Some(true);
135 vis_from_lfoption(get_label_info(world), visible)
136 }
137 pub(super) fn set_visible(world: &mut World) {
138 let options_mut = get_options_mut(world);
139 if let Some(options_mut) = options_mut {
140 options_mut.core.visible = true;
141 }
142 let vis = get_visible(world);
143 world.request_redraw_annotations($actor_name, vis);
144 }
145 };
146}
147#[macro_export]
148macro_rules! world_annotations_accessor {
149 ($actor_name:expr, $access_func:ident, $error_msg:expr, $annotations_type:ty) => {
150 pub(super) fn get_annos_(
151 world: &World,
152 is_no_anno_fine: bool,
153 ) -> Option<&$annotations_type> {
154 if let Some(current_file_path) = world.data.meta_data.file_path_relative() {
155 let res = $crate::get_annos_from_tdm!(
156 $actor_name,
157 &world.data.tools_data_map,
158 current_file_path,
159 $access_func
160 );
161 if res.is_none() && !is_no_anno_fine {
162 tracing::error!("{}", $error_msg);
163 }
164 res
165 } else {
166 None
167 }
168 }
169 #[allow(unused)]
170 pub(super) fn get_annos(world: &World) -> Option<&$annotations_type> {
171 get_annos_(world, false)
172 }
173 #[allow(unused)]
174 pub(super) fn get_annos_if_some(world: &World) -> Option<&$annotations_type> {
175 get_annos_(world, true)
176 }
177 };
178}
179#[macro_export]
180macro_rules! annotations_accessor_mut {
181 ($actor_name:expr, $access_func:ident, $error_msg:expr, $annotations_type:ty) => {
182 pub(super) fn get_annos_mut_(
183 world: &mut World,
184 is_no_anno_fine: bool,
185 ) -> Option<&mut $annotations_type> {
186 if let Some(current_file_path) = world.data.meta_data.file_path_relative() {
187 let shape_initial = world.shape_orig();
188 let res = world
189 .data
190 .tools_data_map
191 .get_mut($actor_name)
192 .and_then(|x| x.specifics.$access_func().ok())
193 .and_then(|d| d.get_annos_mut(¤t_file_path, shape_initial));
194 if res.is_none() {
195 tracing::error!("{}", $error_msg);
196 }
197 res
198 } else {
199 if !is_no_anno_fine {
200 tracing::error!("could not find filepath in meta data")
201 };
202 None
203 }
204 }
205 pub fn get_annos_mut(world: &mut World) -> Option<&mut $annotations_type> {
206 let is_no_anno_fine = world.data.meta_data.flags.is_file_list_empty == Some(true);
207 get_annos_mut_(world, is_no_anno_fine)
208 }
209 pub fn use_currentimageshape_for_annos(world: &mut World) {
210 get_annos_mut(world);
215 }
216 };
217}
218
219pub trait InstanceAnnoAccess<T>
220where
221 T: InstanceAnnotate,
222{
223 fn get_annos(world: &World) -> Option<&InstanceAnnotations<T>>;
224 fn get_annos_mut(world: &mut World) -> Option<&mut InstanceAnnotations<T>>;
225 fn get_clipboard(world: &World) -> Option<&ClipboardData<T>>;
226 fn set_clipboard(world: &mut World, clipboard: Option<ClipboardData<T>>);
227}
228#[macro_export]
229macro_rules! instance_annotations_accessor {
230 ($annotations_type:ty) => {
231 pub(super) struct InstanceAnnoAccessors;
232 impl $crate::world::InstanceAnnoAccess<$annotations_type> for InstanceAnnoAccessors {
233 fn get_annos(world: &World) -> Option<&$crate::tools_data::annotations::InstanceAnnotations<$annotations_type>> {
234 get_annos(world)
235 }
236 fn get_annos_mut(
237 world: &mut World,
238 ) -> Option<&mut $crate::tools_data::annotations::InstanceAnnotations<$annotations_type>> {
239 get_annos_mut(world)
240 }
241 fn get_clipboard(
242 world: &World,
243 ) -> Option<&$crate::tools_data::annotations::ClipboardData<$annotations_type>> {
244 get_specific(world).and_then(|d| d.clipboard.as_ref())
245 }
246 fn set_clipboard(
247 world: &mut World,
248 clipboard: Option<$crate::tools_data::annotations::ClipboardData<$annotations_type>>,
249 ) {
250 let specific_data = get_specific_mut(world);
251 if let Some(d) = specific_data {
252 d.clipboard = clipboard;
253 }
254 }
255 }
256 };
257}
258
259#[derive(Clone, Default, PartialEq)]
260pub struct DataRaw {
261 im_background: DynamicImage,
262 ui_image_rect: Option<ShapeF>,
263 pub meta_data: MetaData,
264 pub tools_data_map: ToolsDataMap,
265}
266
267impl DataRaw {
268 #[must_use]
269 pub fn new(
270 im_background: DynamicImage,
271 tools_data_map: ToolsDataMap,
272 meta_data: MetaData,
273 ui_image_rect: Option<ShapeF>,
274 ) -> Self {
275 DataRaw {
276 im_background,
277 ui_image_rect,
278 meta_data,
279 tools_data_map,
280 }
281 }
282
283 #[must_use]
284 pub fn im_background(&self) -> &DynamicImage {
285 &self.im_background
286 }
287 pub fn im_background_mut(&mut self) -> &mut DynamicImage {
288 &mut self.im_background
289 }
290
291 pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
292 self.ui_image_rect = ui_image_rect;
293 }
294
295 pub fn apply<FI>(&mut self, mut f_i: FI)
296 where
297 FI: FnMut(DynamicImage) -> DynamicImage,
298 {
299 self.im_background = f_i(mem::take(&mut self.im_background));
300 }
301
302 #[must_use]
303 pub fn shape(&self) -> ShapeI {
304 ShapeI::from_im(&self.im_background)
305 }
306
307 #[must_use]
308 pub fn bg_to_uncropped_view(&self) -> ViewImage {
309 image_util::orig_to_0_255(&self.im_background, &None)
310 }
311}
312
313impl Debug for DataRaw {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 write!(
316 f,
317 "\nshape {:?}\ntools data {:?}",
318 self.shape(),
319 self.tools_data_map,
320 )
321 }
322}
323
324fn evaluate_visibility(
325 visibility: Visibility,
326 tool_name: &str,
327 data: &DataRaw,
328) -> Option<Vec<Annotation>> {
329 match (
330 visibility,
331 &data.meta_data.file_path_relative(),
332 data.tools_data_map.get(tool_name),
333 ) {
334 (Visibility::All, Some(file_path_relative), Some(td)) => {
335 td.specifics.to_annotations_view(file_path_relative, None)
336 }
337 (Visibility::Only(idx), Some(file_path), Some(td)) => {
338 td.specifics.to_annotations_view(file_path, Some(idx))
339 }
340 (Visibility::None, _, _) => Some(vec![]),
341 _ => None,
342 }
343}
344#[derive(Clone, Default)]
346pub struct World {
347 pub update_view: UpdateView,
348 pub data: DataRaw,
349 zoom_box: Option<BbF>,
351}
352
353impl World {
354 #[must_use]
355 pub fn new(ims_raw: DataRaw, zoom_box: Option<BbF>) -> Self {
356 let update_view = UpdateView::new(&ims_raw, zoom_box);
357 let mut world = Self {
358 data: ims_raw,
359 zoom_box,
360 update_view,
361 };
362 world.data.tools_data_map =
363 add_tools_initial_data(mem::take(&mut world.data.tools_data_map));
364 world
365 }
366 pub fn reset_updateview(&mut self) {
367 self.update_view = UpdateView::new(&self.data, self.zoom_box);
368 }
369
370 #[must_use]
371 pub fn ui_image_rect(&self) -> Option<ShapeF> {
372 self.data.ui_image_rect
373 }
374
375 pub fn request_redraw_annotations(&mut self, tool_name: &str, visibility_active: Visibility) {
380 let visible_inactive_tools = self
381 .data
382 .tools_data_map
383 .get(tool_name)
384 .map(|td| td.visible_inactive_tools.clone());
385 let tool_names_inactive = get_visible_inactive_names(tool_name);
386 let mut annos_inactive: Option<Vec<Annotation>> = None;
387 if let Some(visible_inactive_tools) = visible_inactive_tools {
388 for (tool_name_inactive, show) in tool_names_inactive
389 .iter()
390 .zip(visible_inactive_tools.iter())
391 {
392 let vli = self.data.tools_data_map.get(tool_name_inactive).map(|td| {
393 match &td.specifics {
394 tools_data::ToolSpecifics::Bbox(bbox_data) => {
395 (bbox_data.options.core.visible, bbox_data.label_info())
396 }
397 tools_data::ToolSpecifics::Brush(brush_data) => {
398 (brush_data.options.core.visible, brush_data.label_info())
399 }
400 _ => {
401 panic!("tool {tool_name_inactive} does not redraw annotations ");
402 }
403 }
404 });
405 let visibility_inactive = if let Some((visible, label_info)) = vli {
406 vis_from_lfoption(Some(label_info), visible)
407 } else {
408 Visibility::All
409 };
410 if show && visibility_active != Visibility::None {
411 if let Some(annos) = &mut annos_inactive {
412 let annos_inner = evaluate_visibility(
413 visibility_inactive,
414 tool_name_inactive,
415 &self.data,
416 );
417 if let Some(annos_inner) = annos_inner {
418 annos.extend(annos_inner);
419 }
420 } else {
421 annos_inactive = evaluate_visibility(
422 visibility_inactive,
423 tool_name_inactive,
424 &self.data,
425 );
426 }
427 }
428 }
429 }
430 let annos_active = evaluate_visibility(visibility_active, tool_name, &self.data);
431 if let Some(annos_active) = annos_active {
432 if let Some(annos_inactive) = annos_inactive {
433 let mut annos = annos_active;
434 annos.extend(annos_inactive);
435 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos);
436 } else {
437 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_active);
438 }
439 } else if let Some(annos_inactive) = annos_inactive {
440 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_inactive);
441 }
442 }
443
444 pub fn request_redraw_tmp_anno(&mut self, anno: Annotation) {
445 self.update_view.tmp_annos = UpdateTmpAnno::Yes(anno);
446 }
447
448 pub fn stop_tmp_anno(&mut self) {
449 self.update_view.tmp_annos = UpdateTmpAnno::No;
450 }
451
452 pub fn request_redraw_image(&mut self) {
453 if self.data.meta_data.file_path_relative().is_some() {
454 self.update_view.image = UpdateImage::Yes(self.data.bg_to_uncropped_view());
455 }
456 }
457
458 #[must_use]
460 pub fn from_real_im(
461 im: DynamicImage,
462 tools_data: ToolsDataMap,
463 ui_image_rect: Option<ShapeF>,
464 file_path: Option<String>,
465 prj_path: &Path,
466 file_selected_idx: Option<usize>,
467 ) -> Self {
468 let meta_data = match (file_path, file_selected_idx) {
469 (Some(fp), Some(fsidx)) => MetaData::from_filepath(fp, fsidx, prj_path),
470 _ => MetaData::default(),
471 };
472 Self::new(DataRaw::new(im, tools_data, meta_data, ui_image_rect), None)
473 }
474
475 #[must_use]
476 pub fn shape_orig(&self) -> ShapeI {
477 self.data.shape()
478 }
479
480 pub fn set_zoom_box(&mut self, zoom_box: Option<BbF>) {
481 let mut set_zb = || {
482 let zoom_box =
483 zoom_box.map(|zb| BbF::new_fit_to_image(zb.x, zb.y, zb.w, zb.h, self.shape_orig()));
484 self.zoom_box = zoom_box;
485 self.update_view = UpdateView::from_zoombox(zoom_box);
486 };
487 if let Some(zb) = zoom_box {
488 if zb.h > 1.0 && zb.w > 1.0 {
489 set_zb();
490 }
491 } else {
492 set_zb();
493 }
494 }
495
496 #[must_use]
497 pub fn zoom_box(&self) -> &Option<BbF> {
498 &self.zoom_box
499 }
500
501 pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
502 self.data.set_image_rect(ui_image_rect);
503 }
504 pub fn set_background_image(&mut self, image: DynamicImage) {
505 if ShapeI::from_im(&image) != self.shape_orig() {
506 self.zoom_box = None;
507 }
508 self.data.im_background = image;
509 }
510}
511impl Debug for World {
512 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513 write!(f, "\nims_raw {:?}", &self.data)
514 }
515}
516
517#[cfg(test)]
518fn rgba_at(i: usize, im: &ViewImage) -> [u8; 4] {
519 let x = (i % im.width() as usize) as u32;
520 let y = (i / im.width() as usize) as u32;
521 let rgb = im.get_pixel(x, y).0;
522 let rgb_changed = rgb;
523 [rgb_changed[0], rgb_changed[1], rgb_changed[2], 0xff]
524}
525#[cfg(test)]
526use image::Rgb;
527
528#[test]
529fn test_rgba() {
530 let mut im_test = ViewImage::new(64, 64);
531 im_test.put_pixel(0, 0, Rgb([23, 23, 23]));
532 assert_eq!(rgba_at(0, &im_test), [23, 23, 23, 255]);
533 im_test.put_pixel(0, 1, Rgb([23, 23, 23]));
534 assert_eq!(rgba_at(64, &im_test), [23, 23, 23, 255]);
535 im_test.put_pixel(7, 11, Rgb([23, 23, 23]));
536 assert_eq!(rgba_at(11 * 64 + 7, &im_test), [23, 23, 23, 255]);
537}