rvlib/tools/
brush.rs

1use brush_data::BrushToolData;
2use std::{cmp::Ordering, mem, thread};
3
4use crate::{
5    annotations_accessor_mut,
6    cfg::ExportPath,
7    events::{Events, KeyCode},
8    history::{History, Record},
9    instance_annotations_accessor, make_tool_transform,
10    meta_data::MetaData,
11    result::trace_ok_err,
12    tools::{
13        core::{check_recolorboxes, check_trigger_history_update, check_trigger_redraw},
14        instance_anno_shared::check_cocoimport,
15    },
16    tools_data::{
17        self,
18        annotations::{BrushAnnotations, InstanceAnnotations},
19        brush_data::{self, MAX_INTENSITY, MAX_THICKNESS, MIN_INTENSITY, MIN_THICKNESS},
20        coco_io::to_per_file_crowd,
21        vis_from_lfoption, ExportAsCoco, InstanceAnnotate, LabelInfo, Rot90ToolData,
22    },
23    tools_data_accessors, tools_data_accessors_objects,
24    util::Visibility,
25    world::World,
26    world_annotations_accessor, Annotation, BrushAnnotation, Line, ShapeI,
27};
28use rvimage_domain::{BrushLine, Canvas, PtF, TPtF};
29
30use super::{
31    core::{
32        change_annos, check_autopaste, check_erase_mode, deselect_all, label_change_key,
33        map_held_key, map_released_key, on_selection_keys, HeldKey, Mover, ReleasedKey,
34    },
35    instance_anno_shared::get_rot90_data,
36    Manipulate, BRUSH_NAME,
37};
38
39pub const ACTOR_NAME: &str = "Brush";
40const MISSING_ANNO_MSG: &str = "brush annotations have not yet been initialized";
41const MISSING_DATA_MSG: &str = "brush data not available";
42annotations_accessor_mut!(ACTOR_NAME, brush_mut, MISSING_ANNO_MSG, BrushAnnotations);
43world_annotations_accessor!(ACTOR_NAME, brush, MISSING_ANNO_MSG, BrushAnnotations);
44instance_annotations_accessor!(Canvas);
45tools_data_accessors!(
46    ACTOR_NAME,
47    MISSING_DATA_MSG,
48    brush_data,
49    BrushToolData,
50    brush,
51    brush_mut
52);
53tools_data_accessors_objects!(
54    ACTOR_NAME,
55    MISSING_DATA_MSG,
56    brush_data,
57    BrushToolData,
58    brush,
59    brush_mut
60);
61pub(super) fn change_annos_brush(world: &mut World, change: impl FnOnce(&mut BrushAnnotations)) {
62    change_annos::<_, DataAccessors, InstanceAnnoAccessors>(world, change);
63}
64
65fn import_coco(
66    meta_data: &MetaData,
67    coco_file: &ExportPath,
68    rot90_data: Option<&Rot90ToolData>,
69) -> Option<BrushToolData> {
70    trace_ok_err(tools_data::coco_io::read_coco(meta_data, coco_file, rot90_data).map(|(_, d)| d))
71}
72
73fn max_select_dist(shape: ShapeI) -> TPtF {
74    (TPtF::from(shape.w.pow(2) + shape.h.pow(2)).sqrt() / 100.0).max(50.0)
75}
76
77fn draw_erase_circle(mut world: World, mp: PtF) -> World {
78    let show_only_current = get_specific(&world).map(|d| d.label_info.show_only_current);
79    let options = get_options(&world).copied();
80    let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
81    if let Some(options) = options {
82        let erase = |annos: &mut BrushAnnotations| {
83            let to_be_removed_line_idx = find_closest_canvas(annos, mp, |idx| {
84                annos.is_of_current_label(idx, idx_current, show_only_current)
85            });
86            if let Some((idx, _)) = to_be_removed_line_idx {
87                let canvas = annos.edit(idx);
88                trace_ok_err(canvas.draw_circle(mp, options.thickness, 0));
89            }
90        };
91        change_annos_brush(&mut world, erase);
92        set_visible(&mut world);
93    }
94    world
95}
96fn key_released(events: &Events, mut world: World, mut history: History) -> (World, History) {
97    let released_key = map_released_key(events);
98    (world, history) = on_selection_keys::<_, DataAccessors, InstanceAnnoAccessors>(
99        world,
100        history,
101        released_key,
102        events.held_ctrl(),
103        BRUSH_NAME,
104    );
105    let mut trigger_redraw = false;
106    if let Some(label_info) = get_specific_mut(&mut world).map(|s| &mut s.label_info) {
107        (*label_info, trigger_redraw) = label_change_key(released_key, mem::take(label_info));
108    }
109    if trigger_redraw {
110        let visible = get_options(&world).map(|o| o.core.visible) == Some(true);
111        let vis = vis_from_lfoption(get_label_info(&world), visible);
112        world.request_redraw_annotations(BRUSH_NAME, vis);
113    }
114    match released_key {
115        ReleasedKey::H if events.held_ctrl() => {
116            // Hide all boxes (selected or not)
117            if let Some(options_mut) = get_options_mut(&mut world) {
118                options_mut.core.visible = !options_mut.core.visible;
119            }
120            let vis = get_visible(&world);
121            world.request_redraw_annotations(BRUSH_NAME, vis);
122        }
123        _ => (),
124    }
125    world = check_erase_mode::<DataAccessors>(released_key, set_visible, world);
126    (world, history)
127}
128fn find_closest_canvas(
129    annos: &BrushAnnotations,
130    p: PtF,
131    predicate: impl Fn(usize) -> bool,
132) -> Option<(usize, f64)> {
133    annos
134        .elts()
135        .iter()
136        .enumerate()
137        .map(|(i, cvs)| {
138            (
139                i,
140                cvs.dist_to_boundary(p) * if cvs.contains(p) { 0.0 } else { 1.0 },
141            )
142        })
143        .filter(|(i, _)| predicate(*i))
144        .min_by(|(_, x), (_, y)| match x.partial_cmp(y) {
145            Some(o) => o,
146            None => Ordering::Greater,
147        })
148}
149
150fn check_selected_intensity_thickness(mut world: World) -> World {
151    let options = get_options(&world).copied();
152    let annos = get_annos_mut(&mut world);
153    let mut any_selected = false;
154    if let (Some(annos), Some(options)) = (annos, options) {
155        if options.is_selection_change_needed {
156            for brushline in annos.selected_elts_iter_mut() {
157                brushline.intensity = options.intensity;
158                any_selected = true;
159            }
160        }
161    }
162    let options_mut = get_options_mut(&mut world);
163    if let Some(options_mut) = options_mut {
164        options_mut.is_selection_change_needed = false;
165        if any_selected {
166            options_mut.core.is_redraw_annos_triggered = true;
167        }
168    }
169    world
170}
171
172fn check_export(mut world: World) -> World {
173    let options = get_options(&world);
174    let specifics = get_specific(&world);
175
176    if options.map(|o| o.core.import_export_trigger.export_triggered()) == Some(true) {
177        let rot90_data = get_rot90_data(&world).cloned();
178        if let Some(data) = specifics {
179            let meta_data = world.data.meta_data.clone();
180            let mut data = data.clone();
181            let per_file_crowd = options.map(|o| o.per_file_crowd) == Some(true);
182            let f_export = move || {
183                let start = std::time::Instant::now();
184                if per_file_crowd {
185                    to_per_file_crowd(&mut data.annotations_map);
186                }
187                let coco_file_conn = data.cocofile_conn();
188                match tools_data::write_coco(&meta_data, data, rot90_data.as_ref(), &coco_file_conn)
189                {
190                    Ok((p, _)) => tracing::info!("export to {p:?} successfully triggered"),
191                    Err(e) => tracing::error!("trigger export failed due to {e:?}"),
192                };
193                tracing::info!("export took {} seconds", start.elapsed().as_secs_f32());
194            };
195            thread::spawn(f_export);
196        }
197        if let Some(options_mut) = get_options_mut(&mut world) {
198            options_mut.core.import_export_trigger.untrigger_export();
199        }
200    }
201    world
202}
203
204pub(super) fn on_mouse_held_right(
205    mouse_pos: Option<PtF>,
206    mover: &mut Mover,
207    mut world: World,
208    history: History,
209) -> (World, History) {
210    if get_options(&world).map(|o| o.core.erase) != Some(true) {
211        let orig_shape = world.data.shape();
212        let move_boxes = |mpo_from, mpo_to| {
213            let annos = get_annos_mut(&mut world);
214            if let Some(annos) = annos {
215                let (mut elts, cat_idxs, selected_mask) = mem::take(annos).separate_data();
216                for (i, anno) in elts.iter_mut().enumerate() {
217                    if selected_mask[i] {
218                        anno.follow_movement(mpo_from, mpo_to, orig_shape);
219                    }
220                }
221                *annos = InstanceAnnotations::new(elts, cat_idxs, selected_mask).unwrap();
222            }
223            Some(())
224        };
225        mover.move_mouse_held(move_boxes, mouse_pos);
226        let vis = get_visible(&world);
227        world.request_redraw_annotations(ACTOR_NAME, vis);
228    }
229    (world, history)
230}
231#[derive(Clone, Debug)]
232pub struct Brush {
233    mover: Mover,
234}
235
236impl Brush {
237    fn mouse_pressed(
238        &mut self,
239        events: &Events,
240        mut world: World,
241        history: History,
242    ) -> (World, History) {
243        if events.pressed(KeyCode::MouseRight) {
244            self.mover.move_mouse_pressed(events.mouse_pos_on_orig);
245        } else {
246            if !(events.held_alt() || events.held_ctrl() || events.held_shift()) {
247                world = deselect_all::<_, DataAccessors, InstanceAnnoAccessors>(world, BRUSH_NAME);
248            }
249            if !events.held_ctrl() {
250                let options = get_options(&world).copied();
251                let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
252                if let (Some(mp), Some(options)) = (events.mouse_pos_on_orig, options) {
253                    let erase = options.core.erase;
254                    if !erase {
255                        if let (Some(d), Some(cat_idx)) =
256                            (get_specific_mut(&mut world), idx_current)
257                        {
258                            let line = Line::from(mp);
259                            d.tmp_line = Some((
260                                BrushLine {
261                                    line,
262                                    intensity: options.intensity,
263                                    thickness: options.thickness,
264                                },
265                                cat_idx,
266                            ));
267                        }
268                    }
269                }
270                set_visible(&mut world);
271            }
272        }
273        (world, history)
274    }
275    fn mouse_held(
276        &mut self,
277        events: &Events,
278        mut world: World,
279        history: History,
280    ) -> (World, History) {
281        if events.held(KeyCode::MouseRight) {
282            on_mouse_held_right(events.mouse_pos_on_orig, &mut self.mover, world, history)
283        } else {
284            if !events.held_ctrl() {
285                let options = get_options(&world).copied();
286                if let (Some(mp), Some(options)) = (events.mouse_pos_on_orig, options) {
287                    if options.core.erase {
288                        world = draw_erase_circle(world, mp);
289                    } else {
290                        let line = if let Some((line, _)) =
291                            get_specific_mut(&mut world).and_then(|d| d.tmp_line.as_mut())
292                        {
293                            let last_point = line.line.last_point();
294                            let dist = if let Some(last_point) = last_point {
295                                last_point.dist_square(&mp)
296                            } else {
297                                100.0
298                            };
299                            if dist >= 3.0 {
300                                line.line.push(mp);
301                            }
302                            Some(line.clone())
303                        } else {
304                            None
305                        };
306                        if let (Some(line), Some(color)) = (
307                            line,
308                            get_specific(&world)
309                                .map(|d| d.label_info.colors()[d.label_info.cat_idx_current]),
310                        ) {
311                            let orig_shape = world.shape_orig();
312                            let canvas_with_new_buffer = || {
313                                let lower_buffer_bound = 100;
314                                let extension_factor = if line.line.points.len() < 10 {
315                                    4.0
316                                } else if line.line.points.len() < 50 {
317                                    3.0
318                                } else {
319                                    2.0
320                                };
321                                Canvas::from_line_extended(
322                                    &line,
323                                    orig_shape,
324                                    extension_factor,
325                                    lower_buffer_bound,
326                                )
327                            };
328                            let canvas = if let Some(buffer) =
329                                mem::take(&mut world.update_view.tmp_anno_buffer)
330                            {
331                                match buffer {
332                                    Annotation::Brush(brush_anno) => {
333                                        tracing::debug!("found buffer for tmp anno");
334                                        Canvas::new(&line, orig_shape, Some(brush_anno.canvas.mask))
335                                    }
336                                    _ => canvas_with_new_buffer(),
337                                }
338                            } else {
339                                canvas_with_new_buffer()
340                            };
341
342                            let canvas = trace_ok_err(canvas);
343                            if let Some(canvas) = canvas {
344                                world.request_redraw_tmp_anno(Annotation::Brush(BrushAnnotation {
345                                    canvas,
346                                    color,
347                                    label: None,
348                                    is_selected: None,
349                                    fill_alpha: options.fill_alpha,
350                                }));
351                            }
352                        }
353                    }
354                }
355            }
356
357            (world, history)
358        }
359    }
360
361    fn mouse_released(
362        &mut self,
363        events: &Events,
364        mut world: World,
365        mut history: History,
366    ) -> (World, History) {
367        if events.held_ctrl() {
368            let shape_orig = world.shape_orig();
369            let show_only_current = get_specific(&world).map(|d| d.label_info.show_only_current);
370            let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
371            if let (Some(mp), Some(annos)) = (events.mouse_pos_on_orig, get_annos_mut(&mut world)) {
372                let to_be_selected_line_idx = find_closest_canvas(annos, mp, |idx| {
373                    annos.is_of_current_label(idx, idx_current, show_only_current)
374                });
375                if let Some((idx, dist)) = to_be_selected_line_idx {
376                    if dist < max_select_dist(shape_orig) {
377                        if annos.selected_mask()[idx] {
378                            annos.deselect(idx);
379                        } else {
380                            annos.select(idx);
381                        }
382                    } else {
383                        world = deselect_all::<_, DataAccessors, InstanceAnnoAccessors>(
384                            world, BRUSH_NAME,
385                        );
386                    }
387                }
388            }
389            set_visible(&mut world);
390        } else if !(events.held_alt() || events.held_shift()) {
391            // neither shift nor alt nor ctrl were held => a brushline has been finished
392            // or a brush line has been deleted.
393            let erase = get_options(&world).map(|o| o.core.erase);
394            let cat_idx = get_specific(&world).map(|o| o.label_info.cat_idx_current);
395            if erase != Some(true) {
396                let shape_orig = world.shape_orig();
397                let line = get_specific(&world).and_then(|d| d.tmp_line.clone());
398                let line = if let Some((line, _)) = line {
399                    Some(line)
400                } else if let (Some(mp), Some(options)) =
401                    (events.mouse_pos_on_orig, get_options(&world))
402                {
403                    Some(BrushLine {
404                        line: Line::from(mp),
405                        intensity: options.intensity,
406                        thickness: options.thickness,
407                    })
408                } else {
409                    None
410                };
411
412                let change_annos = |annos: &mut BrushAnnotations| {
413                    if let (Some(line), Some(cat_idx)) = (line, cat_idx) {
414                        let canvas = Canvas::new(&line, shape_orig, None);
415                        if let Ok(canvas) = canvas {
416                            annos.add_elt(canvas, cat_idx);
417                        }
418                    }
419                };
420                change_annos_brush(&mut world, change_annos);
421                if let Some(d) = get_specific_mut(&mut world) {
422                    d.tmp_line = None;
423                }
424                set_visible(&mut world);
425            } else if let Some(mp) = events.mouse_pos_on_orig {
426                world = draw_erase_circle(world, mp);
427            }
428            history.push(Record::new(world.clone(), ACTOR_NAME));
429        }
430        (world, history)
431    }
432    #[allow(clippy::unused_self)]
433    fn key_released(
434        &mut self,
435        events: &Events,
436        world: World,
437        history: History,
438    ) -> (World, History) {
439        key_released(events, world, history)
440    }
441    fn key_held(
442        &mut self,
443        events: &Events,
444        mut world: World,
445        history: History,
446    ) -> (World, History) {
447        const INTENSITY_STEP: f64 = MAX_INTENSITY / 20.0;
448        const THICKNESS_STEP: f64 = MAX_THICKNESS / 20.0;
449        let held_key = map_held_key(events);
450        let snap_to_step = |x: TPtF, step: TPtF| {
451            if x < 2.0 * step {
452                (x.div_euclid(step)) * step
453            } else {
454                x
455            }
456        };
457        match held_key {
458            HeldKey::I if events.held_alt() => {
459                if let Some(o) = get_options_mut(&mut world) {
460                    o.intensity = MIN_INTENSITY
461                        .max(snap_to_step(o.intensity - INTENSITY_STEP, INTENSITY_STEP));
462                    o.is_selection_change_needed = true;
463                }
464            }
465            HeldKey::I => {
466                if let Some(o) = get_options_mut(&mut world) {
467                    o.intensity = MAX_INTENSITY
468                        .min(snap_to_step(o.intensity + INTENSITY_STEP, INTENSITY_STEP));
469                    o.is_selection_change_needed = true;
470                }
471            }
472            HeldKey::T if events.held_alt() => {
473                if let Some(o) = get_options_mut(&mut world) {
474                    o.thickness = MIN_THICKNESS
475                        .max(snap_to_step(o.thickness - THICKNESS_STEP, THICKNESS_STEP));
476                    o.is_selection_change_needed = true;
477                }
478            }
479            HeldKey::T => {
480                if let Some(o) = get_options_mut(&mut world) {
481                    o.thickness = MAX_THICKNESS
482                        .min(snap_to_step(o.thickness + THICKNESS_STEP, THICKNESS_STEP));
483                    o.is_selection_change_needed = true;
484                }
485            }
486            HeldKey::None => (),
487        }
488        (world, history)
489    }
490}
491
492impl Manipulate for Brush {
493    fn new() -> Self {
494        Self {
495            mover: Mover::new(),
496        }
497    }
498
499    fn on_filechange(&mut self, mut world: World, mut history: History) -> (World, History) {
500        let brush_data = get_specific_mut(&mut world);
501        if let Some(brush_data) = brush_data {
502            for (_, (anno, _)) in brush_data.anno_iter_mut() {
503                anno.deselect_all();
504            }
505        }
506        (world, history) =
507            check_autopaste::<_, DataAccessors, InstanceAnnoAccessors>(world, history, ACTOR_NAME);
508        set_visible(&mut world);
509        (world, history)
510    }
511    fn on_activate(&mut self, mut world: World) -> World {
512        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
513            data.menu_active = true;
514        }
515        set_visible(&mut world);
516        world
517    }
518    fn on_deactivate(&mut self, mut world: World) -> World {
519        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
520            data.menu_active = false;
521        }
522        world.request_redraw_annotations(BRUSH_NAME, Visibility::None);
523        world
524    }
525    fn on_always_active_zoom(&mut self, mut world: World, history: History) -> (World, History) {
526        let visible = get_options(&world).map(|o| o.core.visible) == Some(true);
527        let vis = vis_from_lfoption(get_label_info(&world), visible);
528        world.request_redraw_annotations(BRUSH_NAME, vis);
529        (world, history)
530    }
531    fn events_tf(
532        &mut self,
533        mut world: World,
534        mut history: History,
535        events: &Events,
536    ) -> (World, History) {
537        world = check_trigger_redraw::<DataAccessors>(world, BRUSH_NAME);
538        (world, history) =
539            check_trigger_history_update::<DataAccessors>(world, history, BRUSH_NAME);
540        let imported;
541        (world, imported) = check_cocoimport::<_, _, DataAccessors>(
542            world,
543            get_specific,
544            get_specific_mut,
545            import_coco,
546        );
547        if imported {
548            set_visible(&mut world);
549        }
550        world = check_recolorboxes::<DataAccessors>(world, BRUSH_NAME);
551        world = check_selected_intensity_thickness(world);
552        world = check_export(world);
553        make_tool_transform!(
554            self,
555            world,
556            history,
557            events,
558            [
559                (pressed, KeyCode::MouseLeft, mouse_pressed),
560                (pressed, KeyCode::MouseRight, mouse_pressed),
561                (held, KeyCode::MouseLeft, mouse_held),
562                (held, KeyCode::MouseRight, mouse_held),
563                (released, KeyCode::MouseLeft, mouse_released),
564                (released, KeyCode::Back, key_released),
565                (released, KeyCode::Delete, key_released),
566                (released, KeyCode::A, key_released),
567                (released, KeyCode::C, key_released),
568                (released, KeyCode::D, key_released),
569                (released, KeyCode::E, key_released),
570                (released, KeyCode::H, key_released),
571                (held, KeyCode::I, key_held),
572                (held, KeyCode::T, key_held),
573                (released, KeyCode::V, key_released),
574                (released, KeyCode::Key1, key_released),
575                (released, KeyCode::Key2, key_released),
576                (released, KeyCode::Key3, key_released),
577                (released, KeyCode::Key4, key_released),
578                (released, KeyCode::Key5, key_released),
579                (released, KeyCode::Key6, key_released),
580                (released, KeyCode::Key7, key_released),
581                (released, KeyCode::Key8, key_released),
582                (released, KeyCode::Key9, key_released)
583            ]
584        )
585    }
586}