diff --git a/crates/viewer/re_view_graph/src/layout/provider.rs b/crates/viewer/re_view_graph/src/layout/provider.rs index d9c718d29146..176f929b19d5 100644 --- a/crates/viewer/re_view_graph/src/layout/provider.rs +++ b/crates/viewer/re_view_graph/src/layout/provider.rs @@ -98,10 +98,15 @@ pub fn update_simulation( } pub struct ForceLayoutProvider { - simulation: fj::Simulation, + // If all nodes are fixed, we can skip the simulation. + simulation: Option, pub request: LayoutRequest, } +fn requires_simulation(request: &LayoutRequest) -> bool { + request.all_nodes().any(|(_, v)| v.fixed_position.is_none()) +} + fn considered_edges(request: &LayoutRequest) -> Vec<(usize, usize)> { let node_index: ahash::HashMap = request .all_nodes() @@ -117,6 +122,13 @@ fn considered_edges(request: &LayoutRequest) -> Vec<(usize, usize)> { impl ForceLayoutProvider { pub fn new(request: LayoutRequest, params: &ForceLayoutParams) -> Self { + if !requires_simulation(&request) { + return Self { + simulation: None, + request, + }; + } + let nodes = request.all_nodes().map(|(_, v)| fj::Node::from(v)); let radii = request .all_nodes() @@ -129,7 +141,7 @@ impl ForceLayoutProvider { let simulation = update_simulation(simulation, params, edges, radii); Self { - simulation, + simulation: Some(simulation), request, } } @@ -139,6 +151,13 @@ impl ForceLayoutProvider { layout: &Layout, params: &ForceLayoutParams, ) -> Self { + if !requires_simulation(&request) { + return Self { + simulation: None, + request, + }; + } + let nodes = request.all_nodes().map(|(id, template)| { let node = fj::Node::from(template); @@ -161,7 +180,7 @@ impl ForceLayoutProvider { let simulation = update_simulation(simulation, params, edges, radii); Self { - simulation, + simulation: Some(simulation), request, } } @@ -169,7 +188,16 @@ impl ForceLayoutProvider { fn layout(&self) -> Layout { // We make use of the fact here that the simulation is stable, i.e. the // order of the nodes is the same as in the `request`. - let mut positions = self.simulation.positions(); + let positions: &mut dyn Iterator = if let Some(simulation) = &self.simulation { + &mut simulation + .positions() + .map(|[x, y]| Pos2::new(x as f32, y as f32)) + } else { + &mut self.request.all_nodes().map(|(_, v)| { + v.fixed_position + .expect("if there is no simulation, all nodes have to be fixed") + }) + }; let mut layout = Layout::empty(); @@ -177,8 +205,7 @@ impl ForceLayoutProvider { let mut current_rect = Rect::NOTHING; for (node, template) in &graph.nodes { - let [x, y] = positions.next().expect("positions has to match the layout"); - let pos = Pos2::new(x as f32, y as f32); + let pos = positions.next().expect("positions has to match the layout"); let extent = Rect::from_center_size(pos, template.size); current_rect = current_rect.union(extent); layout.nodes.insert(*node, extent); @@ -306,12 +333,15 @@ impl ForceLayoutProvider { /// Returns `true` if finished. pub fn tick(&mut self) -> Layout { - self.simulation.tick(1); + if let Some(simulation) = self.simulation.as_mut() { + simulation.tick(1); + } + self.layout() } pub fn is_finished(&self) -> bool { - self.simulation.is_finished() + self.simulation.as_ref().map_or(true, |s| s.is_finished()) } }