use egui::NumExt as _;
use nohash_hasher::IntMap;
use re_log_types::EntityPathHash;
use re_viewer_context::VisualizerCollection;
use crate::{view_kind::SpatialSpaceViewKind, visualizers::SpatialViewVisualizerData};
#[derive(Clone)]
pub struct SceneBoundingBoxes {
pub current: re_math::BoundingBox,
pub smoothed: re_math::BoundingBox,
pub per_entity: IntMap<EntityPathHash, re_math::BoundingBox>,
}
impl Default for SceneBoundingBoxes {
fn default() -> Self {
Self {
current: re_math::BoundingBox::NOTHING,
smoothed: re_math::BoundingBox::NOTHING,
per_entity: IntMap::default(),
}
}
}
impl SceneBoundingBoxes {
pub fn update(
&mut self,
ui: &egui::Ui,
visualizers: &VisualizerCollection,
space_kind: SpatialSpaceViewKind,
) {
re_tracing::profile_function!();
let previous = self.current;
self.current = re_math::BoundingBox::NOTHING;
self.per_entity.clear();
for data in visualizers.iter_visualizer_data::<SpatialViewVisualizerData>() {
let data_is_only_2d = data
.preferred_view_kind
.map_or(false, |kind| kind == SpatialSpaceViewKind::TwoD);
if space_kind == SpatialSpaceViewKind::ThreeD && data_is_only_2d {
continue;
}
for (entity, bbox) in &data.bounding_boxes {
self.per_entity
.entry(*entity)
.and_modify(|bbox_entry| *bbox_entry = bbox_entry.union(*bbox))
.or_insert(*bbox);
}
}
for bbox in self.per_entity.values() {
self.current = self.current.union(*bbox);
}
let discontinuity = detect_boundingbox_discontinuity(self.current, previous);
if !self.smoothed.is_finite() || self.smoothed.is_nothing() || discontinuity {
self.smoothed = self.current;
} else {
let dt = ui.input(|input| input.stable_dt.at_most(0.1));
let reach_this_factor = 0.9;
let in_this_many_seconds = 0.2;
let smoothing_factor =
egui::emath::exponential_smooth_factor(reach_this_factor, in_this_many_seconds, dt);
let current_center = self.current.center();
let current_size = self.current.size();
let new_smoothed_center = self
.smoothed
.center()
.lerp(current_center, smoothing_factor);
let new_smoothed_size = self.smoothed.size().lerp(current_size, smoothing_factor);
self.smoothed =
re_math::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.ctx().request_repaint();
}
}
}
}
fn detect_boundingbox_discontinuity(
current: re_math::BoundingBox,
previous: re_math::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
}