Skip to main content

re_viewer_context/view/
highlights.rs

1use nohash_hasher::IntMap;
2use re_entity_db::InstancePath;
3use re_log_types::{EntityPathHash, Instance};
4use re_renderer::OutlineMaskPreference;
5use re_sdk_types::blueprint::components::VisualizerInstructionId;
6
7use crate::{HoverHighlight, InteractionHighlight, SelectionHighlight};
8
9/// Highlights of a specific entity path in a specific view.
10///
11/// Using this in bulk on many instances is faster than querying single objects.
12#[derive(Debug)]
13pub struct ViewEntityHighlight {
14    overall: InteractionHighlight,
15    instances: ahash::HashMap<Instance, InteractionHighlight>,
16
17    /// If present, only data from the specified visualizer instruction should be highlighted, otherwise, highlight independently of the visualizer instruction.
18    visualizer_instruction: Option<VisualizerInstructionId>,
19}
20
21impl ViewEntityHighlight {
22    pub fn new(visualizer_instruction: Option<VisualizerInstructionId>) -> Self {
23        Self {
24            overall: InteractionHighlight::default(),
25            instances: ahash::HashMap::default(),
26            visualizer_instruction,
27        }
28    }
29
30    /// Adds a new highlight to the entity highlight, combining it with existing highlights.
31    #[inline]
32    pub fn add(&mut self, instance: &InstancePath, highlight: InteractionHighlight) {
33        let highlight_target = if let Some(selected_index) = instance.instance.specific_index() {
34            self.instances.entry(selected_index).or_default()
35        } else {
36            &mut self.overall
37        };
38        *highlight_target = (*highlight_target).max(highlight);
39    }
40
41    /// Adds a new selection highlight to the entity highlight, combining it with existing highlights.
42    #[inline]
43    pub fn add_selection(&mut self, instance: &InstancePath, selection: SelectionHighlight) {
44        self.add(
45            instance,
46            InteractionHighlight {
47                selection,
48                hover: HoverHighlight::None,
49            },
50        );
51    }
52
53    /// Adds a new hover highlight to the entity highlight, combining it with existing highlights.
54    #[inline]
55    pub fn add_hover(&mut self, instance: &InstancePath, hover: HoverHighlight) {
56        self.add(
57            instance,
58            InteractionHighlight {
59                selection: SelectionHighlight::None,
60                hover,
61            },
62        );
63    }
64}
65
66#[derive(Copy, Clone)]
67pub struct OptionalViewEntityHighlight<'a>(Option<&'a ViewEntityHighlight>);
68
69impl OptionalViewEntityHighlight<'_> {
70    #[inline]
71    pub fn index_highlight(
72        &self,
73        instance: Instance,
74        visualizer: VisualizerInstructionId,
75    ) -> InteractionHighlight {
76        match self.0 {
77            Some(entity_highlight) => {
78                if entity_highlight
79                    .visualizer_instruction
80                    .is_some_and(|v| v != visualizer)
81                {
82                    // This highlight is for a different visualizer instruction, so ignore it.
83                    InteractionHighlight::default()
84                } else {
85                    entity_highlight
86                        .instances
87                        .get(&instance)
88                        .copied()
89                        .unwrap_or_default()
90                        .max(entity_highlight.overall)
91                }
92            }
93            None => InteractionHighlight::default(),
94        }
95    }
96}
97
98#[derive(Default, Debug)]
99pub struct ViewOutlineMasks {
100    pub overall: OutlineMaskPreference,
101    pub instances: ahash::HashMap<Instance, OutlineMaskPreference>,
102}
103
104impl ViewOutlineMasks {
105    pub fn index_outline_mask(&self, instance: Instance) -> OutlineMaskPreference {
106        self.instances
107            .get(&instance)
108            .copied()
109            .unwrap_or_default()
110            .with_fallback_to(self.overall)
111    }
112
113    /// Add a new outline mask to this entity path, combining it with existing masks.
114    pub fn add(&mut self, instance: &InstancePath, preference: OutlineMaskPreference) {
115        let outline_mask_target = if let Some(selected_index) = instance.instance.specific_index() {
116            self.instances.entry(selected_index).or_default()
117        } else {
118            &mut self.overall
119        };
120        *outline_mask_target = preference.with_fallback_to(*outline_mask_target);
121    }
122}
123
124/// Highlights in a specific view.
125///
126/// Using this in bulk on many objects is faster than querying single objects.
127#[derive(Default, Debug)]
128pub struct ViewHighlights {
129    pub highlighted_entity_paths: IntMap<EntityPathHash, ViewEntityHighlight>,
130    pub outlines_masks: IntMap<EntityPathHash, ViewOutlineMasks>,
131}
132
133impl ViewHighlights {
134    #[inline]
135    pub fn entity_highlight(
136        &self,
137        entity_path_hash: EntityPathHash,
138    ) -> OptionalViewEntityHighlight<'_> {
139        OptionalViewEntityHighlight(self.highlighted_entity_paths.get(&entity_path_hash))
140    }
141
142    #[inline]
143    pub fn entity_outline_mask(&self, entity_path_hash: EntityPathHash) -> &ViewOutlineMasks {
144        use std::sync::OnceLock;
145        static CELL: OnceLock<ViewOutlineMasks> = OnceLock::new();
146
147        self.outlines_masks
148            .get(&entity_path_hash)
149            .unwrap_or_else(|| CELL.get_or_init(ViewOutlineMasks::default))
150    }
151
152    #[inline]
153    pub fn any_outlines(&self) -> bool {
154        !self.outlines_masks.is_empty()
155    }
156}