rvlib/
world.rs

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, ExportAsCoco, LabelInfo, ToolSpecifics, ToolsData, ToolsDataMap,
8};
9use crate::types::ViewImage;
10use crate::util::Visibility;
11use crate::{image_util, InstanceAnnotate, UpdatePermAnnos, UpdateView, UpdateZoomBox};
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
52/// Often needed meta data when accessing annotations, see different `AnnoMetaAccessors` structs.
53pub 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(world: &World) -> Option<&$data_module::$data_type> {
72            $crate::world::get_specific(tools_data::$data_func, get_data(world))
73        }
74        pub(super) fn get_data_mut(
75            world: &mut World,
76        ) -> rvimage_domain::RvResult<&mut $crate::tools_data::ToolsData> {
77            $crate::world::get_mut(world, $actor_name, $missing_data_msg)
78        }
79        pub(super) fn get_specific_mut(world: &mut World) -> Option<&mut $data_module::$data_type> {
80            $crate::world::get_specific_mut(tools_data::$data_func_mut, get_data_mut(world))
81        }
82    };
83}
84#[macro_export]
85macro_rules! tools_data_accessors_objects {
86    ($actor_name:expr, $missing_data_msg:expr, $data_module:ident, $data_type:ident, $data_func:ident, $data_func_mut:ident) => {
87        pub(super) fn get_options(world: &World) -> Option<&$data_module::Options> {
88            get_specific(world).map(|d| &d.options)
89        }
90        pub(super) fn get_options_mut(world: &mut World) -> Option<&mut $data_module::Options> {
91            get_specific_mut(world).map(|d| &mut d.options)
92        }
93        pub(super) fn get_track_changes_str(world: &World) -> Option<&'static str> {
94            lazy_static::lazy_static! {
95                static ref TRACK_CHANGE_STR: String = $crate::tools::core::make_track_changes_str(ACTOR_NAME);
96            };
97            let track_changes =
98                get_options(world).map(|o| o.core.track_changes) == Some(true);
99            $crate::util::wrap_if(&TRACK_CHANGE_STR, track_changes)
100        }
101
102        pub(super) fn get_label_info(world: &World) -> Option<&LabelInfo> {
103            get_specific(world).map(|d| &d.label_info)
104        }
105        pub(super) fn get_instance_label_display(world: &World) -> $crate::tools_data::InstanceLabelDisplay {
106            get_options(world).map(|d| d.core.instance_label_display).unwrap_or_default()
107        }
108
109        /// when you access annotations, you often also need this metadata
110        pub(super) struct DataAccessors;
111        impl $crate::world::MetaDataAccess for DataAccessors {
112            fn get_core_options(world: &World) -> Option<&$crate::tools_data::Options> {
113                get_options(world).map(|o| &o.core)
114            }
115            fn get_core_options_mut(world: &mut World) -> Option<&mut $crate::tools_data::Options> {
116                get_options_mut(world).map(|o| &mut o.core)
117            }
118            fn get_track_changes_str(world: &World) -> Option<&'static str> {
119                get_track_changes_str(world)
120            }
121            fn get_label_info(world: &World) -> Option<&LabelInfo> {
122                get_label_info(world)
123            }
124            fn get_label_info_mut(world: &mut World) -> Option<&mut LabelInfo> {
125                get_specific_mut(world).map(|d| &mut d.label_info)
126            }
127        }
128
129        pub(super) fn get_visible(world: &World) -> Visibility {
130            let visible = get_options(world).map(|o| o.core.visible) == Some(true);
131            vis_from_lfoption(get_label_info(world), visible)
132        }
133        pub(super) fn set_visible(world: &mut World) {
134            let options_mut = get_options_mut(world);
135            if let Some(options_mut) = options_mut {
136                options_mut.core.visible = true;
137            }
138            let vis = get_visible(world);
139            world.request_redraw_annotations($actor_name, vis);
140        }
141    };
142}
143#[macro_export]
144macro_rules! world_annotations_accessor {
145    ($actor_name:expr, $access_func:ident, $error_msg:expr, $annotations_type:ty) => {
146        pub(super) fn get_annos_(
147            world: &World,
148            is_no_anno_fine: bool,
149        ) -> Option<&$annotations_type> {
150            if let Some(current_file_path) = world.data.meta_data.file_path_relative() {
151                let res = $crate::get_annos_from_tdm!(
152                    $actor_name,
153                    &world.data.tools_data_map,
154                    current_file_path,
155                    $access_func
156                );
157                if res.is_none() && !is_no_anno_fine {
158                    tracing::error!("{}", $error_msg);
159                }
160                res
161            } else {
162                None
163            }
164        }
165        #[allow(unused)]
166        pub(super) fn get_annos(world: &World) -> Option<&$annotations_type> {
167            get_annos_(world, false)
168        }
169        #[allow(unused)]
170        pub(super) fn get_annos_if_some(world: &World) -> Option<&$annotations_type> {
171            get_annos_(world, true)
172        }
173    };
174}
175#[macro_export]
176macro_rules! annotations_accessor_mut {
177    ($actor_name:expr, $access_func:ident, $error_msg:expr, $annotations_type:ty) => {
178        pub(super) fn get_annos_mut_(
179            world: &mut World,
180            is_no_anno_fine: bool,
181        ) -> Option<&mut $annotations_type> {
182            if let Some(current_file_path) = world.data.meta_data.file_path_relative() {
183                let shape_initial = *world.data.shape_initial();
184                let res = world
185                    .data
186                    .tools_data_map
187                    .get_mut($actor_name)
188                    .and_then(|x| x.specifics.$access_func().ok())
189                    .and_then(|d| d.get_annos_mut(&current_file_path, shape_initial));
190                if res.is_none() {
191                    tracing::error!("{}", $error_msg);
192                }
193                res
194            } else {
195                if !is_no_anno_fine {
196                    tracing::error!("could not find filepath in meta data")
197                };
198                None
199            }
200        }
201        pub(super) fn get_annos_mut(world: &mut World) -> Option<&mut $annotations_type> {
202            let is_no_anno_fine = world.data.meta_data.flags.is_file_list_empty == Some(true);
203            get_annos_mut_(world, is_no_anno_fine)
204        }
205    };
206}
207
208pub trait InstanceAnnoAccess<T>
209where
210    T: InstanceAnnotate,
211{
212    fn get_annos(world: &World) -> Option<&InstanceAnnotations<T>>;
213    fn get_annos_mut(world: &mut World) -> Option<&mut InstanceAnnotations<T>>;
214    fn get_clipboard(world: &World) -> Option<&ClipboardData<T>>;
215    fn set_clipboard(world: &mut World, clipboard: Option<ClipboardData<T>>);
216}
217#[macro_export]
218macro_rules! instance_annotations_accessor {
219    ($annotations_type:ty) => {
220        pub(super) struct InstanceAnnoAccessors;
221        impl $crate::world::InstanceAnnoAccess<$annotations_type> for InstanceAnnoAccessors {
222            fn get_annos(world: &World) -> Option<&$crate::tools_data::annotations::InstanceAnnotations<$annotations_type>> {
223                get_annos(world)
224            }
225            fn get_annos_mut(
226                world: &mut World,
227            ) -> Option<&mut $crate::tools_data::annotations::InstanceAnnotations<$annotations_type>> {
228                get_annos_mut(world)
229            }
230            fn get_clipboard(
231                world: &World,
232            ) -> Option<&$crate::tools_data::annotations::ClipboardData<$annotations_type>> {
233                get_specific(world).and_then(|d| d.clipboard.as_ref())
234            }
235            fn set_clipboard(
236                world: &mut World,
237                clipboard: Option<$crate::tools_data::annotations::ClipboardData<$annotations_type>>,
238            ) {
239                let specific_data = get_specific_mut(world);
240                if let Some(d) = specific_data {
241                    d.clipboard = clipboard;
242                }
243            }
244        }
245    };
246}
247
248#[derive(Clone, Default, PartialEq)]
249pub struct DataRaw {
250    im_background: DynamicImage,
251    shape_initial: ShapeI,
252    ui_image_rect: Option<ShapeF>,
253    pub meta_data: MetaData,
254    pub tools_data_map: ToolsDataMap,
255}
256
257impl DataRaw {
258    #[must_use]
259    pub fn new(
260        im_background: DynamicImage,
261        tools_data_map: ToolsDataMap,
262        meta_data: MetaData,
263        ui_image_rect: Option<ShapeF>,
264    ) -> Self {
265        let shape_initial = ShapeI::from_im(&im_background);
266        DataRaw {
267            im_background,
268            shape_initial,
269            ui_image_rect,
270            meta_data,
271            tools_data_map,
272        }
273    }
274
275    #[must_use]
276    pub fn im_background(&self) -> &DynamicImage {
277        &self.im_background
278    }
279
280    pub fn take_background(&mut self) -> DynamicImage {
281        mem::take(&mut self.im_background)
282    }
283
284    #[must_use]
285    pub fn shape_initial(&self) -> &ShapeI {
286        &self.shape_initial
287    }
288
289    pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
290        self.ui_image_rect = ui_image_rect;
291    }
292
293    pub fn apply<FI>(&mut self, mut f_i: FI)
294    where
295        FI: FnMut(DynamicImage) -> DynamicImage,
296    {
297        self.im_background = f_i(mem::take(&mut self.im_background));
298    }
299
300    #[must_use]
301    pub fn shape(&self) -> ShapeI {
302        ShapeI::from_im(&self.im_background)
303    }
304
305    #[must_use]
306    pub fn bg_to_uncropped_view(&self) -> ViewImage {
307        image_util::orig_to_0_255(&self.im_background, &None)
308    }
309}
310
311impl Debug for DataRaw {
312    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313        write!(
314            f,
315            "\nshape {:?}\ntools data {:?}",
316            self.shape(),
317            self.tools_data_map,
318        )
319    }
320}
321
322fn evaluate_visibility(
323    visibility: Visibility,
324    tool_name: &str,
325    data: &DataRaw,
326) -> Option<Vec<Annotation>> {
327    match (
328        visibility,
329        &data.meta_data.file_path_relative(),
330        data.tools_data_map.get(tool_name),
331    ) {
332        (Visibility::All, Some(file_path_relative), Some(td)) => {
333            td.specifics.to_annotations_view(file_path_relative, None)
334        }
335        (Visibility::Only(idx), Some(file_path), Some(td)) => {
336            td.specifics.to_annotations_view(file_path, Some(idx))
337        }
338        (Visibility::None, _, _) => Some(vec![]),
339        _ => None,
340    }
341}
342/// Everything we need to draw
343#[derive(Clone, Default)]
344pub struct World {
345    pub update_view: UpdateView,
346    pub data: DataRaw,
347    // transforms coordinates from view to raw image
348    zoom_box: Option<BbF>,
349}
350
351impl World {
352    #[must_use]
353    pub fn new(ims_raw: DataRaw, zoom_box: Option<BbF>) -> Self {
354        let im = ims_raw.bg_to_uncropped_view();
355        let world = Self {
356            data: ims_raw,
357            zoom_box,
358            update_view: UpdateView {
359                image: UpdateImage::Yes(im),
360                perm_annos: UpdatePermAnnos::No,
361                tmp_annos: UpdateTmpAnno::No,
362                zoom_box: UpdateZoomBox::Yes(zoom_box),
363                image_info: None,
364                tmp_anno_buffer: None,
365            },
366        };
367        add_tools_initial_data(world)
368    }
369
370    #[must_use]
371    pub fn ui_image_rect(&self) -> Option<ShapeF> {
372        self.data.ui_image_rect
373    }
374
375    /// Annotations shall be drawn again
376    ///
377    /// # Panics
378    /// Panics if a tool name is passed that does not have annotations to be redrawn.
379    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    /// real image in contrast to the loading image
459    #[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}
505impl Debug for World {
506    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507        write!(f, "\nims_raw {:?}", &self.data)
508    }
509}
510
511#[cfg(test)]
512fn rgba_at(i: usize, im: &ViewImage) -> [u8; 4] {
513    let x = (i % im.width() as usize) as u32;
514    let y = (i / im.width() as usize) as u32;
515    let rgb = im.get_pixel(x, y).0;
516    let rgb_changed = rgb;
517    [rgb_changed[0], rgb_changed[1], rgb_changed[2], 0xff]
518}
519#[cfg(test)]
520use image::Rgb;
521
522#[test]
523fn test_rgba() {
524    let mut im_test = ViewImage::new(64, 64);
525    im_test.put_pixel(0, 0, Rgb([23, 23, 23]));
526    assert_eq!(rgba_at(0, &im_test), [23, 23, 23, 255]);
527    im_test.put_pixel(0, 1, Rgb([23, 23, 23]));
528    assert_eq!(rgba_at(64, &im_test), [23, 23, 23, 255]);
529    im_test.put_pixel(7, 11, Rgb([23, 23, 23]));
530    assert_eq!(rgba_at(11 * 64 + 7, &im_test), [23, 23, 23, 255]);
531}