-
Notifications
You must be signed in to change notification settings - Fork 366
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplify graph viewer rendering logic (#8227)
### Related * This is one of the big action items of #7897 <!-- Include links to any related issues/PRs in a bulleted list, for example: * Closes #1234 * Part of #1337 --> ### What <!-- 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. --> It changes two things: 1. Instead of using multiple `egui::Areas`, we now only have a single top-level area. 2. We compute the sizes of the nodes (information that is important to the layout) in a single preprocessing path instead of the weird frame-to-frame computation that we did before. Overall this will improve code quality, performance, and will unlock new GraphViz-like layout algorithms (which require defined label size from the beginning) down the road. --------- Co-authored-by: Antoine Beyeler <antoine@rerun.io>
- Loading branch information
Showing
23 changed files
with
966 additions
and
865 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,164 @@ | ||
mod hash; | ||
|
||
use egui::{Pos2, Vec2}; | ||
pub(crate) use hash::GraphNodeHash; | ||
mod index; | ||
pub(crate) use index::NodeIndex; | ||
pub(crate) use index::NodeId; | ||
|
||
use re_chunk::EntityPath; | ||
use re_types::components::{self, GraphType}; | ||
|
||
use re_types::components::{GraphNode, GraphType}; | ||
use crate::{ | ||
ui::DrawableLabel, | ||
visualizers::{EdgeData, NodeData, NodeInstance}, | ||
}; | ||
|
||
use crate::visualizers::{EdgeData, EdgeInstance, NodeData, NodeInstance}; | ||
/// Describes the different kind of nodes that we can have in a graph. | ||
pub enum Node { | ||
/// An explicit node is a node that was provided via [`re_types::archetypes::GraphNodes`]. | ||
/// | ||
/// It therefore has an instance, as well as all properties that can be added via that archetype. | ||
Explicit { | ||
instance: NodeInstance, | ||
label: DrawableLabel, | ||
}, | ||
|
||
pub struct NodeInstanceImplicit { | ||
pub node: GraphNode, | ||
pub index: NodeIndex, | ||
/// An implicit node is a node that was provided via [`re_types::archetypes::GraphEdges`], but does not have a corresponding [`re_types::components::GraphNode`] in an [`re_types::archetypes::GraphNodes`] archetype. | ||
/// | ||
/// Because it was never specified directly, it also does not have many of the properties that an [`Node::Explicit`] has. | ||
Implicit { | ||
id: NodeId, | ||
graph_node: components::GraphNode, | ||
label: DrawableLabel, | ||
}, | ||
} | ||
|
||
impl std::hash::Hash for NodeInstanceImplicit { | ||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||
self.index.hash(state); | ||
impl Node { | ||
pub fn id(&self) -> NodeId { | ||
match self { | ||
Self::Explicit { instance, .. } => instance.id, | ||
Self::Implicit { id, .. } => *id, | ||
} | ||
} | ||
|
||
/// The original [`components::GraphNode`] id that was logged by the user. | ||
pub fn graph_node(&self) -> &components::GraphNode { | ||
match self { | ||
Self::Explicit { instance, .. } => &instance.graph_node, | ||
Self::Implicit { graph_node, .. } => graph_node, | ||
} | ||
} | ||
|
||
pub fn label(&self) -> &DrawableLabel { | ||
match self { | ||
Self::Explicit { label, .. } | Self::Implicit { label, .. } => label, | ||
} | ||
} | ||
|
||
pub fn size(&self) -> Vec2 { | ||
self.label().size() | ||
} | ||
|
||
pub fn position(&self) -> Option<Pos2> { | ||
match self { | ||
Self::Explicit { | ||
instance: NodeInstance { position, .. }, | ||
.. | ||
} => *position, | ||
Self::Implicit { .. } => None, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Hash)] | ||
pub struct Graph<'a> { | ||
explicit: &'a [NodeInstance], | ||
implicit: Vec<NodeInstanceImplicit>, | ||
edges: &'a [EdgeInstance], | ||
pub struct Edge { | ||
pub from: NodeId, | ||
pub to: NodeId, | ||
pub arrow: bool, | ||
} | ||
|
||
pub struct Graph { | ||
entity: EntityPath, | ||
nodes: Vec<Node>, | ||
edges: Vec<Edge>, | ||
kind: GraphType, | ||
} | ||
|
||
impl<'a> Graph<'a> { | ||
pub fn new(node_data: Option<&'a NodeData>, edge_data: Option<&'a EdgeData>) -> Self { | ||
impl Graph { | ||
pub fn new<'a>( | ||
ui: &egui::Ui, | ||
entity: EntityPath, | ||
node_data: Option<&'a NodeData>, | ||
edge_data: Option<&'a EdgeData>, | ||
) -> Self { | ||
// We keep track of the nodes to find implicit nodes. | ||
let mut seen = ahash::HashSet::default(); | ||
|
||
let explicit = if let Some(data) = node_data { | ||
seen.extend(data.nodes.iter().map(|n| n.index)); | ||
data.nodes.as_slice() | ||
let mut nodes: Vec<Node> = if let Some(data) = node_data { | ||
seen.extend(data.nodes.iter().map(|n| n.id)); | ||
// TODO(grtlr): We should see if we can get rid of some of the cloning here. | ||
data.nodes | ||
.iter() | ||
.map(|n| Node::Explicit { | ||
instance: n.clone(), | ||
label: DrawableLabel::from_label(ui, &n.label), | ||
}) | ||
.collect() | ||
} else { | ||
&[][..] | ||
Vec::new() | ||
}; | ||
|
||
let (edges, implicit, kind) = if let Some(data) = edge_data { | ||
let mut implicit = Vec::new(); | ||
let (edges, kind) = if let Some(data) = edge_data { | ||
for edge in &data.edges { | ||
if !seen.contains(&edge.source_index) { | ||
implicit.push(NodeInstanceImplicit { | ||
node: edge.source.clone(), | ||
index: edge.source_index, | ||
nodes.push(Node::Implicit { | ||
id: edge.source_index, | ||
graph_node: edge.source.clone(), | ||
label: DrawableLabel::implicit_circle(), | ||
}); | ||
seen.insert(edge.source_index); | ||
} | ||
if !seen.contains(&edge.target_index) { | ||
implicit.push(NodeInstanceImplicit { | ||
node: edge.target.clone(), | ||
index: edge.target_index, | ||
nodes.push(Node::Implicit { | ||
id: edge.target_index, | ||
graph_node: edge.target.clone(), | ||
label: DrawableLabel::implicit_circle(), | ||
}); | ||
seen.insert(edge.target_index); | ||
} | ||
} | ||
(data.edges.as_slice(), implicit, Some(data.graph_type)) | ||
|
||
let es = data.edges.iter().map(|e| Edge { | ||
from: e.source_index, | ||
to: e.target_index, | ||
arrow: data.graph_type == GraphType::Directed, | ||
}); | ||
|
||
(es.collect(), data.graph_type) | ||
} else { | ||
(&[][..], Vec::new(), None) | ||
(Vec::new(), GraphType::default()) | ||
}; | ||
|
||
Self { | ||
explicit, | ||
implicit, | ||
entity, | ||
nodes, | ||
edges, | ||
kind: kind.unwrap_or_default(), | ||
kind, | ||
} | ||
} | ||
|
||
pub fn nodes_explicit(&self) -> impl Iterator<Item = &NodeInstance> { | ||
self.explicit.iter() | ||
} | ||
|
||
pub fn nodes_implicit(&self) -> impl Iterator<Item = &NodeInstanceImplicit> + '_ { | ||
self.implicit.iter() | ||
pub fn nodes(&self) -> &[Node] { | ||
&self.nodes | ||
} | ||
|
||
pub fn edges(&self) -> impl Iterator<Item = &EdgeInstance> { | ||
self.edges.iter() | ||
pub fn edges(&self) -> &[Edge] { | ||
&self.edges | ||
} | ||
|
||
pub fn kind(&self) -> GraphType { | ||
self.kind | ||
} | ||
|
||
pub fn entity(&self) -> &EntityPath { | ||
&self.entity | ||
} | ||
} |
Oops, something went wrong.