From f8e01107811ddc04265a7a247a7807cd1ba2683a 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] 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 581dd8bdcec9..aa493c64f625 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, rect_in_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); }