re_viewer 0.2.0-alpha.0

The Rerun viewer
Documentation
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
use std::collections::BTreeSet;

use nohash_hasher::{IntMap, IntSet};
use re_data_store::{EntityPath, EntityProperties, EntityPropertyMap};
use slotmap::SlotMap;
use smallvec::{smallvec, SmallVec};

slotmap::new_key_type! { pub struct DataBlueprintGroupHandle; }

/// A grouping of several data-blueprints.
#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct DataBlueprintGroup {
    pub display_name: String,

    /// Individual settings. Mutate & display this.
    pub properties_individual: EntityProperties,

    /// Properties, as inherited from parent. Read from this.
    ///
    /// Recalculated at the start of each frame from [`Self::properties_individual`].
    #[cfg_attr(feature = "serde", serde(skip))]
    pub properties_projected: EntityProperties,

    /// Parent of this blueprint group. Every data blueprint except the root has a parent.
    pub parent: DataBlueprintGroupHandle,

    pub children: SmallVec<[DataBlueprintGroupHandle; 4]>,

    /// Direct child entities of this blueprint group.
    ///
    /// Musn't be a `HashSet` because we want to preserve order of entity paths.
    pub entities: BTreeSet<EntityPath>,
}

/// Data blueprints for all entity paths in a space view.
#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
struct DataBlueprints {
    /// Individual settings. Mutate this.
    individual: EntityPropertyMap,

    /// Properties, as inherited from parent. Read from this.
    ///
    /// Recalculated at the start of each frame from [`Self::individual`].
    #[cfg_attr(feature = "serde", serde(skip))]
    projected: EntityPropertyMap,
}

/// Tree of all data blueprint groups for a single space view.
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub struct DataBlueprintTree {
    /// All data blueprint groups.
    groups: SlotMap<DataBlueprintGroupHandle, DataBlueprintGroup>,

    /// Mapping from entity paths to blueprints.
    ///
    /// We also use this for building up groups from hierarchy, meaning that some paths in here
    /// may not represent existing entities, i.e. the blueprint groups they are pointing to may not
    /// necessarily have the respective path as a child.
    path_to_group: IntMap<EntityPath, DataBlueprintGroupHandle>,

    /// List of all entities that we query via this data blueprint collection.
    ///
    /// Two things to keep in sync:
    /// * children on [`DataBlueprintGroup`] this is on
    /// * elements in [`Self::path_to_group`]
    /// TODO(andreas): Can we reduce the amount of these dependencies?
    entity_paths: IntSet<EntityPath>,

    /// Root group, always exists as a placeholder
    root_group_handle: DataBlueprintGroupHandle,

    data_blueprints: DataBlueprints,
}

impl Default for DataBlueprintTree {
    fn default() -> Self {
        let mut groups = SlotMap::default();
        let root_group = groups.insert(DataBlueprintGroup::default());

        let mut path_to_blueprint = IntMap::default();
        path_to_blueprint.insert(EntityPath::root(), root_group);

        Self {
            groups,
            path_to_group: path_to_blueprint,
            entity_paths: IntSet::default(),
            root_group_handle: root_group,
            data_blueprints: DataBlueprints::default(),
        }
    }
}

impl DataBlueprintTree {
    /// Returns a handle to the root data blueprint.
    ///
    /// Even if there are no other groups, we always have a root group at the top.
    /// Typically, we don't show the root group in the ui.
    pub fn root_handle(&self) -> DataBlueprintGroupHandle {
        self.root_group_handle
    }

    /// Returns the root data blueprint.
    ///
    /// Even if there are no other groups, we always have a root group at the top.
    /// Typically, we don't show the root group in the ui.
    pub fn root_group(&self) -> &DataBlueprintGroup {
        self.groups.get(self.root_group_handle).unwrap()
    }

    /// Resolves a data blueprint group handle.
    pub fn group(&self, handle: DataBlueprintGroupHandle) -> Option<&DataBlueprintGroup> {
        self.groups.get(handle)
    }

    /// Resolves a data blueprint group handle.
    pub fn group_mut(
        &mut self,
        handle: DataBlueprintGroupHandle,
    ) -> Option<&mut DataBlueprintGroup> {
        self.groups.get_mut(handle)
    }

