1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use nohash_hasher::IntMap;

use re_entity_db::InstancePath;
use re_log_types::{EntityPathHash, Instance};
use re_renderer::OutlineMaskPreference;

use crate::{HoverHighlight, InteractionHighlight, SelectionHighlight};

/// Highlights of a specific entity path in a specific space view.
///
/// Using this in bulk on many instances is faster than querying single objects.
#[derive(Default)]
pub struct SpaceViewEntityHighlight {
    overall: InteractionHighlight,
    instances: ahash::HashMap<Instance, InteractionHighlight>,
}

impl SpaceViewEntityHighlight {
    /// Adds a new highlight to the entity highlight, combining it with existing highlights.
    #[inline]
    pub fn add(&mut self, instance: &InstancePath, highlight: InteractionHighlight) {
        let highlight_target = if let Some(selected_index) = instance.instance.specific_index() {
            self.instances.entry(selected_index).or_default()
        } else {
            &mut self.overall
        };
        *highlight_target = (*highlight_target).max(highlight);
    }

    /// Adds a new selection highlight to the entity highlight, combining it with existing highlights.
    #[inline]
    pub fn add_selection(&mut self, instance: &InstancePath, selection: SelectionHighlight) {
        self.add(
            instance,
            InteractionHighlight {
                selection,
                hover: HoverHighlight::None,
            },
        );
    }

    /// Adds a new hover highlight to the entity highlight, combining it with existing highlights.
    #[inline]
    pub fn add_hover(&mut self, instance: &InstancePath, hover: HoverHighlight) {
        self.add(
            instance,
            InteractionHighlight {
                selection: SelectionHighlight::None,
                hover,
            },
        );
    }
}

#[derive(Copy, Clone)]
pub struct OptionalSpaceViewEntityHighlight<'a>(Option<&'a SpaceViewEntityHighlight>);

impl<'a> OptionalSpaceViewEntityHighlight<'a> {
    #[inline]
    pub fn index_highlight(&self, instance: Instance) -> InteractionHighlight {
        match self.0 {
            Some(entity_highlight) => entity_highlight
                .instances
                .get(&instance)
                .copied()
                .unwrap_or_default()
                .max(entity_highlight.overall),
            None => InteractionHighlight::default(),
        }
    }
}

#[derive(Default)]
pub struct SpaceViewOutlineMasks {
    pub overall: OutlineMaskPreference,
    pub instances: ahash::HashMap<Instance, OutlineMaskPreference>,
}

impl SpaceViewOutlineMasks {
    pub fn index_outline_mask(&self, instance: Instance) -> OutlineMaskPreference {
        self.instances
            .get(&instance)
            .copied()
            .unwrap_or_default()
            .with_fallback_to(self.overall)
    }

    /// Add a new outline mask to this entity path, combining it with existing masks.
    pub fn add(&mut self, instance: &InstancePath, preference: OutlineMaskPreference) {
        let outline_mask_target = if let Some(selected_index) = instance.instance.specific_index() {
            self.instances.entry(selected_index).or_default()
        } else {
            &mut self.overall
        };
        *outline_mask_target = preference.with_fallback_to(*outline_mask_target);
    }
}

/// Highlights in a specific space view.
///
/// Using this in bulk on many objects is faster than querying single objects.
#[derive(Default)]
pub struct SpaceViewHighlights {
    pub highlighted_entity_paths: IntMap<EntityPathHash, SpaceViewEntityHighlight>,
    pub outlines_masks: IntMap<EntityPathHash, SpaceViewOutlineMasks>,
}

impl SpaceViewHighlights {
    #[inline]
    pub fn entity_highlight(
        &self,
        entity_path_hash: EntityPathHash,
    ) -> OptionalSpaceViewEntityHighlight<'_> {
        OptionalSpaceViewEntityHighlight(self.highlighted_entity_paths.get(&entity_path_hash))
    }

    #[inline]
    pub fn entity_outline_mask(&self, entity_path_hash: EntityPathHash) -> &SpaceViewOutlineMasks {
        use std::sync::OnceLock;
        static CELL: OnceLock<SpaceViewOutlineMasks> = OnceLock::new();

        self.outlines_masks
            .get(&entity_path_hash)
            .unwrap_or_else(|| CELL.get_or_init(SpaceViewOutlineMasks::default))
    }

    #[inline]
    pub fn any_outlines(&self) -> bool {
        !self.outlines_masks.is_empty()
    }
}