Compare commits
5 Commits
d3f9f81468
...
180cd516ea
Author | SHA1 | Date |
---|---|---|
mars | 180cd516ea | |
mars | bb72e6d753 | |
mars | 4f3363fc0f | |
mars | 13142ee210 | |
mars | eb5eb4f76d |
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"editor"
|
||||
"editor",
|
||||
"ramen"
|
||||
]
|
||||
|
||||
[package]
|
||||
|
|
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
glam = { version = "0.20", features = ["serde"] }
|
||||
slab = "^0.4"
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
use super::{Graph, GraphError, GraphResult};
|
||||
use crate::node::{Edge, Node, NodeKind, SlotIndex};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Command: Debug {
|
||||
fn undo(&self, graph: &Graph) -> GraphResult<DynCommand>;
|
||||
fn apply(&self, graph: &mut Graph) -> GraphResult<()>;
|
||||
}
|
||||
|
||||
pub type DynCommand = Box<dyn Command>;
|
||||
|
||||
pub struct CommandHistory {
|
||||
commands: Vec<(DynCommand, DynCommand)>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl CommandHistory {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
commands: Vec::new(),
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, graph: &mut Graph, cmd: DynCommand) -> GraphResult<()> {
|
||||
let undo = cmd.undo(graph)?;
|
||||
cmd.apply(graph)?;
|
||||
|
||||
// Discard redo history on new push
|
||||
if self.cursor < self.commands.len() {
|
||||
self.commands.truncate(self.cursor);
|
||||
}
|
||||
|
||||
self.commands.push((cmd, undo));
|
||||
self.cursor += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn undo(&mut self, graph: &mut Graph) -> GraphResult<bool> {
|
||||
if self.cursor > 0 {
|
||||
self.cursor -= 1;
|
||||
self.commands[self.cursor].1.apply(graph)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, graph: &mut Graph) -> GraphResult<bool> {
|
||||
if self.cursor < self.commands.len() {
|
||||
self.commands[self.cursor].0.apply(graph)?;
|
||||
self.cursor += 1;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CreateNode {
|
||||
pub kind: NodeKind,
|
||||
pub pos: glam::Vec2,
|
||||
pub inputs: Vec<Option<SlotIndex>>,
|
||||
}
|
||||
|
||||
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: self.inputs.clone(),
|
||||
};
|
||||
|
||||
graph.nodes.insert(node);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Node> for CreateNode {
|
||||
fn from(node: Node) -> Self {
|
||||
Self {
|
||||
kind: node.kind,
|
||||
pos: node.pos,
|
||||
inputs: node.inputs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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: target.inputs.clone(),
|
||||
};
|
||||
|
||||
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 edge: Edge,
|
||||
}
|
||||
|
||||
impl Command for SetEdge {
|
||||
fn undo(&self, graph: &Graph) -> GraphResult<DynCommand> {
|
||||
let target = graph.get_node(self.edge.output.node)?;
|
||||
|
||||
let old_input = target
|
||||
.inputs
|
||||
.get(self.edge.output.slot)
|
||||
.ok_or(GraphError::InvalidReference)?;
|
||||
|
||||
Ok(match old_input {
|
||||
Some(old_input) => Box::new(SetEdge {
|
||||
edge: Edge {
|
||||
input: *old_input,
|
||||
output: self.edge.output,
|
||||
},
|
||||
}),
|
||||
None => Box::new(DeleteEdge {
|
||||
input: self.edge.output,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(&self, graph: &mut Graph) -> GraphResult<()> {
|
||||
let target = graph.get_node_mut(self.edge.output.node)?;
|
||||
|
||||
let input = target
|
||||
.inputs
|
||||
.get_mut(self.edge.output.slot)
|
||||
.ok_or(GraphError::InvalidReference)?;
|
||||
|
||||
*input = Some(self.edge.input);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeleteEdge {
|
||||
pub input: SlotIndex,
|
||||
}
|
||||
|
||||
impl Command for DeleteEdge {
|
||||
fn undo(&self, graph: &Graph) -> GraphResult<DynCommand> {
|
||||
let target = graph.get_node(self.input.node)?;
|
||||
|
||||
let old_input = target
|
||||
.inputs
|
||||
.get(self.input.slot)
|
||||
.ok_or(GraphError::InvalidReference)?
|
||||
.ok_or(GraphError::MissingEdge)?;
|
||||
|
||||
let undo = SetEdge {
|
||||
edge: Edge {
|
||||
input: old_input,
|
||||
output: self.input,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Box::new(undo))
|
||||
}
|
||||
|
||||
fn apply(&self, graph: &mut Graph) -> GraphResult<()> {
|
||||
let target = graph.get_node_mut(self.input.node)?;
|
||||
|
||||
let input = target
|
||||
.inputs
|
||||
.get_mut(self.input.slot)
|
||||
.ok_or(GraphError::InvalidReference)?;
|
||||
|
||||
if input.is_none() {
|
||||
Err(GraphError::MissingEdge)
|
||||
} else {
|
||||
*input = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::node::AtomOp;
|
||||
|
||||
impl CommandHistory {
|
||||
pub fn push_multi(
|
||||
&mut self,
|
||||
graph: &mut Graph,
|
||||
cmds: impl IntoIterator<Item = DynCommand>,
|
||||
) -> GraphResult<()> {
|
||||
for cmd in cmds {
|
||||
println!("{:#?}", cmd);
|
||||
self.push(graph, cmd)?;
|
||||
println!("{:#?}", graph);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self, graph: &mut Graph) -> GraphResult<()> {
|
||||
while self.cursor > 0 {
|
||||
self.cursor -= 1;
|
||||
let undo = &self.commands[self.cursor].1;
|
||||
println!("{:#?}", undo);
|
||||
undo.apply(graph)?;
|
||||
println!("{:#?}", graph);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn three_node_add_cmds() -> Vec<DynCommand> {
|
||||
vec![
|
||||
Box::new(CreateNode::from(Node::literal(1.0))),
|
||||
Box::new(CreateNode::from(Node::literal(2.0))),
|
||||
Box::new(CreateNode::from(Node {
|
||||
kind: NodeKind::Op(AtomOp::Add),
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![None; 2],
|
||||
})),
|
||||
Box::new(SetEdge {
|
||||
edge: Edge {
|
||||
input: SlotIndex { node: 0, slot: 0 },
|
||||
output: SlotIndex { node: 2, slot: 0 },
|
||||
},
|
||||
}),
|
||||
Box::new(SetEdge {
|
||||
edge: Edge {
|
||||
input: SlotIndex { node: 1, slot: 0 },
|
||||
output: SlotIndex { node: 2, slot: 1 },
|
||||
},
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_node() -> GraphResult<()> {
|
||||
let mut graph = Graph::new();
|
||||
|
||||
let cmd = CreateNode {
|
||||
kind: NodeKind::Lit(0.0),
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![],
|
||||
};
|
||||
|
||||
let undo = cmd.undo(&graph)?;
|
||||
|
||||
cmd.apply(&mut graph)?;
|
||||
assert_eq!(graph.nodes.len(), 1);
|
||||
|
||||
undo.apply(&mut graph)?;
|
||||
assert_eq!(graph.nodes.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_node_add() -> GraphResult<()> {
|
||||
let mut graph = Graph::new();
|
||||
let cmds = three_node_add_cmds();
|
||||
|
||||
for cmd in cmds.into_iter() {
|
||||
println!("{:#?}", cmd);
|
||||
cmd.apply(&mut graph)?;
|
||||
println!("{:#?}", graph);
|
||||
}
|
||||
|
||||
graph.topological_order()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_node_add_rewind() -> GraphResult<()> {
|
||||
let mut graph = Graph::new();
|
||||
let mut history = CommandHistory::new();
|
||||
let cmds = three_node_add_cmds();
|
||||
history.push_multi(&mut graph, cmds)?;
|
||||
graph.topological_order()?;
|
||||
history.rewind(&mut graph)?;
|
||||
assert_eq!(graph.nodes.len(), 0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,64 +1,67 @@
|
|||
fn main() {
|
||||
// Represent the graph as adjacency lists
|
||||
let mut 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
|
||||
]
|
||||
};
|
||||
use slab::Slab;
|
||||
|
||||
pub mod command;
|
||||
pub mod node;
|
||||
|
||||
use node::{AtomOp, Edge, Node, NodeKind, SlotIndex};
|
||||
|
||||
fn main() {
|
||||
let mut graph = Graph::new();
|
||||
|
||||
let nodes = &mut graph.nodes;
|
||||
let n0 = nodes.insert(Node::literal(1.0));
|
||||
let n1 = nodes.insert(Node::literal(2.0));
|
||||
let n2 = nodes.insert(Node::op(AtomOp::Add, n0, n1));
|
||||
let n3 = nodes.insert(Node::literal(4.0));
|
||||
let n4 = nodes.insert(Node::op(AtomOp::Div, n2, n3));
|
||||
let n5 = nodes.insert(Node::op(AtomOp::Mul, n4, n4));
|
||||
let n6 = nodes.insert(Node::op(AtomOp::Add, n5, n0));
|
||||
let n7 = nodes.insert(Node::literal(0.0)); // Should be ignored since it's not used
|
||||
|
||||
graph.compile().expect("Compilation failure");
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Node {
|
||||
kind: NodeKind,
|
||||
inputs: Vec<usize>,
|
||||
#[derive(Debug)]
|
||||
pub enum GraphError {
|
||||
InvalidReference,
|
||||
MissingEdge,
|
||||
Cyclic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum NodeKind {
|
||||
Lit(f32),
|
||||
Op(AtomOp)
|
||||
}
|
||||
pub type GraphResult<T> = Result<T, GraphError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GraphError {
|
||||
Cyclic
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Graph {
|
||||
nodes: Vec<Node>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum AtomOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
pub struct Graph {
|
||||
pub nodes: Slab<Node>,
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
fn compile(&self) -> Result<(), GraphError> {
|
||||
// TODO: Only compile the nodes that are actually used
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
nodes: Slab::new()
|
||||
}
|
||||
}
|
||||
|
||||
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) -> GraphResult<()> {
|
||||
// TODO: Only compile the nodes that are actually used (set an output node)
|
||||
|
||||
// Iterate through the nodes in topological order
|
||||
for key in self.topological_order()? {
|
||||
let node = &self.nodes[key];
|
||||
match &node.kind {
|
||||
match node.kind {
|
||||
NodeKind::Lit(x) => {
|
||||
//
|
||||
println!("let n{}: f32 = {:?};", key, x);
|
||||
},
|
||||
}
|
||||
NodeKind::Op(op) => {
|
||||
let symbol = match op {
|
||||
AtomOp::Add => '+',
|
||||
|
@ -66,46 +69,60 @@ impl Graph {
|
|||
AtomOp::Mul => '*',
|
||||
AtomOp::Div => '/',
|
||||
};
|
||||
println!("let n{}: f32 = n{} {} n{};", key, &node.inputs[0], symbol, &node.inputs[1]);
|
||||
},
|
||||
|
||||
let inputs = node.unwrap_inputs()?;
|
||||
let lhs = inputs[0].node;
|
||||
let rhs = inputs[1].node;
|
||||
println!("let n{}: f32 = n{} {} n{};", key, lhs, symbol, rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// Return the edges that can be derived from the graph
|
||||
fn edges(&self) -> Result<Vec<(usize, usize)>, GraphError> {
|
||||
fn edges(&self) -> Result<Vec<Edge>, 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));
|
||||
|
||||
let mut edges = Vec::new();
|
||||
for (node_idx, node) in topo_order.iter().enumerate() {
|
||||
for (slot_idx, input) in self.nodes[*node].inputs.iter().enumerate() {
|
||||
if let Some(input) = input {
|
||||
let output = SlotIndex {
|
||||
node: node_idx,
|
||||
slot: slot_idx,
|
||||
};
|
||||
|
||||
edges.push(Edge {
|
||||
input: *input,
|
||||
output,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 j in &node.inputs {
|
||||
adjacency_lists[*j].push(i);
|
||||
for (i, node) in self.nodes.iter() {
|
||||
for j in node.inputs.iter().flatten() {
|
||||
adjacency_lists[j.node].push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +130,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 +156,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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
use crate::{GraphError, GraphResult};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Node {
|
||||
pub kind: NodeKind,
|
||||
pub pos: glam::Vec2,
|
||||
pub inputs: Vec<Option<SlotIndex>>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn unwrap_inputs(&self) -> GraphResult<Vec<SlotIndex>> {
|
||||
let mut inputs = Vec::with_capacity(self.inputs.len());
|
||||
|
||||
for input in self.inputs.iter() {
|
||||
if let Some(input) = input {
|
||||
inputs.push(*input);
|
||||
} else {
|
||||
return Err(GraphError::MissingEdge);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(inputs)
|
||||
}
|
||||
|
||||
/// Temporary helper function for writing tests
|
||||
pub fn literal(val: f32) -> Self {
|
||||
Self {
|
||||
kind: NodeKind::Lit(val),
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Temporary helper function for writing tests
|
||||
pub fn op(op: AtomOp, lhs: usize, rhs: usize) -> Self {
|
||||
let lhs = SlotIndex { node: lhs, slot: 0 };
|
||||
let rhs = SlotIndex { node: rhs, slot: 0 };
|
||||
|
||||
Self {
|
||||
kind: NodeKind::Op(op),
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![Some(lhs), Some(rhs)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum NodeKind {
|
||||
Lit(f32),
|
||||
Op(AtomOp),
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum AtomOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SlotIndex {
|
||||
pub node: usize,
|
||||
pub slot: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Edge {
|
||||
pub input: SlotIndex,
|
||||
pub output: SlotIndex,
|
||||
}
|
Loading…
Reference in New Issue