    /// Calls the visitor function on every entity path in the given group and its descending groups.
    pub fn visit_group_entities_recursively(
        &self,
        handle: DataBlueprintGroupHandle,
        visitor: &mut impl FnMut(&EntityPath),
    ) {
        let Some(group) = self.groups.get(handle) else {
            return;
        };

        for entity in &group.entities {
            visitor(entity);
        }

        for child in &group.children {
            self.visit_group_entities_recursively(*child, visitor);
        }
    }

    /// Returns entity properties with the hierarchy applied.
    pub fn data_blueprints_projected(&self) -> &EntityPropertyMap {
        &self.data_blueprints.projected
    }

    /// Returns mutable individual entity properties, the hierarchy was not applied to this.
    pub fn data_blueprints_individual(&mut self) -> &mut EntityPropertyMap {
        &mut self.data_blueprints.individual
    }

    pub fn contains_entity(&self, path: &EntityPath) -> bool {
        self.entity_paths.contains(path)
    }

    /// List of all entities that we query via this data blueprint collection.
    pub fn entity_paths(&self) -> &IntSet<EntityPath> {
        &self.entity_paths
    }

    /// Should be called on frame start.
    ///
    /// Propagates any data blueprint changes along the tree.
    pub fn on_frame_start(&mut self) {
        crate::profile_function!();

        // NOTE: We could do this projection only when the entity properties changes
        // and/or when new entity paths are added, but such memoization would add complexity.

        fn project_tree(
            tree: &mut DataBlueprintTree,
            parent_properties: &EntityProperties,
            group_handle: DataBlueprintGroupHandle,
        ) {
            let Some(group) = tree.groups.get_mut(group_handle) else {
                debug_assert!(false, "Invalid group handle in blueprint group tree");
                return;
            };

            let group_properties_projected =
                parent_properties.with_child(&group.properties_individual);
            group.properties_projected = group_properties_projected.clone();

            for entity_path in &group.entities {
                let projected_properties = group_properties_projected
                    .with_child(&tree.data_blueprints.individual.get(entity_path));
                tree.data_blueprints
                    .projected
                    .set(entity_path.clone(), projected_properties);
            }

            let children = group.children.clone(); // TODO(andreas): How to avoid this clone?
            for child in &children {
                project_tree(tree, &group_properties_projected, *child);
            }
        }

        project_tree(self, &EntityProperties::default(), self.root_group_handle);
    }

