From 38737f4d3205c63368ed1cbdaf8858cc8c1923fb Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 18 Jul 2023 12:14:26 +0200 Subject: [PATCH] move object property heuristics to heuristics.rs --- .../re_space_view_spatial/src/heuristics.rs | 173 +++++++++++++++++ .../src/space_view_2d.rs | 5 +- .../src/space_view_3d.rs | 5 +- crates/re_space_view_spatial/src/ui.rs | 183 +----------------- 4 files changed, 183 insertions(+), 183 deletions(-) diff --git a/crates/re_space_view_spatial/src/heuristics.rs b/crates/re_space_view_spatial/src/heuristics.rs index acc820e9b49d..70c743a82893 100644 --- a/crates/re_space_view_spatial/src/heuristics.rs +++ b/crates/re_space_view_spatial/src/heuristics.rs @@ -1,4 +1,8 @@ +use egui::NumExt as _; use nohash_hasher::IntSet; + +use re_components::{Pinhole, Tensor, TensorDataMeaning, Transform3D}; +use re_data_store::EditableAutoValue; use re_log_types::{EntityPath, Timeline}; use re_viewer_context::{AutoSpawnHeuristic, SpaceViewClassName, ViewerContext}; @@ -45,3 +49,172 @@ pub fn auto_spawn_heuristic( AutoSpawnHeuristic::SpawnClassWithHighestScoreForRoot(score) } + +pub fn update_object_property_heuristics( + ctx: &mut ViewerContext<'_>, + ent_paths: &IntSet, + entity_properties: &mut re_data_store::EntityPropertyMap, + scene_bbox_accum: &macaw::BoundingBox, + spatial_kind: SpatialSpaceViewKind, +) { + re_tracing::profile_function!(); + + for entity_path in ent_paths { + update_pinhole_property_heuristics(ctx, entity_path, entity_properties, scene_bbox_accum); + update_depth_cloud_property_heuristics(ctx, entity_path, entity_properties, spatial_kind); + update_transform3d_lines_heuristics(ctx, entity_path, entity_properties, scene_bbox_accum); + } +} + +pub fn auto_size_world_heuristic( + scene_bbox_accum: &macaw::BoundingBox, + scene_num_primitives: usize, +) -> f32 { + if scene_bbox_accum.is_nothing() || scene_bbox_accum.is_nan() { + return 0.01; + } + + // Motivation: Size should be proportional to the scene extent, here covered by its diagonal + let diagonal_length = (scene_bbox_accum.max - scene_bbox_accum.min).length(); + let heuristic0 = diagonal_length * 0.0025; + + // Motivation: A lot of times we look at the entire scene and expect to see everything on a flat screen with some gaps between. + let size = scene_bbox_accum.size(); + let mut sorted_components = size.to_array(); + sorted_components.sort_by(|a, b| a.partial_cmp(b).unwrap()); + // Median is more robust against outlier in one direction (as such pretty poor still though) + let median_extent = sorted_components[1]; + // sqrt would make more sense but using a smaller root works better in practice. + let heuristic1 = + (median_extent / (scene_num_primitives.at_least(1) as f32).powf(1.0 / 1.7)) * 0.25; + + heuristic0.min(heuristic1) +} + +fn update_pinhole_property_heuristics( + ctx: &mut ViewerContext<'_>, + entity_path: &EntityPath, + entity_properties: &mut re_data_store::EntityPropertyMap, + scene_bbox_accum: &macaw::BoundingBox, +) { + let store = &ctx.store_db.entity_db.data_store; + if store + .query_latest_component::(entity_path, &ctx.current_query()) + .is_some() + { + let mut properties = entity_properties.get(entity_path); + if properties.pinhole_image_plane_distance.is_auto() { + let scene_size = scene_bbox_accum.size().length(); + let default_image_plane_distance = if scene_size.is_finite() && scene_size > 0.0 { + scene_size * 0.02 // Works pretty well for `examples/python/open_photogrammetry_format/main.py --no-frames` + } else { + 1.0 + }; + properties.pinhole_image_plane_distance = + EditableAutoValue::Auto(default_image_plane_distance); + entity_properties.set(entity_path.clone(), properties); + } + } +} + +fn update_depth_cloud_property_heuristics( + ctx: &mut ViewerContext<'_>, + entity_path: &EntityPath, + entity_properties: &mut re_data_store::EntityPropertyMap, + spatial_kind: SpatialSpaceViewKind, +) -> Option<()> { + let store = &ctx.store_db.entity_db.data_store; + let tensor = store.query_latest_component::(entity_path, &ctx.current_query())?; + + let mut properties = entity_properties.get(entity_path); + if properties.backproject_depth.is_auto() { + properties.backproject_depth = EditableAutoValue::Auto( + tensor.meaning == TensorDataMeaning::Depth + && spatial_kind == SpatialSpaceViewKind::ThreeD, + ); + } + + if tensor.meaning == TensorDataMeaning::Depth { + if properties.depth_from_world_scale.is_auto() { + let auto = tensor.meter.unwrap_or_else(|| { + if tensor.dtype().is_integer() { + 1000.0 + } else { + 1.0 + } + }); + properties.depth_from_world_scale = EditableAutoValue::Auto(auto); + } + + if properties.backproject_radius_scale.is_auto() { + properties.backproject_radius_scale = EditableAutoValue::Auto(1.0); + } + + entity_properties.set(entity_path.clone(), properties); + } + + Some(()) +} + +fn update_transform3d_lines_heuristics( + ctx: &ViewerContext<'_>, + ent_path: &EntityPath, + entity_properties: &mut re_data_store::EntityPropertyMap, + scene_bbox_accum: &macaw::BoundingBox, +) { + if ctx + .store_db + .store() + .query_latest_component::(ent_path, &ctx.current_query()) + .is_none() + { + return; + } + + let mut properties = entity_properties.get(ent_path); + if properties.transform_3d_visible.is_auto() { + fn path_or_child_has_pinhole( + store: &re_arrow_store::DataStore, + ent_path: &EntityPath, + ctx: &ViewerContext<'_>, + ) -> bool { + if store + .query_latest_component::(ent_path, &ctx.current_query()) + .is_some() + { + return true; + } else { + // Any direct child has a pinhole camera? + if let Some(child_tree) = ctx.store_db.entity_db.tree.subtree(ent_path) { + for child in child_tree.children.values() { + if store + .query_latest_component::(&child.path, &ctx.current_query()) + .is_some() + { + return true; + } + } + } + } + + false + } + // By default show the transform if it is a camera extrinsic or if it's the only component on this entity path. + let single_component = ctx + .store_db + .store() + .all_components(&ctx.current_query().timeline, ent_path) + .map_or(false, |c| c.len() == 1); + properties.transform_3d_visible = EditableAutoValue::Auto( + single_component || path_or_child_has_pinhole(ctx.store_db.store(), ent_path, ctx), + ); + } + + if properties.transform_3d_size.is_auto() { + // Size should be proportional to the scene extent, here covered by its diagonal + let diagonal_length = (scene_bbox_accum.max - scene_bbox_accum.min).length(); + properties.transform_3d_size = EditableAutoValue::Auto(diagonal_length * 0.01); + } + + entity_properties.set(ent_path.clone(), properties); +} diff --git a/crates/re_space_view_spatial/src/space_view_2d.rs b/crates/re_space_view_spatial/src/space_view_2d.rs index a97947007f6b..5de863a1bd2a 100644 --- a/crates/re_space_view_spatial/src/space_view_2d.rs +++ b/crates/re_space_view_spatial/src/space_view_2d.rs @@ -8,7 +8,7 @@ use re_viewer_context::{ use crate::{ contexts::{register_contexts, PrimitiveCounter}, - heuristics::auto_spawn_heuristic, + heuristics::{auto_spawn_heuristic, update_object_property_heuristics}, parts::{calculate_bounding_box, register_parts}, ui::SpatialSpaceViewState, view_kind::SpatialSpaceViewKind, @@ -57,10 +57,11 @@ impl SpaceViewClass for SpatialSpaceView2D { ent_paths: &IntSet, entity_properties: &mut re_data_store::EntityPropertyMap, ) { - state.update_object_property_heuristics( + update_object_property_heuristics( ctx, ent_paths, entity_properties, + &state.scene_bbox_accum, SpatialSpaceViewKind::TwoD, ); } diff --git a/crates/re_space_view_spatial/src/space_view_3d.rs b/crates/re_space_view_spatial/src/space_view_3d.rs index 17110b08bb01..be28de75fa01 100644 --- a/crates/re_space_view_spatial/src/space_view_3d.rs +++ b/crates/re_space_view_spatial/src/space_view_3d.rs @@ -10,7 +10,7 @@ use re_viewer_context::{ use crate::{ contexts::{register_contexts, PrimitiveCounter}, - heuristics::auto_spawn_heuristic, + heuristics::{auto_spawn_heuristic, update_object_property_heuristics}, parts::{calculate_bounding_box, register_parts}, ui::SpatialSpaceViewState, view_kind::SpatialSpaceViewKind, @@ -93,10 +93,11 @@ impl SpaceViewClass for SpatialSpaceView3D { ent_paths: &IntSet, entity_properties: &mut re_data_store::EntityPropertyMap, ) { - state.update_object_property_heuristics( + update_object_property_heuristics( ctx, ent_paths, entity_properties, + &state.scene_bbox_accum, SpatialSpaceViewKind::ThreeD, ); } diff --git a/crates/re_space_view_spatial/src/ui.rs b/crates/re_space_view_spatial/src/ui.rs index c21ae0c3b15c..2e168b7cfa5f 100644 --- a/crates/re_space_view_spatial/src/ui.rs +++ b/crates/re_space_view_spatial/src/ui.rs @@ -2,9 +2,8 @@ use eframe::epaint::text::TextWrapping; use egui::{NumExt, WidgetText}; use macaw::BoundingBox; -use nohash_hasher::IntSet; -use re_components::{Pinhole, Tensor, TensorDataMeaning, Transform3D}; -use re_data_store::{EditableAutoValue, EntityPath}; +use re_components::{Tensor, TensorDataMeaning}; +use re_data_store::EntityPath; use re_data_ui::{item_ui, DataUi}; use re_data_ui::{show_zoomed_image_region, show_zoomed_image_region_area_outline}; use re_format::format_f32; @@ -18,6 +17,7 @@ use re_viewer_context::{ }; use super::{eye::Eye, ui_2d::View2DState, ui_3d::View3DState}; +use crate::heuristics::auto_size_world_heuristic; use crate::{ contexts::{AnnotationSceneContext, NonInteractiveEntities}, parts::{CamerasPart, ImagesPart, UiLabel, UiLabelTarget}, @@ -111,181 +111,6 @@ impl SpatialSpaceViewState { config } - // TODO(andreas): Move to heuristics.rs - pub fn update_object_property_heuristics( - &self, - ctx: &mut ViewerContext<'_>, - ent_paths: &IntSet, - entity_properties: &mut re_data_store::EntityPropertyMap, - spatial_kind: SpatialSpaceViewKind, - ) { - re_tracing::profile_function!(); - - for entity_path in ent_paths { - self.update_pinhole_property_heuristics(ctx, entity_path, entity_properties); - Self::update_depth_cloud_property_heuristics( - ctx, - entity_path, - entity_properties, - spatial_kind, - ); - self.update_transform3d_lines_heuristics(ctx, entity_path, entity_properties); - } - } - - fn auto_size_world_heuristic(&self) -> f32 { - if self.scene_bbox_accum.is_nothing() || self.scene_bbox_accum.is_nan() { - return 0.01; - } - - // Motivation: Size should be proportional to the scene extent, here covered by its diagonal - let diagonal_length = (self.scene_bbox_accum.max - self.scene_bbox_accum.min).length(); - let heuristic0 = diagonal_length * 0.0025; - - // Motivation: A lot of times we look at the entire scene and expect to see everything on a flat screen with some gaps between. - let size = self.scene_bbox_accum.size(); - let mut sorted_components = size.to_array(); - sorted_components.sort_by(|a, b| a.partial_cmp(b).unwrap()); - // Median is more robust against outlier in one direction (as such pretty poor still though) - let median_extent = sorted_components[1]; - // sqrt would make more sense but using a smaller root works better in practice. - let heuristic1 = - (median_extent / (self.scene_num_primitives.at_least(1) as f32).powf(1.0 / 1.7)) * 0.25; - - heuristic0.min(heuristic1) - } - - fn update_pinhole_property_heuristics( - &self, - ctx: &mut ViewerContext<'_>, - entity_path: &EntityPath, - entity_properties: &mut re_data_store::EntityPropertyMap, - ) { - let store = &ctx.store_db.entity_db.data_store; - if store - .query_latest_component::(entity_path, &ctx.current_query()) - .is_some() - { - let mut properties = entity_properties.get(entity_path); - if properties.pinhole_image_plane_distance.is_auto() { - let scene_size = self.scene_bbox_accum.size().length(); - let default_image_plane_distance = if scene_size.is_finite() && scene_size > 0.0 { - scene_size * 0.02 // Works pretty well for `examples/python/open_photogrammetry_format/main.py --no-frames` - } else { - 1.0 - }; - properties.pinhole_image_plane_distance = - EditableAutoValue::Auto(default_image_plane_distance); - entity_properties.set(entity_path.clone(), properties); - } - } - } - - fn update_depth_cloud_property_heuristics( - ctx: &mut ViewerContext<'_>, - entity_path: &EntityPath, - entity_properties: &mut re_data_store::EntityPropertyMap, - spatial_kind: SpatialSpaceViewKind, - ) -> Option<()> { - let store = &ctx.store_db.entity_db.data_store; - let tensor = store.query_latest_component::(entity_path, &ctx.current_query())?; - - let mut properties = entity_properties.get(entity_path); - if properties.backproject_depth.is_auto() { - properties.backproject_depth = EditableAutoValue::Auto( - tensor.meaning == TensorDataMeaning::Depth - && spatial_kind == SpatialSpaceViewKind::ThreeD, - ); - } - - if tensor.meaning == TensorDataMeaning::Depth { - if properties.depth_from_world_scale.is_auto() { - let auto = tensor.meter.unwrap_or_else(|| { - if tensor.dtype().is_integer() { - 1000.0 - } else { - 1.0 - } - }); - properties.depth_from_world_scale = EditableAutoValue::Auto(auto); - } - - if properties.backproject_radius_scale.is_auto() { - properties.backproject_radius_scale = EditableAutoValue::Auto(1.0); - } - - entity_properties.set(entity_path.clone(), properties); - } - - Some(()) - } - - fn update_transform3d_lines_heuristics( - &self, - ctx: &ViewerContext<'_>, - ent_path: &EntityPath, - entity_properties: &mut re_data_store::EntityPropertyMap, - ) { - if ctx - .store_db - .store() - .query_latest_component::(ent_path, &ctx.current_query()) - .is_none() - { - return; - } - - let mut properties = entity_properties.get(ent_path); - if properties.transform_3d_visible.is_auto() { - fn path_or_child_has_pinhole( - store: &re_arrow_store::DataStore, - ent_path: &EntityPath, - ctx: &ViewerContext<'_>, - ) -> bool { - if store - .query_latest_component::(ent_path, &ctx.current_query()) - .is_some() - { - return true; - } else { - // Any direct child has a pinhole camera? - if let Some(child_tree) = ctx.store_db.entity_db.tree.subtree(ent_path) { - for child in child_tree.children.values() { - if store - .query_latest_component::( - &child.path, - &ctx.current_query(), - ) - .is_some() - { - return true; - } - } - } - } - - false - } - // By default show the transform if it is a camera extrinsic or if it's the only component on this entity path. - let single_component = ctx - .store_db - .store() - .all_components(&ctx.current_query().timeline, ent_path) - .map_or(false, |c| c.len() == 1); - properties.transform_3d_visible = EditableAutoValue::Auto( - single_component || path_or_child_has_pinhole(ctx.store_db.store(), ent_path, ctx), - ); - } - - if properties.transform_3d_size.is_auto() { - // Size should be proportional to the scene extent, here covered by its diagonal - let diagonal_length = (self.scene_bbox_accum.max - self.scene_bbox_accum.min).length(); - properties.transform_3d_size = EditableAutoValue::Auto(diagonal_length * 0.01); - } - - entity_properties.set(ent_path.clone(), properties); - } - pub fn selection_ui( &mut self, ctx: &mut ViewerContext<'_>, @@ -301,7 +126,7 @@ impl SpatialSpaceViewState { ctx.re_ui.selection_grid(ui, "spatial_settings_ui") .show(ui, |ui| { - let auto_size_world = self.auto_size_world_heuristic(); + let auto_size_world = auto_size_world_heuristic(&self.scene_bbox_accum, self.scene_num_primitives); ctx.re_ui.grid_left_hand_label(ui, "Space origin") .on_hover_text("The origin is at the origin of this Entity. All transforms are relative to it");