From 977792d6915cb6f67502606c36f39171323528b4 Mon Sep 17 00:00:00 2001 From: Valtteri Vallius <36015558+kaphula@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:25:37 +0200 Subject: [PATCH] Separate graphing data from main BT struct (#32) All data needed to draw graphs is now created on the fly every time when `get_graphviz()` gets called. This should make behavior trees consume less memory and construct faster. Issue: https://github.com/Sollimann/bonsai/issues/29 --- bonsai/Cargo.toml | 2 +- bonsai/src/bt.rs | 27 ++++---- bonsai/src/state.rs | 2 +- bonsai/src/visualizer.rs | 131 ++++++++++++++++++--------------------- 4 files changed, 75 insertions(+), 87 deletions(-) diff --git a/bonsai/Cargo.toml b/bonsai/Cargo.toml index 461a129..0083206 100644 --- a/bonsai/Cargo.toml +++ b/bonsai/Cargo.toml @@ -12,7 +12,7 @@ name = "bonsai-bt" readme = "../README.md" repository = "https://github.com/sollimann/bonsai.git" rust-version = "1.60.0" -version = "0.6.1" +version = "0.6.2" [lib] name = "bonsai_bt" diff --git a/bonsai/src/bt.rs b/bonsai/src/bt.rs index 78694b5..bdd57df 100644 --- a/bonsai/src/bt.rs +++ b/bonsai/src/bt.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use petgraph::dot::{Config, Dot}; -use petgraph::{stable_graph::NodeIndex, Graph}; +use petgraph::Graph; use crate::visualizer::NodeType; use crate::{ActionArgs, Behavior, State, Status, UpdateEvent}; @@ -33,10 +33,6 @@ pub struct BT { initial_behavior: Behavior, /// blackboard bb: BlackBoard, - /// Tree formulated as PetGraph - pub(crate) graph: Graph, u32, petgraph::Directed>, - /// root node - root_id: NodeIndex, } impl BT { @@ -44,16 +40,10 @@ impl BT { let backup_behavior = behavior.clone(); let bt = State::new(behavior); - // generate graph - let mut graph = Graph::, u32, petgraph::Directed>::new(); - let root_id = graph.add_node(NodeType::Root); - Self { state: bt, initial_behavior: backup_behavior, bb: BlackBoard(blackboard), - graph, - root_id, } } @@ -109,10 +99,19 @@ impl BT { /// println!("{}", g); /// ``` pub fn get_graphviz(&mut self) -> String { + self.get_graphviz_with_graph_instance().0 + } + + pub(crate) fn get_graphviz_with_graph_instance(&mut self) -> (String, Graph, u32>) { let behavior = self.initial_behavior.to_owned(); - self.dfs_recursive(behavior, self.root_id); - let digraph = Dot::with_config(&self.graph, &[Config::EdgeNoLabel]); - format!("{:?}", digraph) + + let mut graph = Graph::, u32, petgraph::Directed>::new(); + let root_id = graph.add_node(NodeType::Root); + + Self::dfs_recursive(&mut graph, behavior, root_id); + + let digraph = Dot::with_config(&graph, &[Config::EdgeNoLabel]); + (format!("{:?}", digraph), graph) } /// Retrieve a mutable reference to the blackboard for diff --git a/bonsai/src/state.rs b/bonsai/src/state.rs index eea3e07..6dd1842 100644 --- a/bonsai/src/state.rs +++ b/bonsai/src/state.rs @@ -99,7 +99,7 @@ impl State { Behavior::After(after_all) => State::AfterState(0, after_all.into_iter().map(State::new).collect()), Behavior::RepeatSequence(ev, rep) => { let state = State::new( - rep.get(0) + rep.first() .expect("RepeatSequence's sequence of behaviors to run cannot be empty!") .clone(), ); diff --git a/bonsai/src/visualizer.rs b/bonsai/src/visualizer.rs index 9a4c57e..177d0d3 100644 --- a/bonsai/src/visualizer.rs +++ b/bonsai/src/visualizer.rs @@ -22,103 +22,107 @@ pub(crate) enum NodeType { } impl BT { - pub(crate) fn dfs_recursive(&mut self, behavior: Behavior, parent_node: NodeIndex) { + pub(crate) fn dfs_recursive( + graph: &mut Graph, u32, petgraph::Directed>, + behavior: Behavior, + parent_node: NodeIndex, + ) { match behavior { Behavior::Action(action) => { - let node_id = self.graph.add_node(NodeType::Action(action)); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::Action(action)); + graph.add_edge(parent_node, node_id, 1); } Behavior::Invert(ev) => { - let node_id = self.graph.add_node(NodeType::Invert); - self.graph.add_edge(parent_node, node_id, 1); - self.dfs_recursive(*ev, node_id) + let node_id = graph.add_node(NodeType::Invert); + graph.add_edge(parent_node, node_id, 1); + Self::dfs_recursive(graph, *ev, node_id) } Behavior::AlwaysSucceed(ev) => { - let node_id = self.graph.add_node(NodeType::AlwaysSucceed); - self.graph.add_edge(parent_node, node_id, 1); - self.dfs_recursive(*ev, node_id) + let node_id = graph.add_node(NodeType::AlwaysSucceed); + graph.add_edge(parent_node, node_id, 1); + Self::dfs_recursive(graph, *ev, node_id) } Behavior::Wait(dt) => { - let node_id = self.graph.add_node(NodeType::Wait(dt)); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::Wait(dt)); + graph.add_edge(parent_node, node_id, 1); } Behavior::WaitForever => { - let node_id = self.graph.add_node(NodeType::WaitForever); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::WaitForever); + graph.add_edge(parent_node, node_id, 1); } Behavior::If(condition, success, failure) => { - let node_id = self.graph.add_node(NodeType::If); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::If); + graph.add_edge(parent_node, node_id, 1); // left (if condition) let left = *condition; - self.dfs_recursive(left, node_id); + Self::dfs_recursive(graph, left, node_id); // middle (execute if condition is True) let middle = *success; - self.dfs_recursive(middle, node_id); + Self::dfs_recursive(graph, middle, node_id); // right (execute if condition is False) let right = *failure; - self.dfs_recursive(right, node_id); + Self::dfs_recursive(graph, right, node_id); } Behavior::Select(sel) => { - let node_id = self.graph.add_node(NodeType::Select); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::Select); + graph.add_edge(parent_node, node_id, 1); for b in sel { - self.dfs_recursive(b, node_id) + Self::dfs_recursive(graph, b, node_id) } } Behavior::Sequence(seq) => { - let node_id = self.graph.add_node(NodeType::Sequence); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::Sequence); + graph.add_edge(parent_node, node_id, 1); for b in seq { - self.dfs_recursive(b, node_id) + Self::dfs_recursive(graph, b, node_id) } } Behavior::While(ev, seq) => { - let node_id = self.graph.add_node(NodeType::While); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::While); + graph.add_edge(parent_node, node_id, 1); // left let left = *ev; - self.dfs_recursive(left, node_id); + Self::dfs_recursive(graph, left, node_id); // right let right = Sequence(seq); - self.dfs_recursive(right, node_id) + Self::dfs_recursive(graph, right, node_id) } Behavior::RepeatSequence(ev, seq) => { - let node_id = self.graph.add_node(NodeType::RepeatSequence); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::RepeatSequence); + graph.add_edge(parent_node, node_id, 1); // left let left = *ev; - self.dfs_recursive(left, node_id); + Self::dfs_recursive(graph, left, node_id); // right let right = Sequence(seq); - self.dfs_recursive(right, node_id) + Self::dfs_recursive(graph, right, node_id) } Behavior::WhenAll(all) => { - let node_id = self.graph.add_node(NodeType::WhenAll); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::WhenAll); + graph.add_edge(parent_node, node_id, 1); for b in all { - self.dfs_recursive(b, node_id) + Self::dfs_recursive(graph, b, node_id) } } Behavior::WhenAny(any) => { - let node_id = self.graph.add_node(NodeType::WhenAny); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::WhenAny); + graph.add_edge(parent_node, node_id, 1); for b in any { - self.dfs_recursive(b, node_id) + Self::dfs_recursive(graph, b, node_id) } } Behavior::After(after_all) => { - let node_id = self.graph.add_node(NodeType::After); - self.graph.add_edge(parent_node, node_id, 1); + let node_id = graph.add_node(NodeType::After); + graph.add_edge(parent_node, node_id, 1); for b in after_all { - self.dfs_recursive(b, node_id) + Self::dfs_recursive(graph, b, node_id) } } } @@ -177,8 +181,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -196,8 +199,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -216,8 +218,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -231,8 +232,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -246,8 +246,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -268,8 +267,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -283,8 +281,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -303,8 +300,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -318,8 +314,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -338,8 +333,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -360,8 +354,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); assert_eq!(g.edge_count(), 16); @@ -382,8 +375,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -402,8 +394,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); assert_eq!(g.edge_count(), 7); @@ -421,8 +412,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel])); @@ -441,8 +431,7 @@ mod tests { let h: HashMap = HashMap::new(); let mut bt = BT::new(behavior, h); - bt.get_graphviz(); - let g = bt.graph.clone(); + let (_, g) = bt.get_graphviz_with_graph_instance(); println!("{:?}", Dot::with_config(&g, &[Config::EdgeNoLabel]));