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