use egui::NumExt as _;
use nohash_hasher::IntMap;
use re_log_types::EntityPathHash;
use re_viewer_context::VisualizerCollection;
use crate::view_kind::SpatialViewKind;
use crate::visualizers::SpatialViewVisualizerData;
#[derive(Clone)]
pub struct SceneBoundingBoxes {
pub current: macaw::BoundingBox,
pub per_entity: IntMap<EntityPathHash, macaw::BoundingBox>,
pub region_of_interest_current: macaw::BoundingBox,
pub region_of_interest_smoothed: macaw::BoundingBox,
pub region_of_interest_per_entity: IntMap<EntityPathHash, macaw::BoundingBox>,
}
impl Default for SceneBoundingBoxes {
fn default() -> Self {
Self {
current: macaw::BoundingBox::nothing(),
per_entity: IntMap::default(),
region_of_interest_current: macaw::BoundingBox::nothing(),
region_of_interest_smoothed: macaw::BoundingBox::nothing(),
region_of_interest_per_entity: IntMap::default(),
}
}
}
impl SceneBoundingBoxes {
pub fn update(
&mut self,
ui: &egui::Ui,
visualizers: &VisualizerCollection,
space_kind: SpatialViewKind,
) {
re_tracing::profile_function!();
let previous_region_of_interest = self.region_of_interest_current;
self.current = macaw::BoundingBox::nothing();
self.per_entity.clear();
self.region_of_interest_current = macaw::BoundingBox::nothing();
self.region_of_interest_per_entity.clear();
for data in visualizers.iter_visualizer_data::<SpatialViewVisualizerData>() {
let data_is_only_2d = data
.preferred_view_kind
.is_some_and(|kind| kind == SpatialViewKind::TwoD);
if space_kind == SpatialViewKind::ThreeD && data_is_only_2d {
continue;
}
for (entity, bbox) in data.iter_bounding_boxes() {
self.per_entity
.entry(*entity)
.and_modify(|bbox_entry| *bbox_entry = bbox_entry.union(*bbox))
.or_insert(*bbox);
}
for (entity, region_of_interest) in data.iter_regions_of_interest() {
self.region_of_interest_per_entity
.entry(*entity)
.and_modify(|entry| *entry = entry.union(*region_of_interest))
.or_insert(*region_of_interest);
}
}
self.current = self
.per_entity
.values()
.copied()
.fold(macaw::BoundingBox::nothing(), macaw::BoundingBox::union);
self.region_of_interest_current = self
.region_of_interest_per_entity
.values()
.copied()
.fold(macaw::BoundingBox::nothing(), macaw::BoundingBox::union);
let discontinuity =
detect_discontinuity(self.region_of_interest_current, previous_region_of_interest);
if !self.region_of_interest_smoothed.is_finite()
|| self.region_of_interest_smoothed.is_nothing()
|| discontinuity
{
self.region_of_interest_smoothed = self.region_of_interest_current;
} else {
let dt = ui.input(|input| input.stable_dt.at_most(0.1));
let reach_this_factor = 0.9;
let in_this_many_secs = 0.2;
let smoothing_factor =
egui::emath::exponential_smooth_factor(reach_this_factor, in_this_many_secs, dt);
let current_center = self.region_of_interest_current.center();
let current_size = self.region_of_interest_current.size();
let new_smoothed_center = self
.region_of_interest_smoothed
.center()
.lerp(current_center, smoothing_factor);
let new_smoothed_size = self
.region_of_interest_smoothed
.size()
.lerp(current_size, smoothing_factor);
self.region_of_interest_smoothed =
macaw::BoundingBox::from_center_size(new_smoothed_center, new_smoothed_size);
let current_diagonal_length = current_size.length();
let sameness_threshold = current_diagonal_length * (0.1 / 100.0); if new_smoothed_center.distance(current_center) > sameness_threshold
|| (new_smoothed_size.length() - current_diagonal_length) / current_diagonal_length
> sameness_threshold
{
ui.request_repaint();
}
}
}
}
fn detect_discontinuity(current: macaw::BoundingBox, previous: macaw::BoundingBox) -> bool {
if !previous.is_finite() {
return true;
}
let current_size = current.size().length();
let previous_size = previous.size().length();
let size_change = (current_size - previous_size).abs();
let size_change_ratio = size_change / previous_size;
if size_change_ratio > 0.5 {
return true;
}
let current_center = current.center();
let previous_center = previous.center();
let center_change = current_center.distance(previous_center);
let center_change_ratio = center_change / previous_size;
if center_change_ratio > 0.5 {
return true;
}
false
}