Skip to content

Instantly share code, notes, and snippets.

@OptimisticPeach OptimisticPeach/nodes.rs forked from wtholliday/nodes.rs
Last active Jul 9, 2019

Embed
What would you like to do?
Exercise in graph data structure with undo/redo.
#[derive(Debug)]
struct Node;
#[derive(Copy, Clone, Debug)]
struct Wire {
from: usize, // node index
to: usize, // node index
}
#[derive(Debug)]
struct Patch {
nodes: Vec<Node>,
wires: Vec<Wire>
}
impl Patch {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
wires: Vec::new(),
}
}
fn connect(&mut self, from: usize, to: usize, undos: &mut UndoStack) {
self.wires.push(Wire{from: from, to: to});
undos.push(Box::new(move |patch, undos| patch.disconnect(from, to, undos)))
}
fn disconnect(&mut self, from: usize, to: usize, undos: &mut UndoStack) {
self.wires.retain(|w| w.from != from || w.to != to);
undos.push(Box::new(move |patch, undos| patch.connect(from, to, undos)))
}
fn add_node(&mut self, index: usize, undos: &mut UndoStack) {
// Insert
self.nodes.insert(index, Node{ });
// Update wires.
for w in &mut self.wires {
w.from += if w.from >= index {1} else {0};
w.to += if w.to >= index {1} else {0};
}
// Record a function to undo.
undos.push(Box::new(move |patch, undos| patch.remove_node(index, undos)))
}
fn remove_node(&mut self, node: usize, undos: &mut UndoStack) {
// Get all the wires we'll need to disconnect.
let mut to_disconnect = Vec::new();
for w in &self.wires {
if w.from == node || w.to == node {
to_disconnect.push(Wire{from: w.from, to: w.to});
}
}
for w in to_disconnect {
self.disconnect(w.from, w.to, undos);
}
// Update other wires.
for w in &mut self.wires {
w.from -= (w.from > node) as usize;
w.to -= (w.to > node) as usize;
}
// Remove the node.
self.nodes.remove(node as usize);
}
}
impl Default for Patch {
fn default() -> Self {
Self::new()
}
}
struct UndoStack {
undoing: bool,
undos : Vec<Box<dyn Fn(&mut Patch, &mut UndoStack)>>,
redos : Vec<Box<dyn Fn(&mut Patch, &mut UndoStack)>>
}
impl UndoStack {
pub fn new() -> Self {
Self {
undoing: false,
undos: Vec::new(),
redos: Vec::new(),
}
}
fn undo(&mut self, patch: &mut Patch) {
self.undoing = true;
if let Some(top) = self.undos.pop() {
top(patch, self);
}
self.undoing = false;
}
fn push(&mut self, f: Box<dyn Fn(&mut Patch, &mut UndoStack)>) {
if self.undoing {
self.redos.push(f)
} else {
self.undos.push(f)
}
}
}
impl Default for UndoStack {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod unit_tests {
#[test]
fn it_works() {
let mut patch = Patch::new();
let mut undos = UndoStack::new();
patch.add_node(0, &mut undos);
patch.add_node(0, &mut undos);
patch.connect(0, 1, &mut undos);
patch.connect(1, 1, &mut undos);
assert_eq!(patch.wires.len(), 2);
//print!("{:?}\n", patch);
undos.undo(&mut patch);
//print!("{:?}\n", patch);
assert_eq!(patch.wires.len(), 1);
assert_eq!(patch.wires[0].from, 0);
assert_eq!(patch.wires[0].to, 1);
patch.connect(1, 1, &mut undos);
patch.remove_node(0, &mut undos);
assert_eq!(patch.wires.len(), 1);
assert_eq!(patch.nodes.len(), 1);
assert_eq!(patch.wires[0].from, 0);
assert_eq!(patch.wires[0].to, 0);
patch.add_node(0, &mut undos);
assert_eq!(patch.wires[0].from, 1);
assert_eq!(patch.wires[0].to, 1);
undos.undo(&mut patch);
assert_eq!(patch.wires[0].from, 0);
assert_eq!(patch.wires[0].to, 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.