Skip to content

Commit 0e9d221

Browse files
authored
Add support for Bezier-curve multi (self-)edges (#8256)
### Related <!-- Include links to any related issues/PRs in a bulleted list, for example: * Closes #1234 * Part of #1337 --> * Closes: #8238 ### What This PR brings self-edges in the form of Bezier-curves. <img width="230" alt="image" src="https://github.com/user-attachments/assets/85304534-891b-415d-ab8e-3006afe73058"> It also tries to make multiple edges between the same node more legible by applying a small arc on such edges to prevent overlap. <!-- Make sure the PR title and labels are set to maximize their usefulness for the CHANGELOG, and our `git log`. If you have noticed any breaking changes, include them in the migration guide. We track various metrics at <https://build.rerun.io>. For maintainers: * To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. * To deploy documentation changes immediately after merging this PR, add the `deploy docs` label. -->
1 parent 312f91c commit 0e9d221

File tree

14 files changed

+570
-223
lines changed

14 files changed

+570
-223
lines changed

crates/viewer/re_space_view_graph/src/graph/index.rs crates/viewer/re_space_view_graph/src/graph/ids.rs

+20
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,23 @@ impl std::fmt::Debug for NodeId {
3434
write!(f, "NodeIndex({:?}@{:?})", self.node_hash, self.entity_hash)
3535
}
3636
}
37+
38+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
39+
pub struct EdgeId {
40+
// TODO(grtlr): Consider something more storage efficient here
41+
pub source: NodeId,
42+
pub target: NodeId,
43+
}
44+
45+
impl EdgeId {
46+
pub fn self_edge(node: NodeId) -> Self {
47+
Self {
48+
source: node,
49+
target: node,
50+
}
51+
}
52+
53+
pub fn is_self_edge(&self) -> bool {
54+
self.source == self.target
55+
}
56+
}

crates/viewer/re_space_view_graph/src/graph/mod.rs

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
//! Provides a basic abstraction over a graph that was logged to an entity.
2+
3+
// For now this is pretty basic, but in the future we might replace this with
4+
// something like `petgraph`, which will unlock more user interactions, such as
5+
// highlighting of neighboring nodes and edges.
6+
17
mod hash;
28

39
use egui::{Pos2, Vec2};
410
pub(crate) use hash::GraphNodeHash;
5-
mod index;
6-
pub(crate) use index::NodeId;
11+
mod ids;
12+
pub(crate) use ids::{EdgeId, NodeId};
713

814
use re_chunk::EntityPath;
915
use re_types::components::{self, GraphType};
1016

1117
use crate::{
18+
layout::EdgeTemplate,
1219
ui::DrawableLabel,
1320
visualizers::{EdgeData, NodeData, NodeInstance},
1421
};
@@ -70,16 +77,10 @@ impl Node {
7077
}
7178
}
7279

73-
pub struct Edge {
74-
pub from: NodeId,
75-
pub to: NodeId,
76-
pub arrow: bool,
77-
}
78-
7980
pub struct Graph {
8081
entity: EntityPath,
8182
nodes: Vec<Node>,
82-
edges: Vec<Edge>,
83+
edges: Vec<EdgeTemplate>,
8384
kind: GraphType,
8485
}
8586

@@ -127,10 +128,10 @@ impl Graph {
127128
}
128129
}
129130

130-
let es = data.edges.iter().map(|e| Edge {
131-
from: e.source_index,
132-
to: e.target_index,
133-
arrow: data.graph_type == GraphType::Directed,
131+
let es = data.edges.iter().map(|e| EdgeTemplate {
132+
source: e.source_index,
133+
target: e.target_index,
134+
target_arrow: data.graph_type == GraphType::Directed,
134135
});
135136

136137
(es.collect(), data.graph_type)
@@ -150,7 +151,7 @@ impl Graph {
150151
&self.nodes
151152
}
152153

153-
pub fn edges(&self) -> &[Edge] {
154+
pub fn edges(&self) -> &[EdgeTemplate] {
154155
&self.edges
155156
}
156157

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Provides geometric (shape) abstractions for the different elements of a graph layout.
2+
3+
use egui::{Pos2, Rect, Vec2};
4+
5+
#[derive(Clone, Debug)]
6+
pub enum PathGeometry {
7+
/// A simple straight edge.
8+
Line { source: Pos2, target: Pos2 },
9+
10+
/// Represents a cubic bezier curve.
11+
///
12+
/// In the future we could probably support more complex splines.
13+
CubicBezier {
14+
source: Pos2,
15+
target: Pos2,
16+
control: [Pos2; 2],
17+
},
18+
// We could add other geometries, such as `Orthogonal` here too.
19+
}
20+
21+
#[derive(Debug)]
22+
pub struct EdgeGeometry {
23+
pub target_arrow: bool,
24+
pub path: PathGeometry,
25+
}
26+
27+
impl EdgeGeometry {
28+
pub fn bounding_rect(&self) -> Rect {
29+
match self.path {
30+
PathGeometry::Line { source, target } => Rect::from_two_pos(source, target),
31+
// TODO(grtlr): This is just a crude (upper) approximation, as the resulting bounding box can be too large.
32+
// For now this is fine, as there are no interactions on edges yet.
33+
PathGeometry::CubicBezier {
34+
source,
35+
target,
36+
ref control,
37+
} => Rect::from_points(&[&[source, target], control.as_slice()].concat()),
38+
}
39+
}
40+
41+
/// The starting position of an edge.
42+
pub fn source_pos(&self) -> Pos2 {
43+
match self.path {
44+
PathGeometry::Line { source, .. } | PathGeometry::CubicBezier { source, .. } => source,
45+
}
46+
}
47+
48+
/// The end position of an edge.
49+
pub fn target_pos(&self) -> Pos2 {
50+
match self.path {
51+
PathGeometry::Line { target, .. } | PathGeometry::CubicBezier { target, .. } => target,
52+
}
53+
}
54+
55+
/// The direction of the edge at the source node (normalized).
56+
pub fn source_arrow_direction(&self) -> Vec2 {
57+
use PathGeometry::{CubicBezier, Line};
58+
match self.path {
59+
Line { source, target } => (source.to_vec2() - target.to_vec2()).normalized(),
60+
CubicBezier {
61+
source, control, ..
62+
} => (control[0].to_vec2() - source.to_vec2()).normalized(),
63+
}
64+
}
65+
66+
/// The direction of the edge at the target node (normalized).
67+
pub fn target_arrow_direction(&self) -> Vec2 {
68+
use PathGeometry::{CubicBezier, Line};
69+
match self.path {
70+
Line { source, target } => (target.to_vec2() - source.to_vec2()).normalized(),
71+
CubicBezier {
72+
target, control, ..
73+
} => (target.to_vec2() - control[1].to_vec2()).normalized(),
74+
}
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
mod geometry;
12
mod provider;
23
mod request;
34
mod result;
5+
mod slots;
46

7+
pub use geometry::{EdgeGeometry, PathGeometry};
58
pub use provider::ForceLayoutProvider;
6-
pub use request::LayoutRequest;
9+
pub use request::{EdgeTemplate, LayoutRequest};
710
pub use result::Layout;

0 commit comments

Comments
 (0)