From 13142ee2102053d43c748213a4310d24ffacc60e Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 17 Sep 2022 03:20:34 -0600 Subject: [PATCH] Ramen command pattern --- ramen/Cargo.toml | 3 +- ramen/src/command.rs | 126 +++++++++++++++++++++++++++++++++++++++++++ ramen/src/main.rs | 117 ++++++++++++++++++++++++++-------------- 3 files changed, 205 insertions(+), 41 deletions(-) create mode 100644 ramen/src/command.rs diff --git a/ramen/Cargo.toml b/ramen/Cargo.toml index 83309cf..524a34f 100644 --- a/ramen/Cargo.toml +++ b/ramen/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] - +glam = { version = "0.20", features = ["serde"] } +slab = "^0.4" diff --git a/ramen/src/command.rs b/ramen/src/command.rs new file mode 100644 index 0000000..89263f5 --- /dev/null +++ b/ramen/src/command.rs @@ -0,0 +1,126 @@ +use super::{Graph, GraphError, GraphResult, Node, NodeKind}; + +pub trait Command { + fn undo(&self, graph: &Graph) -> GraphResult; + fn apply(&self, graph: &mut Graph) -> GraphResult<()>; +} + +pub type DynCommand = Box; + +#[derive(Clone, Debug)] +pub struct CreateNode { + pub kind: NodeKind, + pub pos: glam::Vec2, + pub inputs_num: usize, +} + +impl Command for CreateNode { + fn undo(&self, graph: &Graph) -> GraphResult { + let target = graph.nodes.vacant_key(); + let undo = DeleteNode { target }; + Ok(Box::new(undo)) + } + + fn apply(&self, graph: &mut Graph) -> GraphResult<()> { + let node = Node { + kind: self.kind, + pos: self.pos, + inputs: vec![0; self.inputs_num], + }; + + graph.nodes.insert(node); + + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct DeleteNode { + pub target: usize, +} + +impl Command for DeleteNode { + fn undo(&self, graph: &Graph) -> GraphResult { + let target = graph.get_node(self.target)?; + + let undo = CreateNode { + kind: target.kind, + pos: target.pos, + inputs_num: target.inputs.len(), + }; + + Ok(Box::new(undo)) + } + + fn apply(&self, graph: &mut Graph) -> GraphResult<()> { + graph + .nodes + .try_remove(self.target) + .ok_or(GraphError::InvalidReference) + .map(|_| ()) + } +} + +#[derive(Clone, Debug)] +pub struct MoveNode { + pub target: usize, + pub to: glam::Vec2, +} + +impl Command for MoveNode { + fn undo(&self, graph: &Graph) -> GraphResult { + let target = graph.get_node(self.target)?; + + let undo = Self { + target: self.target, + to: target.pos, + }; + + Ok(Box::new(undo)) + } + + fn apply(&self, graph: &mut Graph) -> GraphResult<()> { + let target = graph.get_node_mut(self.target)?; + target.pos = self.to; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct SetEdge { + pub target_node: usize, + pub slot: usize, + pub input_node: usize, +} + +impl Command for SetEdge { + fn undo(&self, graph: &Graph) -> GraphResult { + let target = graph.get_node(self.target_node)?; + + let old_input = target + .inputs + .get(self.slot) + .ok_or(GraphError::InvalidReference)?; + + let undo = Self { + target_node: self.target_node, + slot: self.slot, + input_node: *old_input, + }; + + Ok(Box::new(undo)) + } + + fn apply(&self, graph: &mut Graph) -> GraphResult<()> { + let target = graph.get_node_mut(self.target_node)?; + + let input = target + .inputs + .get_mut(self.slot) + .ok_or(GraphError::InvalidReference)?; + + *input = self.input_node; + + Ok(()) + } +} diff --git a/ramen/src/main.rs b/ramen/src/main.rs index c6593b8..5a49003 100644 --- a/ramen/src/main.rs +++ b/ramen/src/main.rs @@ -1,53 +1,74 @@ +use slab::Slab; + +pub mod command; + fn main() { + // Create a dummy node position for testing + let pos = glam::Vec2::ZERO; + // Represent the graph as adjacency lists - let mut graph = Graph { + let graph: Graph = // (1 + 2) / 4 - nodes: vec![ - Node { kind: NodeKind::Lit(1.0), inputs: vec![] }, - Node { kind: NodeKind::Lit(2.0), inputs: vec![] }, - Node { kind: NodeKind::Op(AtomOp::Add), inputs: vec![0, 1] }, - Node { kind: NodeKind::Lit(4.0), inputs: vec![] }, - Node { kind: NodeKind::Op(AtomOp::Div), inputs: vec![2, 3] }, - Node { kind: NodeKind::Op(AtomOp::Mul), inputs: vec![4, 4] }, - Node { kind: NodeKind::Op(AtomOp::Add), inputs: vec![5, 0] }, - Node { kind: NodeKind::Lit(0.0), inputs: vec![] }, // Should be ignored since it's not used + vec![ + Node { pos, kind: NodeKind::Lit(1.0), inputs: vec![] }, + Node { pos, kind: NodeKind::Lit(2.0), inputs: vec![] }, + Node { pos, kind: NodeKind::Op(AtomOp::Add), inputs: vec![0, 1] }, + Node { pos, kind: NodeKind::Lit(4.0), inputs: vec![] }, + Node { pos, kind: NodeKind::Op(AtomOp::Div), inputs: vec![2, 3] }, + Node { pos, kind: NodeKind::Op(AtomOp::Mul), inputs: vec![4, 4] }, + Node { pos, kind: NodeKind::Op(AtomOp::Add), inputs: vec![5, 0] }, + Node { pos, kind: NodeKind::Lit(0.0), inputs: vec![] }, // Should be ignored since it's not used ] - }; + .into(); graph.compile().expect("Compilation failure"); } #[derive(Debug, Clone)] -struct Node { - kind: NodeKind, - inputs: Vec, +pub struct Node { + pub kind: NodeKind, + pub pos: glam::Vec2, + pub inputs: Vec, } -#[derive(Debug, Clone)] -enum NodeKind { +#[derive(Copy, Debug, Clone)] +pub enum NodeKind { Lit(f32), - Op(AtomOp) + Op(AtomOp), } #[derive(Debug)] -enum GraphError { - Cyclic +pub enum GraphError { + InvalidReference, + Cyclic, } -#[derive(Debug)] -struct Graph { - nodes: Vec, -} +pub type GraphResult = Result; -#[derive(Debug, Clone)] -enum AtomOp { +#[derive(Copy, Debug, Clone)] +pub enum AtomOp { Add, Sub, Mul, Div, } +#[derive(Debug)] +pub struct Graph { + pub nodes: Slab, +} + impl Graph { + fn get_node(&self, index: usize) -> GraphResult<&Node> { + self.nodes.get(index).ok_or(GraphError::InvalidReference) + } + + fn get_node_mut(&mut self, index: usize) -> GraphResult<&mut Node> { + self.nodes + .get_mut(index) + .ok_or(GraphError::InvalidReference) + } + fn compile(&self) -> Result<(), GraphError> { // TODO: Only compile the nodes that are actually used @@ -58,7 +79,7 @@ impl Graph { NodeKind::Lit(x) => { // println!("let n{}: f32 = {:?};", key, x); - }, + } NodeKind::Op(op) => { let symbol = match op { AtomOp::Add => '+', @@ -66,35 +87,38 @@ impl Graph { AtomOp::Mul => '*', AtomOp::Div => '/', }; - println!("let n{}: f32 = n{} {} n{};", key, &node.inputs[0], symbol, &node.inputs[1]); - }, + println!( + "let n{}: f32 = n{} {} n{};", + key, &node.inputs[0], symbol, &node.inputs[1] + ); + } } } Ok(()) } - + // Return the edges that can be derived from the graph fn edges(&self) -> Result, GraphError> { let topo_order = self.topological_order()?; - + let mut edges: Vec<(usize, usize)> = Vec::new(); for (i, node) in topo_order.iter().enumerate() { for j in &self.nodes[*node].inputs { edges.push((*j, i)); } } - + Ok(edges) } - + fn topological_order(&self) -> Result, GraphError> { // The number of nodes let v = self.nodes.len(); // Forward-directed adjacency lists let mut adjacency_lists: Vec> = vec![Vec::new(); v]; - for (i, node) in self.nodes.iter().enumerate() { + for (i, node) in self.nodes.iter() { for j in &node.inputs { adjacency_lists[*j].push(i); } @@ -102,10 +126,10 @@ impl Graph { // Create list of indegrees let mut indegrees: Vec = vec![0; v]; - for (i, node) in self.nodes.iter().enumerate() { + for (i, node) in self.nodes.iter() { indegrees[i] = node.inputs.len(); } - + // Create a queue and initialize it with nodes with no indegrees let mut queue: Vec = Vec::new(); for (i, count) in indegrees.iter().enumerate() { @@ -113,24 +137,24 @@ impl Graph { queue.push(i); } } - + let mut sort_count = 0; let mut topo_order: Vec = Vec::new(); - + while !queue.is_empty() { let u = queue.pop().unwrap(); topo_order.push(u); - + for i in &adjacency_lists[u] { indegrees[*i] -= 1; if indegrees[*i] == 0 { queue.push(*i); } } - + sort_count += 1; } - + // Check if there was a cycle if sort_count != v { Err(GraphError::Cyclic) @@ -139,3 +163,16 @@ impl Graph { } } } + +impl From> for Graph { + fn from(vec: Vec) -> Self { + let mut nodes = Slab::with_capacity(vec.len()); + + for (idx, node) in vec.into_iter().enumerate() { + let new_idx = nodes.insert(node); + assert_eq!(new_idx, idx); + } + + Self { nodes } + } +}