Add dynamic node kinds
Move commands.rs tests to lib.rs Add TestNodeBuilder
This commit is contained in:
parent
247028caa8
commit
47f0288f21
|
@ -1,5 +1,5 @@
|
|||
use super::{Graph, GraphError, GraphResult};
|
||||
use crate::node::{Edge, Node, NodeKind, SlotIndex};
|
||||
use crate::node::{Edge, Node, SlotIndex};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Command: Debug {
|
||||
|
@ -10,8 +10,8 @@ pub trait Command: Debug {
|
|||
pub type DynCommand = Box<dyn Command>;
|
||||
|
||||
pub struct CommandHistory {
|
||||
commands: Vec<(DynCommand, DynCommand)>,
|
||||
cursor: usize,
|
||||
pub(crate) commands: Vec<(DynCommand, DynCommand)>,
|
||||
pub(crate) cursor: usize,
|
||||
}
|
||||
|
||||
impl CommandHistory {
|
||||
|
@ -60,7 +60,7 @@ impl CommandHistory {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CreateNode {
|
||||
pub kind: NodeKind,
|
||||
pub kind: usize,
|
||||
pub pos: glam::Vec2,
|
||||
pub inputs: Vec<Option<SlotIndex>>,
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ impl Command for MoveNode {
|
|||
|
||||
fn apply(&self, graph: &mut Graph) -> GraphResult<()> {
|
||||
let target = graph.get_node_mut(self.target)?;
|
||||
|
||||
|
||||
if self.relative {
|
||||
target.pos += self.to;
|
||||
} else {
|
||||
|
@ -237,110 +237,3 @@ impl Command for DeleteEdge {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
}
|
||||
|
|
249
ramen/src/lib.rs
249
ramen/src/lib.rs
|
@ -1,9 +1,13 @@
|
|||
use slab::Slab;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod command;
|
||||
pub mod node;
|
||||
pub mod node_kind;
|
||||
|
||||
use node::{AtomOp, Edge, Node, NodeKind, SlotIndex};
|
||||
use command::*;
|
||||
use node::{Edge, Node, SlotIndex};
|
||||
use node_kind::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GraphError {
|
||||
|
@ -16,12 +20,16 @@ pub type GraphResult<T> = Result<T, GraphError>;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Graph {
|
||||
pub node_kinds: Arc<NodeKindStore>,
|
||||
pub nodes: Slab<Node>,
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
pub fn new() -> Self {
|
||||
Self { nodes: Slab::new() }
|
||||
pub fn new(node_kinds: Arc<NodeKindStore>) -> Self {
|
||||
Self {
|
||||
node_kinds,
|
||||
nodes: Slab::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_node(&self, index: usize) -> GraphResult<&Node> {
|
||||
|
@ -34,35 +42,6 @@ impl Graph {
|
|||
.ok_or(GraphError::InvalidReference)
|
||||
}
|
||||
|
||||
pub 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 {
|
||||
NodeKind::Lit(x) => {
|
||||
println!("let n{}: f32 = {:?};", key, x);
|
||||
}
|
||||
NodeKind::Op(op) => {
|
||||
let symbol = match op {
|
||||
AtomOp::Add => '+',
|
||||
AtomOp::Sub => '-',
|
||||
AtomOp::Mul => '*',
|
||||
AtomOp::Div => '/',
|
||||
};
|
||||
|
||||
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
|
||||
pub fn edges(&self) -> Result<Vec<Edge>, GraphError> {
|
||||
let topo_order = self.topological_order()?;
|
||||
|
@ -139,15 +118,207 @@ impl Graph {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Node>> for Graph {
|
||||
fn from(vec: Vec<Node>) -> Self {
|
||||
let mut nodes = Slab::with_capacity(vec.len());
|
||||
pub enum TestOpKind {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
for (idx, node) in vec.into_iter().enumerate() {
|
||||
let new_idx = nodes.insert(node);
|
||||
assert_eq!(new_idx, idx);
|
||||
pub struct TestGraphBuilder {
|
||||
pub graph: Graph,
|
||||
pub literal_kind: usize,
|
||||
pub add_kind: usize,
|
||||
pub sub_kind: usize,
|
||||
pub mul_kind: usize,
|
||||
pub div_kind: usize,
|
||||
}
|
||||
|
||||
impl TestGraphBuilder {
|
||||
pub fn new() -> Self {
|
||||
let mut kinds = NodeKindStore { kinds: vec![] };
|
||||
|
||||
let literal_kind = kinds.push(NodeKind {
|
||||
title: "Literal".into(),
|
||||
inputs: vec![],
|
||||
outputs: vec![NodeKindOutput { label: "".into() }],
|
||||
});
|
||||
|
||||
let mut make_arith_kind = |title: &str| -> usize {
|
||||
kinds.push(NodeKind {
|
||||
title: title.to_string(),
|
||||
inputs: vec![
|
||||
NodeKindInput {
|
||||
label: "A".to_string(),
|
||||
},
|
||||
NodeKindInput {
|
||||
label: "B".to_string(),
|
||||
},
|
||||
],
|
||||
outputs: vec![NodeKindOutput {
|
||||
label: "Result".to_string(),
|
||||
}],
|
||||
})
|
||||
};
|
||||
|
||||
let add_kind = make_arith_kind("Add");
|
||||
let sub_kind = make_arith_kind("Subtract");
|
||||
let mul_kind = make_arith_kind("Multiply");
|
||||
let div_kind = make_arith_kind("Divide");
|
||||
|
||||
let graph = Graph::new(Arc::new(kinds));
|
||||
|
||||
Self {
|
||||
graph,
|
||||
literal_kind,
|
||||
add_kind,
|
||||
sub_kind,
|
||||
mul_kind,
|
||||
div_kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, node: Node) -> usize {
|
||||
self.graph.nodes.insert(node)
|
||||
}
|
||||
|
||||
Self { nodes }
|
||||
pub fn add_literal(&mut self, value: f32) -> usize {
|
||||
self.graph.nodes.insert(self.make_literal(value))
|
||||
}
|
||||
|
||||
pub fn make_literal(&self, _value: f32) -> Node {
|
||||
Node {
|
||||
kind: self.literal_kind,
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_op(&mut self, kind: TestOpKind, a: usize, b: usize) -> usize {
|
||||
self.graph.nodes.insert(self.make_op(kind, a, b))
|
||||
}
|
||||
|
||||
pub fn make_op(&self, kind: TestOpKind, a: usize, b: usize) -> Node {
|
||||
let kind = match kind {
|
||||
TestOpKind::Add => self.add_kind,
|
||||
TestOpKind::Sub => self.sub_kind,
|
||||
TestOpKind::Mul => self.mul_kind,
|
||||
TestOpKind::Div => self.div_kind,
|
||||
};
|
||||
|
||||
Node {
|
||||
kind,
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![
|
||||
Some(SlotIndex { node: a, slot: 0 }),
|
||||
Some(SlotIndex { node: b, slot: 0 }),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn three_node_add_cmds(&self) -> Vec<DynCommand> {
|
||||
vec![
|
||||
Box::new(CreateNode::from(self.make_literal(1.0))),
|
||||
Box::new(CreateNode::from(self.make_literal(2.0))),
|
||||
Box::new(CreateNode::from(Node {
|
||||
kind: self.add_kind,
|
||||
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 },
|
||||
},
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_node() -> GraphResult<()> {
|
||||
let mut builder = TestGraphBuilder::new();
|
||||
|
||||
let cmd = CreateNode {
|
||||
kind: builder.literal_kind,
|
||||
pos: glam::Vec2::ZERO,
|
||||
inputs: vec![],
|
||||
};
|
||||
|
||||
let undo = cmd.undo(&builder.graph)?;
|
||||
|
||||
cmd.apply(&mut builder.graph)?;
|
||||
assert_eq!(builder.graph.nodes.len(), 1);
|
||||
|
||||
undo.apply(&mut builder.graph)?;
|
||||
assert_eq!(builder.graph.nodes.len(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_node_add() -> GraphResult<()> {
|
||||
let mut builder = TestGraphBuilder::new();
|
||||
let cmds = builder.three_node_add_cmds();
|
||||
|
||||
for cmd in cmds.into_iter() {
|
||||
println!("{:#?}", cmd);
|
||||
cmd.apply(&mut builder.graph)?;
|
||||
println!("{:#?}", builder.graph);
|
||||
}
|
||||
|
||||
builder.graph.topological_order()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_node_add_rewind() -> GraphResult<()> {
|
||||
let mut builder = TestGraphBuilder::new();
|
||||
let mut history = CommandHistory::new();
|
||||
let cmds = builder.three_node_add_cmds();
|
||||
history.push_multi(&mut builder.graph, cmds)?;
|
||||
builder.graph.topological_order()?;
|
||||
history.rewind(&mut builder.graph)?;
|
||||
assert_eq!(builder.graph.nodes.len(), 0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{GraphError, GraphResult};
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Node {
|
||||
pub kind: NodeKind,
|
||||
pub kind: usize,
|
||||
pub pos: glam::Vec2,
|
||||
pub inputs: Vec<Option<SlotIndex>>,
|
||||
}
|
||||
|
@ -22,27 +22,6 @@ impl Node {
|
|||
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)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at(self, pos: glam::Vec2) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
|
@ -51,20 +30,6 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
#[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, Hash, PartialEq, Eq)]
|
||||
pub struct SlotIndex {
|
||||
pub node: usize,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#[derive(Debug)]
|
||||
pub struct NodeKindStore {
|
||||
pub kinds: Vec<NodeKind>,
|
||||
}
|
||||
|
||||
impl NodeKindStore {
|
||||
pub fn push(&mut self, kind: NodeKind) -> usize {
|
||||
let idx = self.kinds.len();
|
||||
self.kinds.push(kind);
|
||||
idx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeKind {
|
||||
pub title: String,
|
||||
pub inputs: Vec<NodeKindInput>,
|
||||
pub outputs: Vec<NodeKindOutput>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeKindInput {
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeKindOutput {
|
||||
pub label: String,
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use eframe::egui;
|
||||
use ramen::node::{AtomOp, Node};
|
||||
use ramen::Graph;
|
||||
use ramen::{TestGraphBuilder, TestOpKind};
|
||||
use ramen_egui::NodeEditor;
|
||||
|
||||
fn main() {
|
||||
|
@ -23,32 +22,31 @@ impl Application {
|
|||
let scale = 250.0;
|
||||
let pos = |x, y| glam::Vec2::new(x, y) * scale;
|
||||
|
||||
let mut graph = Graph::new();
|
||||
let nodes = &mut graph.nodes;
|
||||
let n0 = nodes.insert(Node::literal(1.0).at(pos(1.0, 1.0)));
|
||||
let n1 = nodes.insert(Node::literal(2.0).at(pos(1.0, 2.0)));
|
||||
let n2 = nodes.insert(Node::op(AtomOp::Add, n0, n1).at(pos(2.0, 1.5)));
|
||||
let n3 = nodes.insert(Node::literal(4.0).at(pos(2.0, 2.5)));
|
||||
let n4 = nodes.insert(Node::op(AtomOp::Div, n2, n3).at(pos(3.0, 2.0)));
|
||||
let n5 = nodes.insert(Node::op(AtomOp::Mul, n4, n4).at(pos(4.0, 2.0)));
|
||||
let _n6 = nodes.insert(Node::op(AtomOp::Add, n5, n0).at(pos(3.0, 1.0)));
|
||||
let mut builder = TestGraphBuilder::new();
|
||||
let n0 = builder.add(builder.make_literal(1.0).at(pos(1.0, 1.0)));
|
||||
let n1 = builder.add(builder.make_literal(2.0).at(pos(1.0, 2.0)));
|
||||
let n2 = builder.add(builder.make_op(TestOpKind::Add, n0, n1).at(pos(2.0, 1.5)));
|
||||
let n3 = builder.add(builder.make_literal(4.0).at(pos(2.0, 2.5)));
|
||||
let n4 = builder.add(builder.make_op(TestOpKind::Div, n2, n3).at(pos(3.0, 2.0)));
|
||||
let n5 = builder.add(builder.make_op(TestOpKind::Mul, n4, n4).at(pos(4.0, 2.0)));
|
||||
let _n6 = builder.add(builder.make_op(TestOpKind::Add, n5, n0).at(pos(3.0, 1.0)));
|
||||
|
||||
Self {
|
||||
editor: NodeEditor::from_graph(graph),
|
||||
editor: NodeEditor::from_graph(builder.graph),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for Application {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::TopBottomPanel::top("menu_panel").show(ctx, |ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
egui::widgets::global_dark_light_mode_switch(ui);
|
||||
|
||||
|
||||
if ui.button("Undo").clicked() {
|
||||
self.editor.undo();
|
||||
}
|
||||
|
||||
|
||||
if ui.button("Redo").clicked() {
|
||||
self.editor.redo();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue