From b186ea45ad8036d394b3a3c777e2d6ebb5afdc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Fri, 13 Dec 2024 11:09:11 +0100 Subject: [PATCH 1/6] Implement level-of-detail on text labels --- crates/viewer/re_view_graph/src/ui/draw.rs | 72 ++++++++++++++++++++-- crates/viewer/re_view_graph/src/ui/mod.rs | 2 +- crates/viewer/re_view_graph/src/view.rs | 6 +- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index 0916e1026372..e13555b9b1ed 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -35,6 +35,7 @@ impl DrawableLabel { } pub struct TextLabel { + color: Option, frame: Frame, galley: Arc, } @@ -44,11 +45,27 @@ pub struct CircleLabel { color: Option, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum LevelOfDetail { + Full, + Low, +} + +impl LevelOfDetail { + pub fn from_scaling(zoom: f32) -> Self { + if zoom < 0.20 { + Self::Low + } else { + Self::Full + } + } +} + impl DrawableLabel { pub fn size(&self) -> Vec2 { match self { Self::Circle(CircleLabel { radius, .. }) => Vec2::splat(radius * 2.0), - Self::Text(TextLabel { galley, frame }) => { + Self::Text(TextLabel { galley, frame, .. }) => { frame.inner_margin.sum() + galley.size() + Vec2::splat(frame.stroke.width * 2.0) } } @@ -82,7 +99,11 @@ impl DrawableLabel { .fill(ui.style().visuals.widgets.noninteractive.bg_fill) .stroke(Stroke::new(1.0, ui.style().visuals.text_color())); - Self::Text(TextLabel { frame, galley }) + Self::Text(TextLabel { + frame, + galley, + color, + }) } } @@ -115,7 +136,7 @@ fn draw_circle_label( } fn draw_text_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlight) -> Response { - let TextLabel { galley, frame } = label; + let TextLabel { galley, frame, .. } = label; let visuals = &ui.style().visuals; let bg = match highlight.hover { @@ -137,12 +158,47 @@ fn draw_text_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlig .inner } +fn draw_rect_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlight) -> Response { + let TextLabel { + galley, + frame, + color, + } = label; + let visuals = &ui.style().visuals; + + let bg = match highlight.hover { + HoverHighlight::None => visuals.widgets.noninteractive.bg_fill, + HoverHighlight::Hovered => visuals.widgets.hovered.bg_fill, + }; + + let stroke = match highlight.selection { + SelectionHighlight::Selection => visuals.selection.stroke, + _ => Stroke::new(1.0, visuals.text_color()), + }; + + // We use `gamma` to correct for the fact that text is not completely solid. + let fill_color = color + .unwrap_or_else(|| visuals.text_color()) + .gamma_multiply(0.5); + + frame + .stroke(stroke) + .fill(bg) + .show(ui, |ui| { + let (resp, painter) = ui.allocate_painter(galley.rect.size(), Sense::click()); + painter.rect_filled(resp.rect, 0.0, fill_color); + resp + }) + .inner +} + /// Draws a node at the given position. fn draw_node( ui: &mut Ui, center: Pos2, node: &DrawableLabel, highlight: InteractionHighlight, + lod: LevelOfDetail, ) -> Response { let builder = UiBuilder::new() .max_rect(Rect::from_center_size(center, node.size())) @@ -152,7 +208,10 @@ fn draw_node( match node { DrawableLabel::Circle(label) => draw_circle_label(&mut node_ui, label, highlight), - DrawableLabel::Text(label) => draw_text_label(&mut node_ui, label, highlight), + DrawableLabel::Text(label) if lod == LevelOfDetail::Full => { + draw_text_label(&mut node_ui, label, highlight) + } + DrawableLabel::Text(label) => draw_rect_label(&mut node_ui, label, highlight), }; node_ui.response() @@ -281,6 +340,7 @@ pub fn draw_graph( graph: &Graph, layout: &Layout, query: &ViewQuery<'_>, + lod: LevelOfDetail, ) -> Rect { let entity_path = graph.entity(); let entity_highlights = query.highlights.entity_highlight(entity_path.hash()); @@ -294,7 +354,7 @@ pub fn draw_graph( let response = match node { Node::Explicit { instance, .. } => { let highlight = entity_highlights.index_highlight(instance.instance_index); - let mut response = draw_node(ui, center, node.label(), highlight); + let mut response = draw_node(ui, center, node.label(), highlight, lod); let instance_path = InstancePath::instance(entity_path.clone(), instance.instance_index); @@ -322,7 +382,7 @@ pub fn draw_graph( response } Node::Implicit { graph_node, .. } => { - draw_node(ui, center, node.label(), Default::default()).on_hover_text(format!( + draw_node(ui, center, node.label(), Default::default(), lod).on_hover_text(format!( "Implicit node {} created via a reference in a GraphEdge component", graph_node.as_str(), )) diff --git a/crates/viewer/re_view_graph/src/ui/mod.rs b/crates/viewer/re_view_graph/src/ui/mod.rs index 4e00cdfe7388..ef5e09d555fa 100644 --- a/crates/viewer/re_view_graph/src/ui/mod.rs +++ b/crates/viewer/re_view_graph/src/ui/mod.rs @@ -2,6 +2,6 @@ mod draw; mod selection; mod state; -pub use draw::{draw_debug, draw_graph, DrawableLabel}; +pub use draw::{draw_debug, draw_graph, DrawableLabel, LevelOfDetail}; pub use selection::view_property_force_ui; pub use state::GraphViewState; diff --git a/crates/viewer/re_view_graph/src/view.rs b/crates/viewer/re_view_graph/src/view.rs index edb59eceb27d..b2dfdc54d7db 100644 --- a/crates/viewer/re_view_graph/src/view.rs +++ b/crates/viewer/re_view_graph/src/view.rs @@ -28,7 +28,7 @@ use re_viewport_blueprint::ViewProperty; use crate::{ graph::Graph, layout::{ForceLayoutParams, LayoutRequest}, - ui::{draw_debug, draw_graph, view_property_force_ui, GraphViewState}, + ui::{draw_debug, draw_graph, view_property_force_ui, GraphViewState, LevelOfDetail}, visualizers::{merge, EdgesVisualizer, NodeVisualizer}, }; @@ -190,11 +190,13 @@ Display a graph of nodes and edges. // We store a copy of the transformation to see if it has changed. let ui_from_world_ref = ui_from_world; + let level_of_detail = LevelOfDetail::from_scaling(ui_from_world.scaling); + let resp = zoom_pan_area(ui, &mut ui_from_world, |ui| { let mut world_bounding_rect = egui::Rect::NOTHING; for graph in &graphs { - let graph_rect = draw_graph(ui, ctx, graph, layout, query); + let graph_rect = draw_graph(ui, ctx, graph, layout, query, level_of_detail); world_bounding_rect = world_bounding_rect.union(graph_rect); } From 1f2f81d42ebc81ebcad4a38336fef50a9abc5fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Fri, 13 Dec 2024 11:37:39 +0100 Subject: [PATCH 2/6] Draw solid boxes instead of stroked rectangles --- crates/viewer/re_view_graph/src/ui/draw.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index e13555b9b1ed..6fa183dd5042 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -181,15 +181,18 @@ fn draw_rect_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlig .unwrap_or_else(|| visuals.text_color()) .gamma_multiply(0.5); - frame - .stroke(stroke) - .fill(bg) - .show(ui, |ui| { - let (resp, painter) = ui.allocate_painter(galley.rect.size(), Sense::click()); - painter.rect_filled(resp.rect, 0.0, fill_color); - resp - }) - .inner + let total_size = galley.rect.size() + frame.inner_margin.sum() + frame.outer_margin.sum(); + + let (resp, painter) = ui.allocate_painter(total_size, Sense::click()); + painter.rect_filled(resp.rect, 0.0, stroke.color.gamma_multiply(0.5)); + painter.rect_filled( + resp.rect + .shrink2(frame.inner_margin.sum() + frame.outer_margin.sum()), + 0.0, + fill_color, + ); + + resp } /// Draws a node at the given position. From 813ad4844421a47027822e117b3a97837decf4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Mon, 16 Dec 2024 10:53:43 +0100 Subject: [PATCH 3/6] Revert "Draw solid boxes instead of stroked rectangles" This reverts commit 50ed438184a80545cd7aaaf2048c261c8aef6751. --- crates/viewer/re_view_graph/src/ui/draw.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index 6fa183dd5042..e13555b9b1ed 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -181,18 +181,15 @@ fn draw_rect_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlig .unwrap_or_else(|| visuals.text_color()) .gamma_multiply(0.5); - let total_size = galley.rect.size() + frame.inner_margin.sum() + frame.outer_margin.sum(); - - let (resp, painter) = ui.allocate_painter(total_size, Sense::click()); - painter.rect_filled(resp.rect, 0.0, stroke.color.gamma_multiply(0.5)); - painter.rect_filled( - resp.rect - .shrink2(frame.inner_margin.sum() + frame.outer_margin.sum()), - 0.0, - fill_color, - ); - - resp + frame + .stroke(stroke) + .fill(bg) + .show(ui, |ui| { + let (resp, painter) = ui.allocate_painter(galley.rect.size(), Sense::click()); + painter.rect_filled(resp.rect, 0.0, fill_color); + resp + }) + .inner } /// Draws a node at the given position. From b7b8b17f5198c7f7f268f20db9b629263d6df501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Mon, 16 Dec 2024 11:04:19 +0100 Subject: [PATCH 4/6] Update crates/viewer/re_view_graph/src/ui/draw.rs Co-authored-by: Antoine Beyeler <49431240+abey79@users.noreply.github.com> --- crates/viewer/re_view_graph/src/ui/draw.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index e13555b9b1ed..4c65360093dd 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -158,6 +158,7 @@ fn draw_text_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlig .inner } +/// Draw a rectangle to "fake" a label at small scales, where actual text would be unreadable anyways. fn draw_rect_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlight) -> Response { let TextLabel { galley, From 336fc3fdee7b46b517dc0f223f6cfad2d9e5535d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Mon, 16 Dec 2024 11:04:26 +0100 Subject: [PATCH 5/6] Update crates/viewer/re_view_graph/src/ui/draw.rs Co-authored-by: Antoine Beyeler <49431240+abey79@users.noreply.github.com> --- crates/viewer/re_view_graph/src/ui/draw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index 4c65360093dd..febea6adc6bc 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -165,7 +165,7 @@ fn draw_rect_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlig frame, color, } = label; - let visuals = &ui.style().visuals; + let visuals = ui.visuals(); let bg = match highlight.hover { HoverHighlight::None => visuals.widgets.noninteractive.bg_fill, From 6b0e47d4b5023c6476f3a2d3ab82ee2b5cf6783d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Mon, 16 Dec 2024 11:06:17 +0100 Subject: [PATCH 6/6] Stylistic --- crates/viewer/re_view_graph/src/ui/draw.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_view_graph/src/ui/draw.rs b/crates/viewer/re_view_graph/src/ui/draw.rs index febea6adc6bc..2b226f4041ca 100644 --- a/crates/viewer/re_view_graph/src/ui/draw.rs +++ b/crates/viewer/re_view_graph/src/ui/draw.rs @@ -209,10 +209,13 @@ fn draw_node( match node { DrawableLabel::Circle(label) => draw_circle_label(&mut node_ui, label, highlight), - DrawableLabel::Text(label) if lod == LevelOfDetail::Full => { - draw_text_label(&mut node_ui, label, highlight) + DrawableLabel::Text(label) => { + if lod == LevelOfDetail::Full { + draw_text_label(&mut node_ui, label, highlight) + } else { + draw_rect_label(&mut node_ui, label, highlight) + } } - DrawableLabel::Text(label) => draw_rect_label(&mut node_ui, label, highlight), }; node_ui.response()