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