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, AccessInstanceData, LabelInfo, ToolSpecifics, ToolsData, ToolsDataMap, vis_from_lfoption,
9};
10use crate::types::{ExtraIms, ExtraViews, ViewImage};
11use crate::util::Visibility;
12use crate::{InstanceAnnotate, UpdatePermAnnos, UpdateView, image_util};
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    extra_ims: ExtraIms,
272    ui_image_rect: Option<ShapeF>,
273    pub meta_data: MetaData,
274    pub tools_data_map: ToolsDataMap,
275}
276
277impl DataRaw {
278    #[must_use]
279    pub fn new(
280        im_background: DynamicImage,
281        extra_ims: ExtraIms,
282        tools_data_map: ToolsDataMap,
283        meta_data: MetaData,
284        ui_image_rect: Option<ShapeF>,
285    ) -> Self {
286        DataRaw {
287            im_background,
288            extra_ims,
289            ui_image_rect,
290            meta_data,
291            tools_data_map,
292        }
293    }
294
295    #[must_use]
296    pub fn im_background(&self) -> &DynamicImage {
297        &self.im_background
298    }
299    pub fn im_background_mut(&mut self) -> &mut DynamicImage {
300        &mut self.im_background
301    }
302
303    pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
304        self.ui_image_rect = ui_image_rect;
305    }
306
307    pub fn apply<FI>(&mut self, mut f_i: FI)
308    where
309        FI: FnMut(DynamicImage) -> DynamicImage,
310    {
311        self.im_background = f_i(mem::take(&mut self.im_background));
312    }
313
314    #[must_use]
315    pub fn shape(&self) -> ShapeI {
316        ShapeI::from_im(&self.im_background)
317    }
318
319    #[must_use]
320    pub fn bg_to_uncropped_view(&self) -> ViewImage {
321        image_util::orig_to_0_255(&self.im_background, &None)
322    }
323    pub fn extra_im_to_extra_views(&self) -> ExtraViews {
324        ExtraViews {
325            prev_ims: self
326                .extra_ims
327                .prev_ims
328                .iter()
329                .map(|im| image_util::orig_to_0_255(im, &None))
330                .collect(),
331            next_ims: self
332                .extra_ims
333                .next_ims
334                .iter()
335                .map(|im| image_util::orig_to_0_255(im, &None))
336                .collect(),
337        }
338    }
339}
340
341impl Debug for DataRaw {
342    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
343        write!(
344            f,
345            "\nshape {:?}\ntools data {:?}",
346            self.shape(),
347            self.tools_data_map,
348        )
349    }
350}
351
352fn evaluate_visibility(
353    visibility: Visibility,
354    tool_name: &str,
355    data: &DataRaw,
356) -> Option<Vec<Annotation>> {
357    match (
358        visibility,
359        &data.meta_data.file_path_relative(),
360        data.tools_data_map.get(tool_name),
361    ) {
362        (Visibility::All, Some(file_path_relative), Some(td)) => {
363            td.specifics.to_annotations_view(file_path_relative, None)
364        }
365        (Visibility::Only(idx), Some(file_path), Some(td)) => {
366            td.specifics.to_annotations_view(file_path, Some(idx))
367        }
368        (Visibility::None, _, _) => Some(vec![]),
369        _ => None,
370    }
371}
372/// Everything we need to draw
373#[derive(Clone, Default)]
374pub struct World {
375    pub update_view: UpdateView,
376    pub data: DataRaw,
377    // transforms coordinates from view to raw image
378    zoom_box: Option<BbF>,
379}
380
381impl World {
382    #[must_use]
383    pub fn new(ims_raw: DataRaw, zoom_box: Option<BbF>) -> Self {
384        let update_view = UpdateView::new(&ims_raw, zoom_box);
385        let mut world = Self {
386            data: ims_raw,
387            zoom_box,
388            update_view,
389        };
390        world.data.tools_data_map =
391            add_tools_initial_data(mem::take(&mut world.data.tools_data_map));
392        world
393    }
394    pub fn reset_updateview(&mut self) {
395        self.update_view = UpdateView::new(&self.data, self.zoom_box);
396    }
397
398    #[must_use]
399    pub fn ui_image_rect(&self) -> Option<ShapeF> {
400        self.data.ui_image_rect
401    }
402
403    /// Annotations shall be drawn again
404    ///
405    /// # Panics
406    /// Panics if a tool name is passed that does not have annotations to be redrawn.
407    pub fn request_redraw_annotations(&mut self, tool_name: &str, visibility_active: Visibility) {
408        let visible_inactive_tools = self
409            .data
410            .tools_data_map
411            .get(tool_name)
412            .map(|td| td.visible_inactive_tools.clone());
413        let tool_names_inactive = get_visible_inactive_names(tool_name);
414        let mut annos_inactive: Option<Vec<Annotation>> = None;
415        if let Some(visible_inactive_tools) = visible_inactive_tools {
416            for (tool_name_inactive, show) in tool_names_inactive
417                .iter()
418                .zip(visible_inactive_tools.iter())
419            {
420                let vli = self.data.tools_data_map.get(tool_name_inactive).map(|td| {
421                    match &td.specifics {
422                        tools_data::ToolSpecifics::Bbox(bbox_data) => {
423                            (bbox_data.options.core.visible, bbox_data.label_info())
424                        }
425                        tools_data::ToolSpecifics::Brush(brush_data) => {
426                            (brush_data.options.core.visible, brush_data.label_info())
427                        }
428                        _ => {
429                            panic!("tool {tool_name_inactive} does not redraw annotations ");
430                        }
431                    }
432                });
433                let visibility_inactive = if let Some((visible, label_info)) = vli {
434                    vis_from_lfoption(Some(label_info), visible)
435                } else {
436                    Visibility::All
437                };
438                if show && visibility_active != Visibility::None {
439                    if let Some(annos) = &mut annos_inactive {
440                        let annos_inner = evaluate_visibility(
441                            visibility_inactive,
442                            tool_name_inactive,
443                            &self.data,
444                        );
445                        if let Some(annos_inner) = annos_inner {
446                            annos.extend(annos_inner);
447                        }
448                    } else {
449                        annos_inactive = evaluate_visibility(
450                            visibility_inactive,
451                            tool_name_inactive,
452                            &self.data,
453                        );
454                    }
455                }
456            }
457        }
458        let annos_active = evaluate_visibility(visibility_active, tool_name, &self.data);
459        if let Some(annos_active) = annos_active {
460            if let Some(annos_inactive) = annos_inactive {
461                let mut annos = annos_active;
462                annos.extend(annos_inactive);
463                self.update_view.perm_annos = UpdatePermAnnos::Yes(annos);
464            } else {
465                self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_active);
466            }
467        } else if let Some(annos_inactive) = annos_inactive {
468            self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_inactive);
469        }
470    }
471
472    pub fn request_redraw_tmp_anno(&mut self, anno: Annotation) {
473        self.update_view.tmp_annos = UpdateTmpAnno::Yes(anno);
474    }
475
476    pub fn stop_tmp_anno(&mut self) {
477        self.update_view.tmp_annos = UpdateTmpAnno::No;
478    }
479
480    pub fn request_redraw_image(&mut self) {
481        if self.data.meta_data.file_path_relative().is_some() {
482            self.update_view.image = UpdateImage::Yes(self.data.bg_to_uncropped_view());
483        }
484    }
485
486    /// real image in contrast to the loading image
487    #[must_use]
488    pub fn from_real_im(
489        im: DynamicImage,
490        extra_ims: ExtraIms,
491        tools_data: ToolsDataMap,
492        ui_image_rect: Option<ShapeF>,
493        file_path: Option<String>,
494        prj_path: &Path,
495        file_selected_idx: Option<usize>,
496    ) -> Self {
497        let meta_data = match (file_path, file_selected_idx) {
498            (Some(fp), Some(fsidx)) => MetaData::from_filepath(fp, fsidx, prj_path),
499            _ => MetaData::default(),
500        };
501        Self::new(
502            DataRaw::new(im, extra_ims, tools_data, meta_data, ui_image_rect),
503            None,
504        )
505    }
506
507    #[must_use]
508    pub fn shape_orig(&self) -> ShapeI {
509        self.data.shape()
510    }
511
512    pub fn set_zoom_box(&mut self, zoom_box: Option<BbF>) {
513        let mut set_zb = || {
514            let zoom_box =
515                zoom_box.map(|zb| BbF::new_fit_to_image(zb.x, zb.y, zb.w, zb.h, self.shape_orig()));
516            self.zoom_box = zoom_box;
517            self.update_view = UpdateView::from_zoombox(zoom_box);
518        };
519        if let Some(zb) = zoom_box {
520            if zb.h > 1.0 && zb.w > 1.0 {
521                set_zb();
522            }
523        } else {
524            set_zb();
525        }
526    }
527
528    #[must_use]
529    pub fn zoom_box(&self) -> &Option<BbF> {
530        &self.zoom_box
531    }
532
533    pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
534        self.data.set_image_rect(ui_image_rect);
535    }
536    pub fn set_background_image(&mut self, image: DynamicImage) {
537        if ShapeI::from_im(&image) != self.shape_orig() {
538            self.zoom_box = None;
539        }
540        self.data.im_background = image;
541    }
542    pub fn set_extra_images(&mut self, images: ExtraIms) {
543        self.data.extra_ims = images;
544    }
545}
546impl Debug for World {
547    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548        write!(f, "\nims_raw {:?}", &self.data)
549    }
550}
551
552#[cfg(test)]
553fn rgba_at(i: usize, im: &ViewImage) -> [u8; 4] {
554    let x = (i % im.width() as usize) as u32;
555    let y = (i / im.width() as usize) as u32;
556    let rgb = im.get_pixel(x, y).0;
557    let rgb_changed = rgb;
558    [rgb_changed[0], rgb_changed[1], rgb_changed[2], 0xff]
559}
560#[cfg(test)]
561use image::Rgb;
562
563#[test]
564fn test_rgba() {
565    let mut im_test = ViewImage::new(64, 64);
566    im_test.put_pixel(0, 0, Rgb([23, 23, 23]));
567    assert_eq!(rgba_at(0, &im_test), [23, 23, 23, 255]);
568    im_test.put_pixel(0, 1, Rgb([23, 23, 23]));
569    assert_eq!(rgba_at(64, &im_test), [23, 23, 23, 255]);
570    im_test.put_pixel(7, 11, Rgb([23, 23, 23]));
571    assert_eq!(rgba_at(11 * 64 + 7, &im_test), [23, 23, 23, 255]);
572}