    /// Adds a list of entity paths to the tree, using grouping as dictated by their entity path hierarchy.
    ///
    /// `base_path` indicates a path at which we short-circuit to the root group.
    ///
    /// Creates a group at *every* step of every path, unless a new group would only contain the entity itself.
    pub fn insert_entities_according_to_hierarchy<'a>(
        &mut self,
        paths: impl Iterator<Item = &'a EntityPath>,
        base_path: &EntityPath,
    ) {
        crate::profile_function!();

        let mut new_leaf_groups = Vec::new();

        for path in paths {
            self.entity_paths.insert(path.clone());

            // Is there already a group associated with this exact path? (maybe because a child was logged there earlier)
            // If so, we can simply move it under this existing group.
            let group_handle = if let Some(group_handle) = self.path_to_group.get(path) {
                *group_handle
            } else if path == base_path {
                // An entity might have directly been logged on the base_path. We map then to the root!
                self.root_group_handle
            } else {
                // Otherwise, create a new group which only contains this entity and add the group to the hierarchy.
                let new_group = self.groups.insert(DataBlueprintGroup {
                    display_name: path_to_group_name(path),
                    ..Default::default()
                });
                self.add_group_to_hierarchy_recursively(new_group, path, base_path);
                new_leaf_groups.push(new_group);
                new_group
            };

            self.add_entity_to_group(group_handle, path);
        }

        // If a leaf group contains only a single element, move that element to the parent and remove the leaf again.
        // (we can't do this as we iterate initially on `paths`, as we don't know if we're data on non-leaf paths until we touched all of them)
        for leaf_group_handle in new_leaf_groups {
            let Some(leaf_group) = self.groups.get_mut(leaf_group_handle) else {
                continue;
            };
            if !leaf_group.children.is_empty() || leaf_group.entities.len() != 1 {
                continue;
            }

            // Remove group.
            let single_entity = leaf_group.entities.iter().next().unwrap().clone();
            let parent_group_handle = leaf_group.parent;
            self.groups.remove(leaf_group_handle);

            // Add entity to its parent and remove the now deleted child.
            let parent_group = self.groups.get_mut(parent_group_handle).unwrap();
            parent_group
                .children
                .retain(|child_group| *child_group != leaf_group_handle);
            parent_group.entities.insert(single_entity.clone());
            self.path_to_group
                .insert(single_entity, parent_group_handle);
        }
    }

    fn add_group_to_hierarchy_recursively(
        &mut self,
        new_group: DataBlueprintGroupHandle,
        associated_path: &EntityPath,
        base_path: &EntityPath,
    ) {
        let Some(mut parent_path) = associated_path.parent() else {
            // Already the root, nothing to do.
            return;
        };

        // Short circuit to the root group at base_path.
        // If the entity is outside of the base path we would walk up all the way to the root
        if &parent_path == base_path {
            parent_path = EntityPath::root();
        }

        let parent_group = match self.path_to_group.entry(parent_path.clone()) {
            std::collections::hash_map::Entry::Occupied(parent_group) => {
                let parent_group = *parent_group.get();
                self.groups
                    .get_mut(parent_group)
                    .unwrap()
                    .children
                    .push(new_group);
                parent_group
            }

            std::collections::hash_map::Entry::Vacant(vacant_mapping) => {
                let parent_group = self.groups.insert(DataBlueprintGroup {
                    display_name: path_to_group_name(&parent_path),
                    children: smallvec![new_group],
                    ..Default::default()
                });
                vacant_mapping.insert(parent_group);
                self.add_group_to_hierarchy_recursively(parent_group, &parent_path, base_path);
                parent_group
            }
        };

        self.groups.get_mut(new_group).unwrap().parent = parent_group;
    }

    /// Adds an entity path to a group.
    ///
    /// If it was already associated with this group, nothing will happen.
    /// If it was already associated with a different group, it will move from there.
    pub fn add_entity_to_group(
        &mut self,
        group_handle: DataBlueprintGroupHandle,
        path: &EntityPath,
    ) {
        if let Some(group) = self.groups.get_mut(group_handle) {
            if !group.entities.insert(path.clone()) {
                // If the entity was already in here it won't be in another group previously.
                return;
            }
        } else {
            return;
        }

        if let Some(previous_group) = self.path_to_group.insert(path.clone(), group_handle) {
            if previous_group != group_handle {
                if let Some(previous_group) = self.groups.get_mut(previous_group) {
                    previous_group.entities.retain(|ent| ent != path);
                }
            }
        }
    }

    /// Removes an entity from the data blueprint collection.
    ///
    /// If the entity was not known by this data blueprint tree nothing happens.
    pub fn remove_entity(&mut self, path: &EntityPath) {
        if let Some(group_handle) = self.path_to_group.get(path) {
            if let Some(group) = self.groups.get_mut(*group_handle) {
                group.entities.remove(path);
                self.remove_group_if_empty(*group_handle);
            }
        }
        self.path_to_group.remove(path);
        self.entity_paths.remove(path);
    }

    /// Removes a group and all its entities and subgroups from the blueprint tree
    pub fn remove_group(&mut self, group_handle: DataBlueprintGroupHandle) {
        crate::profile_function!();

        let Some(group) = self.groups.get(group_handle) else {
            return;
        };

        // Clone group to work around borrow checker issues.
        let group = group.clone();

        // Remove all child groups.
        for child_group in &group.children {
            self.remove_group(*child_group);
        }

        // Remove all child entities.
        for entity_path in &group.entities {
            self.entity_paths.remove(entity_path);
        }

        // Remove from `path_to_group` map.
        // `path_to_group` may map arbitrary paths to this group, some of which aren't in the entity_paths list!
        self.path_to_group
            .retain(|_, group_mapping| *group_mapping != group_handle);

        // Remove group from parent group
        if let Some(parent_group) = self.groups.get_mut(group.parent) {
            parent_group
                .children
                .retain(|child_group| *child_group != group_handle);
        }

        // Never completely remove the root group.
        if group_handle != self.root_group_handle {
            self.groups.remove(group_handle);
        }
    }

    fn remove_group_if_empty(&mut self, group_handle: DataBlueprintGroupHandle) {
        let Some(group) = self.groups.get(group_handle) else {
            return;
        };
        if group.entities.is_empty() && group.children.is_empty() {
            let parent_group_handle = group.parent;
            if let Some(parent_group) = self.groups.get_mut(parent_group_handle) {
                parent_group
                    .children
                    .retain(|child_group| *child_group != group_handle);
                self.remove_group_if_empty(parent_group_handle);
            }
        }
    }
}

fn path_to_group_name(path: &EntityPath) -> String {
    path.iter().last().map_or("/".to_owned(), |c| c.to_string())
}