use std::collections::BTreeMap;
use ahash::HashMap;
use itertools::Itertools;
use nohash_hasher::IntSet;
use re_arrow_store::{DataStore, LatestAtQuery, Timeline};
use re_data_store::{log_db::EntityDb, query_latest_single, ComponentName, EntityPath};
use re_log_types::{
component_types::{Tensor, TensorTrait},
msg_bundle::Component,
};
use crate::{
misc::{space_info::SpaceInfoCollection, ViewerContext},
ui::{view_category::categorize_entity_path, ViewCategory},
};
use super::{view_category::ViewCategorySet, SpaceView};
pub fn all_possible_space_views(
ctx: &ViewerContext<'_>,
spaces_info: &SpaceInfoCollection,
) -> Vec<SpaceView> {
crate::profile_function!();
let root_children = &ctx.log_db.entity_db.tree.children;
let candidate_space_paths = spaces_info
.iter()
.map(|info| &info.path)
.chain(root_children.values().map(|sub_tree| &sub_tree.path))
.unique();
candidate_space_paths
.flat_map(|candidate_space_path| {
default_queried_entities_by_category(ctx, candidate_space_path, spaces_info)
.iter()
.map(|(category, entity_paths)| {
SpaceView::new(*category, candidate_space_path, entity_paths)
})
.collect::<Vec<_>>()
})
.collect()
}
fn contains_any_image(
entity_path: &EntityPath,
entity_db: &EntityDb,
query: &LatestAtQuery,
) -> bool {
re_query::query_entity_with_primary::<Tensor>(&entity_db.data_store, query, entity_path, &[])
.map_or(false, |entity_view| {
entity_view
.iter_primary_flattened()
.any(|tensor| tensor.is_shaped_like_an_image())
})
}
fn is_interesting_space_view_at_root(
entity_db: &EntityDb,
candidate: &SpaceView,
query: &LatestAtQuery,
) -> bool {
if candidate.data_blueprint.root_group().entities.is_empty() {
return false;
}
for entity_path in &candidate.data_blueprint.root_group().entities {
if contains_any_image(entity_path, entity_db, query) {
return false;
}
}
true
}
fn is_interesting_space_view_not_at_root(
entity_db: &EntityDb,
candidate: &SpaceView,
categories_with_interesting_roots: &ViewCategorySet,
query: &LatestAtQuery,
) -> bool {
if candidate.space_path.len() == 1
&& !categories_with_interesting_roots.contains(candidate.category)
{
return true;
}
if candidate.category == ViewCategory::Spatial {
if let Some(transform) = query_latest_single(entity_db, &candidate.space_path, query) {
match transform {
re_log_types::Transform::Rigid3(_) => {}
re_log_types::Transform::Pinhole(_) | re_log_types::Transform::Unknown => {
return true;
}
}
}
}
false
}
pub fn default_created_space_views(
ctx: &ViewerContext<'_>,
spaces_info: &SpaceInfoCollection,
) -> Vec<SpaceView> {
let candidates = all_possible_space_views(ctx, spaces_info);
default_created_space_views_from_candidates(&ctx.log_db.entity_db, candidates)
}
fn default_created_space_views_from_candidates(
entity_db: &EntityDb,
candidates: Vec<SpaceView>,
) -> Vec<SpaceView> {
crate::profile_function!();
let query = LatestAtQuery::new(Timeline::log_time(), re_arrow_store::TimeInt::MAX);
let categories_with_interesting_roots = candidates
.iter()
.filter_map(|space_view_candidate| {
(space_view_candidate.space_path.is_root()
&& is_interesting_space_view_at_root(entity_db, space_view_candidate, &query))
.then_some(space_view_candidate.category)
})
.collect::<ViewCategorySet>();
let mut space_views = Vec::new();
for candidate in candidates {
if candidate.space_path.is_root() {
if !categories_with_interesting_roots.contains(candidate.category) {
continue;
}
} else if !is_interesting_space_view_not_at_root(
entity_db,
&candidate,
&categories_with_interesting_roots,
&query,
) {
continue;
}
if candidate.category == ViewCategory::Tensor {
for entity_path in candidate.data_blueprint.entity_paths() {
let mut space_view =
SpaceView::new(ViewCategory::Tensor, entity_path, &[entity_path.clone()]);
space_view.entities_determined_by_user = true; space_views.push(space_view);
}
continue;
}
if candidate.category == ViewCategory::Spatial {
let mut images_by_size: HashMap<(u64, u64), Vec<EntityPath>> = HashMap::default();
for entity_path in &candidate.data_blueprint.root_group().entities {
if let Ok(entity_view) = re_query::query_entity_with_primary::<Tensor>(
&entity_db.data_store,
&query,
entity_path,
&[],
) {
for tensor in entity_view.iter_primary_flattened() {
if tensor.is_shaped_like_an_image() {
debug_assert!(matches!(tensor.shape.len(), 2 | 3));
let dim = (tensor.shape[0].size, tensor.shape[1].size);
images_by_size
.entry(dim)
.or_default()
.push(entity_path.clone());
}
}
}
}
if images_by_size.len() > 1 {
for dim in images_by_size.keys() {
let images_of_different_size = images_by_size
.iter()
.filter_map(|(other_dim, images)| (dim != other_dim).then_some(images))
.flatten()
.cloned()
.collect::<IntSet<_>>();
let entities = candidate
.data_blueprint
.entity_paths()
.iter()
.filter(|path| !images_of_different_size.contains(path))
.cloned()
.collect_vec();
let mut space_view =
SpaceView::new(candidate.category, &candidate.space_path, &entities);
space_view.entities_determined_by_user = true; space_views.push(space_view);
}
continue;
}
}
space_views.push(candidate);
}
space_views
}
fn has_any_component_except(
entity_path: &EntityPath,
data_store: &DataStore,
timeline: Timeline,
excluded_components: &[ComponentName],
) -> bool {
data_store
.all_components(&timeline, entity_path)
.map_or(false, |all_components| {
all_components
.iter()
.any(|comp| !excluded_components.contains(comp))
})
}
fn is_default_added_to_space_view(
entity_path: &EntityPath,
space_path: &EntityPath,
data_store: &DataStore,
timeline: Timeline,
) -> bool {
let ignored_components = [
re_log_types::Transform::name(),
re_log_types::ViewCoordinates::name(),
re_log_types::MsgId::name(),
re_log_types::component_types::InstanceKey::name(),
re_log_types::component_types::KeypointId::name(),
DataStore::insert_id_key(),
];
entity_path.is_descendant_of(space_path)
|| (entity_path == space_path
&& has_any_component_except(entity_path, data_store, timeline, &ignored_components))
}
pub fn default_queried_entities(
ctx: &ViewerContext<'_>,
space_path: &EntityPath,
spaces_info: &SpaceInfoCollection,
category: ViewCategory,
) -> Vec<EntityPath> {
crate::profile_function!();
let timeline = Timeline::log_time();
let log_db = &ctx.log_db;
let data_store = &log_db.entity_db.data_store;
let mut entities = Vec::new();
let space_info = spaces_info.get_first_parent_with_info(space_path);
space_info.visit_descendants_with_reachable_transform(spaces_info, &mut |space_info| {
entities.extend(
space_info
.descendants_without_transform
.iter()
.filter(|entity_path| {
is_default_added_to_space_view(entity_path, space_path, data_store, timeline)
&& categorize_entity_path(timeline, log_db, entity_path).contains(category)
})
.cloned(),
);
});
entities
}
fn default_queried_entities_by_category(
ctx: &ViewerContext<'_>,
space_path: &EntityPath,
space_info_collection: &SpaceInfoCollection,
) -> BTreeMap<ViewCategory, Vec<EntityPath>> {
crate::profile_function!();
let timeline = Timeline::log_time();
let log_db = &ctx.log_db;
let data_store = &log_db.entity_db.data_store;
let mut groups: BTreeMap<ViewCategory, Vec<EntityPath>> = BTreeMap::default();
let space_info = space_info_collection.get_first_parent_with_info(space_path);
space_info.visit_descendants_with_reachable_transform(
space_info_collection,
&mut |space_info| {
for entity_path in &space_info.descendants_without_transform {
if is_default_added_to_space_view(entity_path, space_path, data_store, timeline) {
for category in categorize_entity_path(timeline, log_db, entity_path) {
groups
.entry(category)
.or_default()
.push(entity_path.clone());
}
}
}
},
);
groups
}