Skip to content

Commit

Permalink
Improve VisualBounds2D behavior in graph view (#8438)
Browse files Browse the repository at this point in the history
* Closes #8429 

This implements improved handling of `VisualBounds2D`. Specifically, it:

* Always defaults to the data bounds when user has not modified the view
(@nikolausWest's suggestion).
* Zooms in and out of view when resizing the view (@abey79's
suggestion).
* Fixes the `VisualBounds2D` selection panel bug (submitted by @Wumpf)

---------

Co-authored-by: Andreas Reich <[email protected]>
  • Loading branch information
grtlr and Wumpf authored Dec 13, 2024
1 parent 98152a7 commit a02c262
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 21 deletions.
14 changes: 10 additions & 4 deletions crates/viewer/re_ui/src/zoom_pan_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! * `view`-space: The space where the pan-and-zoom area is drawn.
//! * `scene`-space: The space where the actual content is drawn.
use egui::{emath::TSTransform, Area, Rect, Response, Ui, UiKind};
use egui::{emath::TSTransform, Area, Rect, Response, Ui, UiKind, Vec2};

/// Helper function to handle pan and zoom interactions on a response.
fn register_pan_and_zoom(ui: &Ui, resp: &Response, ui_from_scene: &mut TSTransform) {
Expand All @@ -19,16 +19,22 @@ fn register_pan_and_zoom(ui: &Ui, resp: &Response, ui_from_scene: &mut TSTransfo
let zoom_delta = ui.ctx().input(|i| i.zoom_delta());
let pan_delta = ui.ctx().input(|i| i.smooth_scroll_delta);

// Most of the time we can return early. This is also important to
// avoid `ui_from_scene` to change slightly due to floating point errors.
if zoom_delta == 1.0 && pan_delta == Vec2::ZERO {
return;
}

// Zoom in on pointer, but only if we are not zoomed out too far.
if zoom_delta < 1.0 || ui_from_scene.scaling < 1.0 {
*ui_from_scene = *ui_from_scene
* TSTransform::from_translation(pointer_in_scene.to_vec2())
* TSTransform::from_scaling(zoom_delta)
* TSTransform::from_translation(-pointer_in_scene.to_vec2());
}

// We clamp the resulting scaling to avoid zooming out too far.
ui_from_scene.scaling = ui_from_scene.scaling.min(1.0);
// We clamp the resulting scaling to avoid zooming out too far.
ui_from_scene.scaling = ui_from_scene.scaling.min(1.0);
}

// Pan:
*ui_from_scene = TSTransform::from_translation(pan_delta) * *ui_from_scene;
Expand Down
5 changes: 2 additions & 3 deletions crates/viewer/re_view_graph/src/ui/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use egui::{emath::TSTransform, Rect};
use egui::Rect;
use re_format::format_f32;
use re_types::blueprint::components::VisualBounds2D;
use re_ui::UiExt;
Expand All @@ -16,7 +16,6 @@ pub struct GraphViewState {
pub show_debug: bool,

pub visual_bounds: Option<VisualBounds2D>,
pub ui_from_world: Option<TSTransform>,
pub rect_in_ui: Option<Rect>,
}

Expand All @@ -25,7 +24,7 @@ impl GraphViewState {
let Some(rect) = self.layout_state.bounding_rect() else {
return;
};
ui.grid_left_hand_label("Layout")
ui.grid_left_hand_label("Bounding box")
.on_hover_text("The bounding box encompassing all entities in the view right now");
ui.vertical(|ui| {
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
Expand Down
19 changes: 5 additions & 14 deletions crates/viewer/re_view_graph/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,22 +183,14 @@ Display a graph of nodes and edges.
let layout = state.layout_state.get(request, params);

// Prepare the view and the transformations.
let prev_rect_in_ui = state.rect_in_ui;
let rect_in_ui = *state.rect_in_ui.insert(ui.max_rect());

let ui_from_world = state
.ui_from_world
.get_or_insert_with(|| fit_to_rect_in_scene(rect_in_ui, rect_in_scene.into()));
let mut ui_from_world = fit_to_rect_in_scene(rect_in_ui, rect_in_scene.into());

// We ensure that the view's center is kept during resizing.
if let Some(prev) = prev_rect_in_ui {
if prev != rect_in_ui {
let delta = rect_in_ui.center() - prev.center();
ui_from_world.translation += delta;
}
}
// We store a copy of the transformation to see if it has changed.
let ui_from_world_ref = ui_from_world;

let resp = zoom_pan_area(ui, rect_in_ui, ui_from_world, |ui| {
let resp = zoom_pan_area(ui, rect_in_ui, &mut ui_from_world, |ui| {
let mut world_bounding_rect = egui::Rect::NOTHING;

for graph in &graphs {
Expand All @@ -217,8 +209,7 @@ Display a graph of nodes and edges.
blueprint::components::VisualBounds2D::from(ui_from_world.inverse() * rect_in_ui);
if resp.double_clicked() {
bounds_property.reset_blueprint_component::<blueprint::components::VisualBounds2D>(ctx);
state.ui_from_world = None;
} else if rect_in_scene != updated_rect_in_scene {
} else if ui_from_world != ui_from_world_ref {
bounds_property.save_blueprint_component(ctx, &updated_rect_in_scene);
}
// Update stored bounds on the state, so visualizers see an up-to-date value.
Expand Down

0 comments on commit a02c262

Please sign in to comment.