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
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(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 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(¤t_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 #[must_use]
281 pub fn shape_initial(&self) -> &ShapeI {
282 &self.shape_initial
283 }
284
285 pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
286 self.ui_image_rect = ui_image_rect;
287 }
288
289 pub fn apply<FI>(&mut self, mut f_i: FI)
290 where
291 FI: FnMut(DynamicImage) -> DynamicImage,
292 {
293 self.im_background = f_i(mem::take(&mut self.im_background));
294 }
295
296 #[must_use]
297 pub fn shape(&self) -> ShapeI {
298 ShapeI::from_im(&self.im_background)
299 }
300
301 #[must_use]
302 pub fn bg_to_uncropped_view(&self) -> ViewImage {
303 image_util::orig_to_0_255(&self.im_background, &None)
304 }
305}
306
307impl Debug for DataRaw {
308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309 write!(
310 f,
311 "\nshape {:?}\ntools data {:?}",
312 self.shape(),
313 self.tools_data_map,
314 )
315 }
316}
317
318fn evaluate_visibility(
319 visibility: Visibility,
320 tool_name: &str,
321 data: &DataRaw,
322) -> Option<Vec<Annotation>> {
323 match (
324 visibility,
325 &data.meta_data.file_path_relative(),
326 data.tools_data_map.get(tool_name),
327 ) {
328 (Visibility::All, Some(file_path_relative), Some(td)) => {
329 td.specifics.to_annotations_view(file_path_relative, None)
330 }
331 (Visibility::Only(idx), Some(file_path), Some(td)) => {
332 td.specifics.to_annotations_view(file_path, Some(idx))
333 }
334 (Visibility::None, _, _) => Some(vec![]),
335 _ => None,
336 }
337}
338#[derive(Clone, Default)]
340pub struct World {
341 pub update_view: UpdateView,
342 pub data: DataRaw,
343 zoom_box: Option<BbF>,
345}
346
347impl World {
348 #[must_use]
349 pub fn new(ims_raw: DataRaw, zoom_box: Option<BbF>) -> Self {
350 let im = ims_raw.bg_to_uncropped_view();
351 let world = Self {
352 data: ims_raw,
353 zoom_box,
354 update_view: UpdateView {
355 image: UpdateImage::Yes(im),
356 perm_annos: UpdatePermAnnos::No,
357 tmp_annos: UpdateTmpAnno::No,
358 zoom_box: UpdateZoomBox::Yes(zoom_box),
359 image_info: None,
360 tmp_anno_buffer: None,
361 },
362 };
363 add_tools_initial_data(world)
364 }
365
366 #[must_use]
367 pub fn ui_image_rect(&self) -> Option<ShapeF> {
368 self.data.ui_image_rect
369 }
370
371 pub fn request_redraw_annotations(&mut self, tool_name: &str, visibility_active: Visibility) {
376 let visible_inactive_tools = self
377 .data
378 .tools_data_map
379 .get(tool_name)
380 .map(|td| td.visible_inactive_tools.clone());
381 let tool_names_inactive = get_visible_inactive_names(tool_name);
382 let mut annos_inactive: Option<Vec<Annotation>> = None;
383 if let Some(visible_inactive_tools) = visible_inactive_tools {
384 for (tool_name_inactive, show) in tool_names_inactive
385 .iter()
386 .zip(visible_inactive_tools.iter())
387 {
388 let vli = self.data.tools_data_map.get(*tool_name_inactive).map(|td| {
389 match &td.specifics {
390 tools_data::ToolSpecifics::Bbox(bbox_data) => {
391 (bbox_data.options.core.visible, bbox_data.label_info())
392 }
393 tools_data::ToolSpecifics::Brush(brush_data) => {
394 (brush_data.options.core.visible, brush_data.label_info())
395 }
396 _ => {
397 panic!("tool {tool_name_inactive} does not redraw annotations ");
398 }
399 }
400 });
401 let visibility_inactive = if let Some((visible, label_info)) = vli {
402 vis_from_lfoption(Some(label_info), visible)
403 } else {
404 Visibility::All
405 };
406 if show && visibility_active != Visibility::None {
407 if let Some(annos) = &mut annos_inactive {
408 let annos_inner = evaluate_visibility(
409 visibility_inactive,
410 tool_name_inactive,
411 &self.data,
412 );
413 if let Some(annos_inner) = annos_inner {
414 annos.extend(annos_inner);
415 }
416 } else {
417 annos_inactive = evaluate_visibility(
418 visibility_inactive,
419 tool_name_inactive,
420 &self.data,
421 );
422 }
423 }
424 }
425 }
426 let annos_active = evaluate_visibility(visibility_active, tool_name, &self.data);
427 if let Some(annos_active) = annos_active {
428 if let Some(annos_inactive) = annos_inactive {
429 let mut annos = annos_active;
430 annos.extend(annos_inactive);
431 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos);
432 } else {
433 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_active);
434 }
435 } else if let Some(annos_inactive) = annos_inactive {
436 self.update_view.perm_annos = UpdatePermAnnos::Yes(annos_inactive);
437 }
438 }
439
440 pub fn request_redraw_tmp_anno(&mut self, anno: Annotation) {
441 self.update_view.tmp_annos = UpdateTmpAnno::Yes(anno);
442 }
443
444 pub fn stop_tmp_anno(&mut self) {
445 self.update_view.tmp_annos = UpdateTmpAnno::No;
446 }
447
448 pub fn request_redraw_image(&mut self) {
449 if self.data.meta_data.file_path_relative().is_some() {
450 self.update_view.image = UpdateImage::Yes(self.data.bg_to_uncropped_view());
451 }
452 }
453
454 #[must_use]
456 pub fn from_real_im(
457 im: DynamicImage,
458 tools_data: ToolsDataMap,
459 ui_image_rect: Option<ShapeF>,
460 file_path: Option<String>,
461 prj_path: &Path,
462 file_selected_idx: Option<usize>,
463 ) -> Self {
464 let meta_data = match (file_path, file_selected_idx) {
465 (Some(fp), Some(fsidx)) => MetaData::from_filepath(fp, fsidx, prj_path),
466 _ => MetaData::default(),
467 };
468 Self::new(DataRaw::new(im, tools_data, meta_data, ui_image_rect), None)
469 }
470
471 #[must_use]
472 pub fn shape_orig(&self) -> ShapeI {
473 self.data.shape()
474 }
475
476 pub fn set_zoom_box(&mut self, zoom_box: Option<BbF>) {
477 let mut set_zb = || {
478 let zoom_box =
479 zoom_box.map(|zb| BbF::new_fit_to_image(zb.x, zb.y, zb.w, zb.h, self.shape_orig()));
480 self.zoom_box = zoom_box;
481 self.update_view = UpdateView::from_zoombox(zoom_box);
482 };
483 if let Some(zb) = zoom_box {
484 if zb.h > 1.0 && zb.w > 1.0 {
485 set_zb();
486 }
487 } else {
488 set_zb();
489 }
490 }
491
492 #[must_use]
493 pub fn zoom_box(&self) -> &Option<BbF> {
494 &self.zoom_box
495 }
496
497 pub fn set_image_rect(&mut self, ui_image_rect: Option<ShapeF>) {
498 self.data.set_image_rect(ui_image_rect);
499 }
500}
501impl Debug for World {
502 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
503 write!(f, "\nims_raw {:?}", &self.data)
504 }
505}
506
507#[cfg(test)]
508fn rgba_at(i: usize, im: &ViewImage) -> [u8; 4] {
509 let x = (i % im.width() as usize) as u32;
510 let y = (i / im.width() as usize) as u32;
511 let rgb = im.get_pixel(x, y).0;
512 let rgb_changed = rgb;
513 [rgb_changed[0], rgb_changed[1], rgb_changed[2], 0xff]
514}
515#[cfg(test)]
516use image::Rgb;
517
518#[test]
519fn test_rgba() {
520 let mut im_test = ViewImage::new(64, 64);
521 im_test.put_pixel(0, 0, Rgb([23, 23, 23]));
522 assert_eq!(rgba_at(0, &im_test), [23, 23, 23, 255]);
523 im_test.put_pixel(0, 1, Rgb([23, 23, 23]));
524 assert_eq!(rgba_at(64, &im_test), [23, 23, 23, 255]);
525 im_test.put_pixel(7, 11, Rgb([23, 23, 23]));
526 assert_eq!(rgba_at(11 * 64 + 7, &im_test), [23, 23, 23, 255]);
527}