Skip to content

Commit

Permalink
[skip ci] Redo graph viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
grtlr committed Nov 28, 2024
1 parent 2a1535c commit 226804c
Show file tree
Hide file tree
Showing 16 changed files with 335 additions and 311 deletions.
116 changes: 0 additions & 116 deletions crates/viewer/re_space_view_graph/src/canvas.rs

This file was deleted.

83 changes: 83 additions & 0 deletions crates/viewer/re_space_view_graph/src/draw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use egui::{Color32, Painter, Pos2, Rect, Response, Sense, Shape, Stroke, Ui, UiBuilder, Vec2};
use re_viewer_context::InteractionHighlight;

use crate::ui::draw::DrawableLabel;

// Sorry for the pun, could not resist 😎.
// On a serious note, is there no other way to create a `Sense` that does nothing?
const NON_SENSE: Sense = Sense {
click: false,
drag: false,
focusable: false,
};

/// Draws a node at the given position.
pub fn draw_node(
ui: &mut Ui,
center: Pos2,
node: &DrawableLabel,
highlight: InteractionHighlight,
) -> Response {
let builder = UiBuilder::new().max_rect(Rect::from_center_size(center, node.size()));
let mut node_ui = ui.new_child(builder);
node.draw(&mut node_ui, highlight)
}

/// Draws a bounding box, as well as a basic coordinate system.
pub fn draw_debug(ui: &Ui, world_bounding_rect: Rect) {
let painter = ui.painter();

// Paint coordinate system at the world origin
let origin = Pos2::new(0.0, 0.0);
let x_axis = Pos2::new(100.0, 0.0);
let y_axis = Pos2::new(0.0, 100.0);

painter.line_segment([origin, x_axis], Stroke::new(1.0, Color32::RED));
painter.line_segment([origin, y_axis], Stroke::new(1.0, Color32::GREEN));

if world_bounding_rect.is_positive() {
painter.rect(
world_bounding_rect,
0.0,
Color32::from_rgba_unmultiplied(255, 0, 255, 8),
Stroke::new(1.0, Color32::from_rgb(255, 0, 255)),
);
}
}

/// Helper function to draw an arrow at the end of the edge
fn draw_arrow(painter: &Painter, tip: Pos2, direction: Vec2, color: Color32) {
let arrow_size = 10.0; // Adjust size as needed
let perpendicular = Vec2::new(-direction.y, direction.x) * 0.5 * arrow_size;

let p1 = tip - direction * arrow_size + perpendicular;
let p2 = tip - direction * arrow_size - perpendicular;

// Draw a filled triangle for the arrow
painter.add(Shape::convex_polygon(
vec![tip, p1, p2],
color,
Stroke::NONE,
));
}

/// Draws an edge between two points, optionally with an arrow at the target point.
pub fn draw_edge(ui: &mut Ui, points: [Pos2; 2], show_arrow: bool) -> Response {
let fg = ui.style().visuals.text_color();

let rect = Rect::from_points(&points);
let painter = ui.painter();
painter.line_segment(points, Stroke::new(1.0, fg));

// Calculate direction vector from source to target
let direction = (points[1] - points[0]).normalized();

// Conditionally draw an arrow at the target point
if show_arrow {
draw_arrow(painter, points[1], direction, fg);
}

// We can add interactions in the future, for now we simply allocate the
// rect, so that bounding boxes are computed correctly.
ui.allocate_rect(rect, NON_SENSE)
}
4 changes: 2 additions & 2 deletions crates/viewer/re_space_view_graph/src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Node {
pub struct Edge {
pub from: NodeIndex,
pub to: NodeIndex,
marker: bool,
pub arrow: bool,
}

pub struct Graph {
Expand Down Expand Up @@ -119,7 +119,7 @@ impl Graph {
let es = data.edges.iter().map(|e| Edge {
from: e.source_index,
to: e.target_index,
marker: data.graph_type == GraphType::Directed,
arrow: data.graph_type == GraphType::Directed,
});

(es.collect(), data.graph_type)
Expand Down
34 changes: 14 additions & 20 deletions crates/viewer/re_space_view_graph/src/layout/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,27 @@ pub struct Layout {
}

impl Layout {
#[deprecated]
pub fn bounding_rect(&self) -> Rect {
// TODO(grtlr): We mostly use this for debugging, but we should probably
// take all elements of the layout into account.
bounding_rect_from_iter(self.nodes.values().copied())
}

/// Gets the final position and size of a node in the layout.
pub fn get_node(&self, node: &NodeIndex) -> Option<Rect> {
self.nodes.get(node).copied()
///
/// Returns `Rect::ZERO` if the node is not present in the layout.
pub fn get_node(&self, node: &NodeIndex) -> Rect {
self.nodes.get(node).copied().unwrap_or(Rect::ZERO)
}

/// Gets the shape of an edge in the final layout.
pub fn get_edge(&self, from: NodeIndex, to: NodeIndex) -> Option<LineSegment> {
self.edges.get(&(from, to)).copied()
}

/// Updates the size and position of a node, for example after size changes.
/// Returns `true` if the node changed its size.
#[deprecated(note = "We should not need to update sizes anymore.")]
pub fn update(&mut self, node: &NodeIndex, rect: Rect) -> bool {
debug_assert!(
self.nodes.contains_key(node),
"node should exist in the layout"
);
if let Some(extent) = self.nodes.get_mut(node) {
let size_changed = (extent.size() - rect.size()).length_sq() > 0.01;
*extent = rect;
return size_changed;
}
false
///
/// Returns `[Pos2::ZERO, Pos2::ZERO]` if the edge is not present in the layout.
pub fn get_edge(&self, from: NodeIndex, to: NodeIndex) -> LineSegment {
self.edges
.get(&(from, to))
.copied()
.unwrap_or([Pos2::ZERO, Pos2::ZERO])
}
}
Loading

0 comments on commit 226804c

Please sign in to comment.