Skip to main content

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