Skip to main content

rvlib/tools/
brush.rs

1use brush_data::BrushToolData;
2use std::{cmp::Ordering, mem, sync::mpsc::Receiver, thread};
3
4use super::{
5    BRUSH_NAME, Manipulate,
6    core::{
7        HeldKey, Mover, ReleasedKey, change_annos, check_autopaste, check_erase_mode,
8        check_instance_label_display_change, deselect_all, instance_label_display_sort,
9        label_change_key, map_held_key, map_released_key, on_selection_keys,
10    },
11    instance_anno_shared::get_rot90_data,
12};
13use crate::{
14    Annotation, BrushAnnotation, Line, ShapeI, annotations_accessor_mut,
15    cfg::ExportPath,
16    events::{Events, KeyCode},
17    history::{History, Record},
18    instance_annotations_accessor, make_tool_transform,
19    meta_data::MetaData,
20    result::trace_ok_err,
21    tools::{
22        core::{check_recolorboxes, check_trigger_history_update, check_trigger_redraw},
23        instance_anno_shared::{check_cocoimport, predictive_labeling},
24    },
25    tools_data::{
26        self, ExportAsCoco, InstanceAnnotate, LabelInfo, Rot90ToolData,
27        annotations::{BrushAnnotations, InstanceAnnotations},
28        brush_data::{self, MAX_INTENSITY, MAX_THICKNESS, MIN_INTENSITY, MIN_THICKNESS},
29        coco_io::to_per_file_crowd,
30        vis_from_lfoption,
31    },
32    tools_data_accessors, tools_data_accessors_objects,
33    util::Visibility,
34    world::World,
35    world_annotations_accessor,
36};
37use rvimage_domain::{BrushLine, Canvas, PtF, TPtF};
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 Some(canvas) = annos.edit(idx)
88            {
89                trace_ok_err(canvas.draw_circle(mp, options.thickness, 0));
90            }
91        };
92        change_annos_brush(&mut world, erase);
93        set_visible(&mut world);
94    }
95    world
96}
97fn mouse_released(events: &Events, mut world: World, mut history: History) -> (World, History) {
98    if events.held_ctrl() {
99        let shape_orig = world.shape_orig();
100        let show_only_current = get_specific(&world).map(|d| d.label_info.show_only_current);
101        let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
102        if let (Some(mp), Some(annos)) = (events.mouse_pos_on_orig, get_annos_mut(&mut world)) {
103            let to_be_selected_line_idx = find_closest_canvas(annos, mp, |idx| {
104                annos.is_of_current_label(idx, idx_current, show_only_current)
105            });
106            if let Some((idx, dist)) = to_be_selected_line_idx {
107                if dist < max_select_dist(shape_orig) {
108                    if annos.selected_mask().get(idx) == Some(&true) {
109                        annos.deselect(idx);
110                    } else {
111                        annos.select(idx);
112                    }
113                } else {
114                    world =
115                        deselect_all::<_, DataAccessors, InstanceAnnoAccessors>(world, BRUSH_NAME);
116                }
117            }
118        }
119        set_visible(&mut world);
120    } else if !(events.held_alt() || events.held_shift()) {
121        // neither shift nor alt nor ctrl were held => a brushline has been finished
122        // or a brush line has been deleted.
123        let erase = get_options(&world).map(|o| o.core.erase);
124        let cat_idx = get_specific(&world).map(|o| o.label_info.cat_idx_current);
125        if erase != Some(true) {
126            let shape_orig = world.shape_orig();
127            let line = get_specific_mut(&mut world).and_then(|d| mem::take(&mut d.tmp_line));
128            let line = if let Some((line, _)) = line {
129                Some(line)
130            } else if let (Some(mp), Some(options)) =
131                (events.mouse_pos_on_orig, get_options(&world))
132            {
133                Some(BrushLine {
134                    line: Line::from(mp),
135                    intensity: options.intensity,
136                    thickness: options.thickness,
137                })
138            } else {
139                None
140            };
141            let ild = get_instance_label_display(&world);
142
143            let change_annos = |annos: &mut BrushAnnotations| {
144                if let (Some(line), Some(cat_idx)) = (line, cat_idx) {
145                    let canvas = Canvas::new(&line, shape_orig, None);
146                    if let Ok(canvas) = canvas {
147                        annos.add_elt(canvas, cat_idx, ild);
148                    }
149                }
150            };
151            change_annos_brush(&mut world, change_annos);
152            set_visible(&mut world);
153        } else if let Some(mp) = events.mouse_pos_on_orig {
154            world = draw_erase_circle(world, mp);
155        }
156        history.push(Record::new(world.clone(), ACTOR_NAME));
157    }
158    (world, history)
159}
160fn mouse_pressed_left(events: &Events, mut world: World) -> World {
161    if !(events.held_alt() || events.held_ctrl() || events.held_shift()) {
162        world = deselect_all::<_, DataAccessors, InstanceAnnoAccessors>(world, BRUSH_NAME);
163    }
164    if !events.held_ctrl() {
165        let options = get_options(&world).copied();
166        let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
167        if let (Some(mp), Some(options)) = (events.mouse_pos_on_orig, options) {
168            let erase = options.core.erase;
169            if !erase && let (Some(d), Some(cat_idx)) = (get_specific_mut(&mut world), idx_current)
170            {
171                let line = Line::from(mp);
172                d.tmp_line = Some((
173                    BrushLine {
174                        line,
175                        intensity: options.intensity,
176                        thickness: options.thickness,
177                    },
178                    cat_idx,
179                ));
180            }
181        }
182        set_visible(&mut world);
183    }
184    world
185}
186fn key_released(events: &Events, mut world: World, mut history: History) -> (World, History) {
187    let released_key = map_released_key(events);
188    (world, history) = on_selection_keys::<_, DataAccessors, InstanceAnnoAccessors>(
189        world,
190        history,
191        released_key,
192        events.held_ctrl(),
193        events.held_shift(),
194        BRUSH_NAME,
195    );
196    let mut trigger_redraw = false;
197    if let Some(label_info) = get_specific_mut(&mut world).map(|s| &mut s.label_info) {
198        (*label_info, trigger_redraw) = label_change_key(released_key, mem::take(label_info));
199    }
200    if trigger_redraw {
201        let visible = get_options(&world).map(|o| o.core.visible) == Some(true);
202        let vis = vis_from_lfoption(get_label_info(&world), visible);
203        world.request_redraw_annotations(BRUSH_NAME, vis);
204    }
205    match released_key {
206        ReleasedKey::H if events.held_ctrl() => {
207            // Hide all boxes (selected or not)
208            if let Some(options_mut) = get_options_mut(&mut world) {
209                options_mut.core.visible = !options_mut.core.visible;
210            }
211            let vis = get_visible(&world);
212            world.request_redraw_annotations(BRUSH_NAME, vis);
213        }
214        _ => (),
215    }
216    world = check_instance_label_display_change::<_, DataAccessors, InstanceAnnoAccessors>(
217        world,
218        released_key,
219        ACTOR_NAME,
220    );
221    world = check_erase_mode::<DataAccessors>(released_key, set_visible, world);
222    (world, history)
223}
224fn find_closest_canvas(
225    annos: &BrushAnnotations,
226    p: PtF,
227    predicate: impl Fn(usize) -> bool,
228) -> Option<(usize, f64)> {
229    annos
230        .elts()
231        .iter()
232        .enumerate()
233        .map(|(i, cvs)| {
234            (
235                i,
236                cvs,
237                cvs.dist_to_boundary(p) * if cvs.contains(p) { 0.0 } else { 1.0 },
238            )
239        })
240        .filter(|(i, _, _)| predicate(*i))
241        .min_by(|(_, x_cvs, x), (_, y_cvs, y)| {
242            fn area(cvs: &Canvas) -> u64 {
243                cvs.mask.iter().map(|a| u64::from(*a)).sum::<u64>()
244            }
245            match x.partial_cmp(y) {
246                Some(o) => match o {
247                    // in case both objects have equal distance from the mouse
248                    // we select the smaller one
249                    Ordering::Equal => area(x_cvs).cmp(&area(y_cvs)),
250                    _ => o,
251                },
252                None => Ordering::Greater,
253            }
254        })
255        .map(|(i, _, x)| (i, x))
256}
257
258fn check_selected_intensity_thickness(mut world: World) -> World {
259    let options = get_options(&world).copied();
260    let annos = get_annos_mut(&mut world);
261    let mut any_selected = false;
262    if let (Some(annos), Some(options)) = (annos, options)
263        && options.is_selection_change_needed
264    {
265        for brushline in annos.selected_elts_iter_mut() {
266            brushline.intensity = options.intensity;
267            any_selected = true;
268        }
269    }
270    let options_mut = get_options_mut(&mut world);
271    if let Some(options_mut) = options_mut {
272        options_mut.is_selection_change_needed = false;
273        if any_selected {
274            options_mut.core.is_redraw_annos_triggered = true;
275        }
276    }
277    world
278}
279
280fn check_export(mut world: World) -> World {
281    let options = get_options(&world);
282    let specifics = get_specific(&world);
283
284    if options.map(|o| o.core.import_export_trigger.export_triggered()) == Some(true) {
285        let rot90_data = get_rot90_data(&world).cloned();
286        if let Some(data) = specifics {
287            let meta_data = world.data.meta_data.clone();
288            let mut data = data.clone();
289            let per_file_crowd = options.map(|o| o.per_file_crowd) == Some(true);
290            let double_check_shape =
291                options.map(|o| o.core.doublecheck_cocoexport_shape) == Some(true);
292            let f_export = move || {
293                let start = std::time::Instant::now();
294                if per_file_crowd {
295                    to_per_file_crowd(&mut data.annotations_map);
296                }
297                let coco_file_conn = data.cocofile_conn();
298                match tools_data::write_coco(
299                    &meta_data,
300                    data,
301                    rot90_data.as_ref(),
302                    &coco_file_conn,
303                    double_check_shape,
304                ) {
305                    Ok((p, _)) => tracing::info!("export to {p:?} successfully triggered"),
306                    Err(e) => tracing::error!("trigger export failed due to {e:?}"),
307                };
308                tracing::info!("export took {} seconds", start.elapsed().as_secs_f32());
309            };
310            thread::spawn(f_export);
311        }
312        if let Some(options_mut) = get_options_mut(&mut world) {
313            options_mut.core.import_export_trigger.untrigger_export();
314        }
315    }
316    world
317}
318
319pub(super) fn on_mouse_held_right(
320    mouse_pos: Option<PtF>,
321    mover: &mut Mover,
322    mut world: World,
323    history: History,
324) -> (World, History) {
325    if get_options(&world).map(|o| o.core.erase) != Some(true) {
326        let orig_shape = world.data.shape();
327        let move_boxes = |mpo_from, mpo_to| {
328            let annos = get_annos_mut(&mut world);
329            if let Some(annos) = annos {
330                let (mut elts, cat_idxs, selected_mask) = mem::take(annos).separate_data();
331                for (i, anno) in elts.iter_mut().enumerate() {
332                    if selected_mask.get(i) == Some(&true) {
333                        anno.follow_movement(mpo_from, mpo_to, orig_shape);
334                    }
335                }
336                *annos = InstanceAnnotations::new(elts, cat_idxs, selected_mask).unwrap();
337            }
338            Some(())
339        };
340        mover.move_mouse_held(move_boxes, mouse_pos);
341        let vis = get_visible(&world);
342        world.request_redraw_annotations(ACTOR_NAME, vis);
343    }
344    (world, history)
345}
346#[derive(Debug)]
347pub struct Brush {
348    mover: Mover,
349    prediction_receiver: Option<Receiver<(World, History)>>,
350}
351
352impl Brush {
353    fn mouse_pressed(
354        &mut self,
355        events: &Events,
356        mut world: World,
357        history: History,
358    ) -> (World, History) {
359        if events.pressed(KeyCode::MouseRight) {
360            self.mover.move_mouse_pressed(events.mouse_pos_on_orig);
361        } else {
362            world = mouse_pressed_left(events, world);
363        }
364        (world, history)
365    }
366    fn mouse_held(
367        &mut self,
368        events: &Events,
369        mut world: World,
370        history: History,
371    ) -> (World, History) {
372        if events.held(KeyCode::MouseRight) {
373            on_mouse_held_right(events.mouse_pos_on_orig, &mut self.mover, world, history)
374        } else {
375            if !events.held_ctrl() {
376                let options = get_options(&world).copied();
377                if let (Some(mp), Some(options)) = (events.mouse_pos_on_orig, options) {
378                    if options.core.erase {
379                        world = draw_erase_circle(world, mp);
380                    } else {
381                        let line = if let Some((line, _)) =
382                            get_specific_mut(&mut world).and_then(|d| d.tmp_line.as_mut())
383                        {
384                            let last_point = line.line.last_point();
385                            let dist = if let Some(last_point) = last_point {
386                                last_point.dist_square(&mp)
387                            } else {
388                                100.0
389                            };
390                            if dist >= 3.0 {
391                                line.line.push(mp);
392                            }
393                            Some(line.clone())
394                        } else {
395                            None
396                        };
397                        if let (Some(line), Some(color)) = (
398                            line,
399                            get_specific(&world)
400                                .and_then(|d| {
401                                    d.label_info.colors().get(d.label_info.cat_idx_current)
402                                })
403                                .copied(),
404                        ) {
405                            let orig_shape = world.shape_orig();
406                            let canvas_with_new_buffer = || {
407                                let lower_buffer_bound = 100;
408                                let extension_factor = if line.line.points.len() < 10 {
409                                    4.0
410                                } else if line.line.points.len() < 50 {
411                                    3.0
412                                } else {
413                                    2.0
414                                };
415                                Canvas::from_line_extended(
416                                    &line,
417                                    orig_shape,
418                                    extension_factor,
419                                    lower_buffer_bound,
420                                )
421                            };
422                            let canvas = if let Some(buffer) =
423                                mem::take(&mut world.update_view.tmp_anno_buffer)
424                            {
425                                match buffer {
426                                    Annotation::Brush(brush_anno) => {
427                                        tracing::debug!("found buffer for tmp anno");
428                                        Canvas::new(&line, orig_shape, Some(brush_anno.canvas.mask))
429                                    }
430                                    _ => canvas_with_new_buffer(),
431                                }
432                            } else {
433                                canvas_with_new_buffer()
434                            };
435
436                            let canvas = trace_ok_err(canvas);
437                            if let Some(canvas) = canvas {
438                                world.request_redraw_tmp_anno(Annotation::Brush(BrushAnnotation {
439                                    canvas,
440                                    color,
441                                    label: None,
442                                    is_selected: None,
443                                    fill_alpha: options.fill_alpha,
444                                    instance_display_label: options.core.instance_label_display,
445                                }));
446                            }
447                        }
448                    }
449                }
450            }
451
452            (world, history)
453        }
454    }
455
456    fn mouse_released(
457        &mut self,
458        events: &Events,
459        world: World,
460        history: History,
461    ) -> (World, History) {
462        mouse_released(events, world, history)
463    }
464
465    #[allow(clippy::unused_self)]
466    fn key_released(
467        &mut self,
468        events: &Events,
469        world: World,
470        history: History,
471    ) -> (World, History) {
472        key_released(events, world, history)
473    }
474    fn key_held(
475        &mut self,
476        events: &Events,
477        mut world: World,
478        history: History,
479    ) -> (World, History) {
480        const INTENSITY_STEP: f64 = MAX_INTENSITY / 20.0;
481        const THICKNESS_STEP: f64 = MAX_THICKNESS / 20.0;
482        let held_key = map_held_key(events);
483        let snap_to_step = |x: TPtF, step: TPtF| {
484            if x < 2.0 * step {
485                (x.div_euclid(step)) * step
486            } else {
487                x
488            }
489        };
490        match held_key {
491            HeldKey::I if events.held_alt() => {
492                if let Some(o) = get_options_mut(&mut world) {
493                    o.intensity = MIN_INTENSITY
494                        .max(snap_to_step(o.intensity - INTENSITY_STEP, INTENSITY_STEP));
495                    o.is_selection_change_needed = true;
496                }
497            }
498            HeldKey::I => {
499                if let Some(o) = get_options_mut(&mut world) {
500                    o.intensity = MAX_INTENSITY
501                        .min(snap_to_step(o.intensity + INTENSITY_STEP, INTENSITY_STEP));
502                    o.is_selection_change_needed = true;
503                }
504            }
505            HeldKey::T if events.held_alt() => {
506                if let Some(o) = get_options_mut(&mut world) {
507                    o.thickness = MIN_THICKNESS
508                        .max(snap_to_step(o.thickness - THICKNESS_STEP, THICKNESS_STEP));
509                    o.is_selection_change_needed = true;
510                }
511            }
512            HeldKey::T => {
513                if let Some(o) = get_options_mut(&mut world) {
514                    o.thickness = MAX_THICKNESS
515                        .min(snap_to_step(o.thickness + THICKNESS_STEP, THICKNESS_STEP));
516                    o.is_selection_change_needed = true;
517                }
518            }
519            HeldKey::None => (),
520        }
521        (world, history)
522    }
523}
524
525impl Clone for Brush {
526    fn clone(&self) -> Self {
527        Self {
528            mover: self.mover,
529            prediction_receiver: None, // JoinHandle cannot be cloned
530        }
531    }
532}
533impl Manipulate for Brush {
534    fn new() -> Self {
535        Self {
536            mover: Mover::new(),
537            prediction_receiver: None,
538        }
539    }
540
541    fn on_filechange(&mut self, mut world: World, mut history: History) -> (World, History) {
542        use_currentimageshape_for_annos(&mut world);
543
544        let brush_data = get_specific_mut(&mut world);
545        if let Some(brush_data) = brush_data {
546            for (_, (anno, _)) in brush_data.anno_iter_mut() {
547                anno.deselect_all();
548            }
549            let ild = get_instance_label_display(&world);
550            world = instance_label_display_sort::<_, DataAccessors, InstanceAnnoAccessors>(
551                world, ild, ACTOR_NAME,
552            );
553        }
554        (world, history) =
555            check_autopaste::<_, DataAccessors, InstanceAnnoAccessors>(world, history, ACTOR_NAME);
556        set_visible(&mut world);
557        (world, history)
558    }
559    fn on_activate(&mut self, mut world: World) -> World {
560        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
561            data.menu_active = true;
562        }
563        set_visible(&mut world);
564        world
565    }
566    fn on_deactivate(&mut self, mut world: World) -> World {
567        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
568            data.menu_active = false;
569        }
570        world.request_redraw_annotations(BRUSH_NAME, Visibility::None);
571        world
572    }
573    fn on_always_active_zoom(&mut self, mut world: World, history: History) -> (World, History) {
574        let visible = get_options(&world).map(|o| o.core.visible) == Some(true);
575        let vis = vis_from_lfoption(get_label_info(&world), visible);
576        world.request_redraw_annotations(BRUSH_NAME, vis);
577        (world, history)
578    }
579    fn events_tf(
580        &mut self,
581        mut world: World,
582        mut history: History,
583        events: &Events,
584    ) -> (World, History) {
585        world = check_trigger_redraw::<DataAccessors>(world, BRUSH_NAME);
586        (world, history) =
587            check_trigger_history_update::<DataAccessors>(world, history, BRUSH_NAME);
588        let imported;
589        (world, imported) = check_cocoimport::<_, _, DataAccessors>(
590            world,
591            get_specific,
592            get_specific_mut,
593            import_coco,
594        );
595        if imported {
596            set_visible(&mut world);
597        }
598        predictive_labeling::<DataAccessors>(
599            &mut world,
600            &mut history,
601            ACTOR_NAME,
602            &mut self.prediction_receiver,
603        );
604        world = check_recolorboxes::<DataAccessors>(world, BRUSH_NAME);
605        world = check_selected_intensity_thickness(world);
606        world = check_export(world);
607        make_tool_transform!(
608            self,
609            world,
610            history,
611            events,
612            [
613                (pressed, KeyCode::MouseLeft, mouse_pressed),
614                (pressed, KeyCode::MouseRight, mouse_pressed),
615                (held, KeyCode::MouseLeft, mouse_held),
616                (held, KeyCode::MouseRight, mouse_held),
617                (released, KeyCode::MouseLeft, mouse_released),
618                (released, KeyCode::Back, key_released),
619                (released, KeyCode::Delete, key_released),
620                (released, KeyCode::A, key_released),
621                (released, KeyCode::C, key_released),
622                (released, KeyCode::D, key_released),
623                (released, KeyCode::E, key_released),
624                (released, KeyCode::H, key_released),
625                (held, KeyCode::I, key_held),
626                (released, KeyCode::L, key_released),
627                (held, KeyCode::T, key_held),
628                (released, KeyCode::V, key_released),
629                (released, KeyCode::Key1, key_released),
630                (released, KeyCode::Key2, key_released),
631                (released, KeyCode::Key3, key_released),
632                (released, KeyCode::Key4, key_released),
633                (released, KeyCode::Key5, key_released),
634                (released, KeyCode::Key6, key_released),
635                (released, KeyCode::Key7, key_released),
636                (released, KeyCode::Key8, key_released),
637                (released, KeyCode::Key9, key_released)
638            ]
639        )
640    }
641}
642
643#[cfg(test)]
644use {
645    crate::{
646        tracing_setup::init_tracing_for_tests,
647        types::{ThumbIms, ViewImage},
648    },
649    image::DynamicImage,
650};
651
652#[cfg(test)]
653pub fn test_data() -> (Option<PtF>, World, History) {
654    use std::path::Path;
655
656    use crate::ToolsDataMap;
657    let im_test = DynamicImage::ImageRgb8(ViewImage::new(64, 64));
658    let mut world = World::from_real_im(
659        im_test,
660        ThumbIms::default(),
661        ToolsDataMap::new(),
662        None,
663        Some("superimage.png".to_string()),
664        Path::new("superimage.png"),
665        Some(0),
666    );
667    world.data.meta_data.flags.is_loading_screen_active = Some(false);
668    get_specific_mut(&mut world)
669        .unwrap()
670        .label_info
671        .push("label".to_string(), None, None)
672        .unwrap();
673    let history = History::default();
674    let mouse_pos = Some((32.0, 32.0).into());
675    (mouse_pos, world, history)
676}
677
678#[test]
679fn test_mouse_released() {
680    init_tracing_for_tests();
681    let (mp, mut world, history) = test_data();
682    let options = get_options_mut(&mut world).unwrap();
683    options.thickness = 1.0;
684    let mut events = Events::default();
685    events.mouse_pos_on_orig = mp;
686    let (world, history) = mouse_released(&events, world, history);
687    let annos = get_annos(&world).unwrap();
688    assert_eq!(annos.len(), 1);
689    assert_eq!(annos.elts()[0].bb.x, 32);
690    assert_eq!(annos.elts()[0].bb.y, 32);
691    events.mouse_pos_on_orig = Some((40, 40).into());
692    let world = mouse_pressed_left(&events, world);
693    let (world, history) = mouse_released(&events, world, history);
694    let annos = get_annos(&world).unwrap();
695    assert_eq!(annos.len(), 2);
696    assert_eq!(annos.elts()[0].bb.x, 32);
697    assert_eq!(annos.elts()[0].bb.y, 32);
698    assert_eq!(annos.elts()[1].bb.x, 40);
699    assert_eq!(annos.elts()[1].bb.y, 40);
700    events.mouse_pos_on_orig = Some((10, 10).into());
701    let (world, _) = mouse_released(&events, world, history);
702    let annos = get_annos(&world).unwrap();
703    assert_eq!(annos.len(), 3);
704    assert_eq!(annos.elts()[0].bb.x, 32);
705    assert_eq!(annos.elts()[0].bb.y, 32);
706    assert_eq!(annos.elts()[1].bb.x, 40);
707    assert_eq!(annos.elts()[1].bb.y, 40);
708    assert_eq!(annos.elts()[2].bb.x, 10);
709    assert_eq!(annos.elts()[2].bb.y, 10);
710}