Ramen command pattern

This commit is contained in:
mars 2022-09-17 03:20:34 -06:00
parent eb5eb4f76d
commit 13142ee210
3 changed files with 205 additions and 41 deletions

View File

@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
glam = { version = "0.20", features = ["serde"] }
slab = "^0.4"

126
ramen/src/command.rs Normal file
View File

@ -0,0 +1,126 @@
use super::{Graph, GraphError, GraphResult, Node, NodeKind};
pub trait Command {
fn undo(&self, graph: &Graph) -> GraphResult<DynCommand>;
fn apply(&self, graph: &mut Graph) -> GraphResult<()>;
}
pub type DynCommand = Box<dyn Command>;
#[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<DynCommand> {
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<DynCommand> {
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<DynCommand> {
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<DynCommand> {
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(())
}
}

View File

@ -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<usize>,
pub struct Node {
pub kind: NodeKind,
pub pos: glam::Vec2,
pub inputs: Vec<usize>,
}
#[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<Node>,
}
pub type GraphResult<T> = Result<T, GraphError>;
#[derive(Debug, Clone)]
enum AtomOp {
#[derive(Copy, Debug, Clone)]
pub enum AtomOp {
Add,
Sub,
Mul,
Div,
}
#[derive(Debug)]
pub struct Graph {
pub nodes: Slab<Node>,
}
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<Vec<(usize, usize)>, 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<Vec<usize>, GraphError> {
// The number of nodes
let v = self.nodes.len();
// Forward-directed adjacency lists
let mut adjacency_lists: Vec<Vec<usize>> = 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<usize> = 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<usize> = 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<usize> = 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<Vec<Node>> for Graph {
fn from(vec: Vec<Node>) -> 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 }
}
}