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, check_instance_label_display_change,
33        deselect_all, instance_label_display_sort, label_change_key, map_held_key,
34        map_released_key, on_selection_keys, HeldKey, Mover, ReleasedKey,
35    },
36    instance_anno_shared::get_rot90_data,
37    Manipulate, BRUSH_NAME,
38};
39
40pub const ACTOR_NAME: &str = "Brush";
41const MISSING_ANNO_MSG: &str = "brush annotations have not yet been initialized";
42const MISSING_DATA_MSG: &str = "brush data not available";
43annotations_accessor_mut!(ACTOR_NAME, brush_mut, MISSING_ANNO_MSG, BrushAnnotations);
44world_annotations_accessor!(ACTOR_NAME, brush, MISSING_ANNO_MSG, BrushAnnotations);
45instance_annotations_accessor!(Canvas);
46tools_data_accessors!(
47    ACTOR_NAME,
48    MISSING_DATA_MSG,
49    brush_data,
50    BrushToolData,
51    brush,
52    brush_mut
53);
54tools_data_accessors_objects!(
55    ACTOR_NAME,
56    MISSING_DATA_MSG,
57    brush_data,
58    BrushToolData,
59    brush,
60    brush_mut
61);
62pub(super) fn change_annos_brush(world: &mut World, change: impl FnOnce(&mut BrushAnnotations)) {
63    change_annos::<_, DataAccessors, InstanceAnnoAccessors>(world, change);
64}
65
66fn import_coco(
67    meta_data: &MetaData,
68    coco_file: &ExportPath,
69    rot90_data: Option<&Rot90ToolData>,
70) -> Option<BrushToolData> {
71    trace_ok_err(tools_data::coco_io::read_coco(meta_data, coco_file, rot90_data).map(|(_, d)| d))
72}
73
74fn max_select_dist(shape: ShapeI) -> TPtF {
75    (TPtF::from(shape.w.pow(2) + shape.h.pow(2)).sqrt() / 100.0).max(50.0)
76}
77
78fn draw_erase_circle(mut world: World, mp: PtF) -> World {
79    let show_only_current = get_specific(&world).map(|d| d.label_info.show_only_current);
80    let options = get_options(&world).copied();
81    let idx_current = get_specific(&world).map(|d| d.label_info.cat_idx_current);
82    if let Some(options) = options {
83        let erase = |annos: &mut BrushAnnotations| {
84            let to_be_removed_line_idx = find_closest_canvas(annos, mp, |idx| {
85                annos.is_of_current_label(idx, idx_current, show_only_current)
86            });
87            if let Some((idx, _)) = to_be_removed_line_idx {
88                let canvas = annos.edit(idx);
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()[idx] {
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 {
170                if let (Some(d), Some(cat_idx)) = (get_specific_mut(&mut world), idx_current) {
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        }
183        set_visible(&mut world);
184    }
185    world
186}
187fn key_released(events: &Events, mut world: World, mut history: History) -> (World, History) {
188    let released_key = map_released_key(events);
189    (world, history) = on_selection_keys::<_, DataAccessors, InstanceAnnoAccessors>(
190        world,
191        history,
192        released_key,
193        events.held_ctrl(),
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.dist_to_boundary(p) * if cvs.contains(p) { 0.0 } else { 1.0 },
237            )
238        })
239        .filter(|(i, _)| predicate(*i))
240        .min_by(|(_, x), (_, y)| match x.partial_cmp(y) {
241            Some(o) => o,
242            None => Ordering::Greater,
243        })
244}
245
246fn check_selected_intensity_thickness(mut world: World) -> World {
247    let options = get_options(&world).copied();
248    let annos = get_annos_mut(&mut world);
249    let mut any_selected = false;
250    if let (Some(annos), Some(options)) = (annos, options) {
251        if options.is_selection_change_needed {
252            for brushline in annos.selected_elts_iter_mut() {
253                brushline.intensity = options.intensity;
254                any_selected = true;
255            }
256        }
257    }
258    let options_mut = get_options_mut(&mut world);
259    if let Some(options_mut) = options_mut {
260        options_mut.is_selection_change_needed = false;
261        if any_selected {
262            options_mut.core.is_redraw_annos_triggered = true;
263        }
264    }
265    world
266}
267
268fn check_export(mut world: World) -> World {
269    let options = get_options(&world);
270    let specifics = get_specific(&world);
271
272    if options.map(|o| o.core.import_export_trigger.export_triggered()) == Some(true) {
273        let rot90_data = get_rot90_data(&world).cloned();
274        if let Some(data) = specifics {
275            let meta_data = world.data.meta_data.clone();
276            let mut data = data.clone();
277            let per_file_crowd = options.map(|o| o.per_file_crowd) == Some(true);
278            let f_export = move || {
279                let start = std::time::Instant::now();
280                if per_file_crowd {
281                    to_per_file_crowd(&mut data.annotations_map);
282                }
283                let coco_file_conn = data.cocofile_conn();
284                match tools_data::write_coco(&meta_data, data, rot90_data.as_ref(), &coco_file_conn)
285                {
286                    Ok((p, _)) => tracing::info!("export to {p:?} successfully triggered"),
287                    Err(e) => tracing::error!("trigger export failed due to {e:?}"),
288                };
289                tracing::info!("export took {} seconds", start.elapsed().as_secs_f32());
290            };
291            thread::spawn(f_export);
292        }
293        if let Some(options_mut) = get_options_mut(&mut world) {
294            options_mut.core.import_export_trigger.untrigger_export();
295        }
296    }
297    world
298}
299
300pub(super) fn on_mouse_held_right(
301    mouse_pos: Option<PtF>,
302    mover: &mut Mover,
303    mut world: World,
304    history: History,
305) -> (World, History) {
306    if get_options(&world).map(|o| o.core.erase) != Some(true) {
307        let orig_shape = world.data.shape();
308        let move_boxes = |mpo_from, mpo_to| {
309            let annos = get_annos_mut(&mut world);
310            if let Some(annos) = annos {
311                let (mut elts, cat_idxs, selected_mask) = mem::take(annos).separate_data();
312                for (i, anno) in elts.iter_mut().enumerate() {
313                    if selected_mask[i] {
314                        anno.follow_movement(mpo_from, mpo_to, orig_shape);
315                    }
316                }
317                *annos = InstanceAnnotations::new(elts, cat_idxs, selected_mask).unwrap();
318            }
319            Some(())
320        };
321        mover.move_mouse_held(move_boxes, mouse_pos);
322        let vis = get_visible(&world);
323        world.request_redraw_annotations(ACTOR_NAME, vis);
324    }
325    (world, history)
326}
327#[derive(Clone, Debug)]
328pub struct Brush {
329    mover: Mover,
330}
331
332impl Brush {
333    fn mouse_pressed(
334        &mut self,
335        events: &Events,
336        mut world: World,
337        history: History,
338    ) -> (World, History) {
339        if events.pressed(KeyCode::MouseRight) {
340            self.mover.move_mouse_pressed(events.mouse_pos_on_orig);
341        } else {
342            world = mouse_pressed_left(events, world);
343        }
344        (world, history)
345    }
346    fn mouse_held(
347        &mut self,
348        events: &Events,
349        mut world: World,
350        history: History,
351    ) -> (World, History) {
352        if events.held(KeyCode::MouseRight) {
353            on_mouse_held_right(events.mouse_pos_on_orig, &mut self.mover, world, history)
354        } else {
355            if !events.held_ctrl() {
356                let options = get_options(&world).copied();
357                if let (Some(mp), Some(options)) = (events.mouse_pos_on_orig, options) {
358                    if options.core.erase {
359                        world = draw_erase_circle(world, mp);
360                    } else {
361                        let line = if let Some((line, _)) =
362                            get_specific_mut(&mut world).and_then(|d| d.tmp_line.as_mut())
363                        {
364                            let last_point = line.line.last_point();
365                            let dist = if let Some(last_point) = last_point {
366                                last_point.dist_square(&mp)
367                            } else {
368                                100.0
369                            };
370                            if dist >= 3.0 {
371                                line.line.push(mp);
372                            }
373                            Some(line.clone())
374                        } else {
375                            None
376                        };
377                        if let (Some(line), Some(color)) = (
378                            line,
379                            get_specific(&world)
380                                .map(|d| d.label_info.colors()[d.label_info.cat_idx_current]),
381                        ) {
382                            let orig_shape = world.shape_orig();
383                            let canvas_with_new_buffer = || {
384                                let lower_buffer_bound = 100;
385                                let extension_factor = if line.line.points.len() < 10 {
386                                    4.0
387                                } else if line.line.points.len() < 50 {
388                                    3.0
389                                } else {
390                                    2.0
391                                };
392                                Canvas::from_line_extended(
393                                    &line,
394                                    orig_shape,
395                                    extension_factor,
396                                    lower_buffer_bound,
397                                )
398                            };
399                            let canvas = if let Some(buffer) =
400                                mem::take(&mut world.update_view.tmp_anno_buffer)
401                            {
402                                match buffer {
403                                    Annotation::Brush(brush_anno) => {
404                                        tracing::debug!("found buffer for tmp anno");
405                                        Canvas::new(&line, orig_shape, Some(brush_anno.canvas.mask))
406                                    }
407                                    _ => canvas_with_new_buffer(),
408                                }
409                            } else {
410                                canvas_with_new_buffer()
411                            };
412
413                            let canvas = trace_ok_err(canvas);
414                            if let Some(canvas) = canvas {
415                                world.request_redraw_tmp_anno(Annotation::Brush(BrushAnnotation {
416                                    canvas,
417                                    color,
418                                    label: None,
419                                    is_selected: None,
420                                    fill_alpha: options.fill_alpha,
421                                    instance_display_label: options.core.instance_label_display,
422                                }));
423                            }
424                        }
425                    }
426                }
427            }
428
429            (world, history)
430        }
431    }
432
433    fn mouse_released(
434        &mut self,
435        events: &Events,
436        world: World,
437        history: History,
438    ) -> (World, History) {
439        mouse_released(events, world, history)
440    }
441
442    #[allow(clippy::unused_self)]
443    fn key_released(
444        &mut self,
445        events: &Events,
446        world: World,
447        history: History,
448    ) -> (World, History) {
449        key_released(events, world, history)
450    }
451    fn key_held(
452        &mut self,
453        events: &Events,
454        mut world: World,
455        history: History,
456    ) -> (World, History) {
457        const INTENSITY_STEP: f64 = MAX_INTENSITY / 20.0;
458        const THICKNESS_STEP: f64 = MAX_THICKNESS / 20.0;
459        let held_key = map_held_key(events);
460        let snap_to_step = |x: TPtF, step: TPtF| {
461            if x < 2.0 * step {
462                (x.div_euclid(step)) * step
463            } else {
464                x
465            }
466        };
467        match held_key {
468            HeldKey::I if events.held_alt() => {
469                if let Some(o) = get_options_mut(&mut world) {
470                    o.intensity = MIN_INTENSITY
471                        .max(snap_to_step(o.intensity - INTENSITY_STEP, INTENSITY_STEP));
472                    o.is_selection_change_needed = true;
473                }
474            }
475            HeldKey::I => {
476                if let Some(o) = get_options_mut(&mut world) {
477                    o.intensity = MAX_INTENSITY
478                        .min(snap_to_step(o.intensity + INTENSITY_STEP, INTENSITY_STEP));
479                    o.is_selection_change_needed = true;
480                }
481            }
482            HeldKey::T if events.held_alt() => {
483                if let Some(o) = get_options_mut(&mut world) {
484                    o.thickness = MIN_THICKNESS
485                        .max(snap_to_step(o.thickness - THICKNESS_STEP, THICKNESS_STEP));
486                    o.is_selection_change_needed = true;
487                }
488            }
489            HeldKey::T => {
490                if let Some(o) = get_options_mut(&mut world) {
491                    o.thickness = MAX_THICKNESS
492                        .min(snap_to_step(o.thickness + THICKNESS_STEP, THICKNESS_STEP));
493                    o.is_selection_change_needed = true;
494                }
495            }
496            HeldKey::None => (),
497        }
498        (world, history)
499    }
500}
501
502impl Manipulate for Brush {
503    fn new() -> Self {
504        Self {
505            mover: Mover::new(),
506        }
507    }
508
509    fn on_filechange(&mut self, mut world: World, mut history: History) -> (World, History) {
510        let brush_data = get_specific_mut(&mut world);
511        if let Some(brush_data) = brush_data {
512            for (_, (anno, _)) in brush_data.anno_iter_mut() {
513                anno.deselect_all();
514            }
515            let ild = get_instance_label_display(&world);
516            world = instance_label_display_sort::<_, DataAccessors, InstanceAnnoAccessors>(
517                world, ild, ACTOR_NAME,
518            );
519        }
520        (world, history) =
521            check_autopaste::<_, DataAccessors, InstanceAnnoAccessors>(world, history, ACTOR_NAME);
522        set_visible(&mut world);
523        (world, history)
524    }
525    fn on_activate(&mut self, mut world: World) -> World {
526        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
527            data.menu_active = true;
528        }
529        set_visible(&mut world);
530        world
531    }
532    fn on_deactivate(&mut self, mut world: World) -> World {
533        if let Some(data) = trace_ok_err(get_data_mut(&mut world)) {
534            data.menu_active = false;
535        }
536        world.request_redraw_annotations(BRUSH_NAME, Visibility::None);
537        world
538    }
539    fn on_always_active_zoom(&mut self, mut world: World, history: History) -> (World, History) {
540        let visible = get_options(&world).map(|o| o.core.visible) == Some(true);
541        let vis = vis_from_lfoption(get_label_info(&world), visible);
542        world.request_redraw_annotations(BRUSH_NAME, vis);
543        (world, history)
544    }
545    fn events_tf(
546        &mut self,
547        mut world: World,
548        mut history: History,
549        events: &Events,
550    ) -> (World, History) {
551        world = check_trigger_redraw::<DataAccessors>(world, BRUSH_NAME);
552        (world, history) =
553            check_trigger_history_update::<DataAccessors>(world, history, BRUSH_NAME);
554        let imported;
555        (world, imported) = check_cocoimport::<_, _, DataAccessors>(
556            world,
557            get_specific,
558            get_specific_mut,
559            import_coco,
560        );
561        if imported {
562            set_visible(&mut world);
563        }
564        world = check_recolorboxes::<DataAccessors>(world, BRUSH_NAME);
565        world = check_selected_intensity_thickness(world);
566        world = check_export(world);
567        make_tool_transform!(
568            self,
569            world,
570            history,
571            events,
572            [
573                (pressed, KeyCode::MouseLeft, mouse_pressed),
574                (pressed, KeyCode::MouseRight, mouse_pressed),
575                (held, KeyCode::MouseLeft, mouse_held),
576                (held, KeyCode::MouseRight, mouse_held),
577                (released, KeyCode::MouseLeft, mouse_released),
578                (released, KeyCode::Back, key_released),
579                (released, KeyCode::Delete, key_released),
580                (released, KeyCode::A, key_released),
581                (released, KeyCode::C, key_released),
582                (released, KeyCode::D, key_released),
583                (released, KeyCode::E, key_released),
584                (released, KeyCode::H, key_released),
585                (held, KeyCode::I, key_held),
586                (released, KeyCode::L, key_released),
587                (held, KeyCode::T, key_held),
588                (released, KeyCode::V, key_released),
589                (released, KeyCode::Key1, key_released),
590                (released, KeyCode::Key2, key_released),
591                (released, KeyCode::Key3, key_released),
592                (released, KeyCode::Key4, key_released),
593                (released, KeyCode::Key5, key_released),
594                (released, KeyCode::Key6, key_released),
595                (released, KeyCode::Key7, key_released),
596                (released, KeyCode::Key8, key_released),
597                (released, KeyCode::Key9, key_released)
598            ]
599        )
600    }
601}
602
603#[cfg(test)]
604use {
605    crate::{tracing_setup::init_tracing_for_tests, types::ViewImage},
606    image::DynamicImage,
607};
608
609#[cfg(test)]
610pub fn test_data() -> (Option<PtF>, World, History) {
611    use std::path::Path;
612
613    use crate::ToolsDataMap;
614    let im_test = DynamicImage::ImageRgb8(ViewImage::new(64, 64));
615    let mut world = World::from_real_im(
616        im_test,
617        ToolsDataMap::new(),
618        None,
619        Some("superimage.png".to_string()),
620        Path::new("superimage.png"),
621        Some(0),
622    );
623    world.data.meta_data.flags.is_loading_screen_active = Some(false);
624    get_specific_mut(&mut world)
625        .unwrap()
626        .label_info
627        .push("label".to_string(), None, None)
628        .unwrap();
629    let history = History::default();
630    let mouse_pos = Some((32.0, 32.0).into());
631    (mouse_pos, world, history)
632}
633
634#[test]
635fn test_mouse_released() {
636    init_tracing_for_tests();
637    let (mp, mut world, history) = test_data();
638    let options = get_options_mut(&mut world).unwrap();
639    options.thickness = 1.0;
640    let mut events = Events::default();
641    events.mouse_pos_on_orig = mp;
642    let (world, history) = mouse_released(&events, world, history);
643    let annos = get_annos(&world).unwrap();
644    assert_eq!(annos.len(), 1);
645    assert_eq!(annos.elts()[0].bb.x, 32);
646    assert_eq!(annos.elts()[0].bb.y, 32);
647    events.mouse_pos_on_orig = Some((40, 40).into());
648    let world = mouse_pressed_left(&events, world);
649    let (world, history) = mouse_released(&events, world, history);
650    let annos = get_annos(&world).unwrap();
651    assert_eq!(annos.len(), 2);
652    assert_eq!(annos.elts()[0].bb.x, 32);
653    assert_eq!(annos.elts()[0].bb.y, 32);
654    assert_eq!(annos.elts()[1].bb.x, 40);
655    assert_eq!(annos.elts()[1].bb.y, 40);
656    events.mouse_pos_on_orig = Some((10, 10).into());
657    let (world, _) = mouse_released(&events, world, history);
658    let annos = get_annos(&world).unwrap();
659    assert_eq!(annos.len(), 3);
660    assert_eq!(annos.elts()[0].bb.x, 32);
661    assert_eq!(annos.elts()[0].bb.y, 32);
662    assert_eq!(annos.elts()[1].bb.x, 40);
663    assert_eq!(annos.elts()[1].bb.y, 40);
664    assert_eq!(annos.elts()[2].bb.x, 10);
665    assert_eq!(annos.elts()[2].bb.y, 10);
666}