egui,autorouter: implement undo/redo in GUI

This commit is contained in:
Mikolaj Wielgus 2024-05-15 03:40:48 +02:00
parent 114fb747c8
commit 4a4f18f558
6 changed files with 97 additions and 42 deletions

View File

@ -29,6 +29,7 @@ use crate::{
pub struct Autoroute {
ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>,
navmesh: Option<Navmesh>, // Useful for debugging.
cur_ratline: Option<EdgeIndex<usize>>,
}
impl Autoroute {
@ -38,17 +39,18 @@ impl Autoroute {
) -> Option<Self> {
let mut ratlines_iter = Box::new(ratlines.into_iter());
let Some(cur_edge) = ratlines_iter.next() else {
let Some(cur_ratline) = ratlines_iter.next() else {
return None;
};
let (from, to) = Self::edge_from_to(autorouter, cur_edge);
let (source, target) = Self::ratline_endpoints(autorouter, cur_ratline);
let layout = autorouter.layout.lock().unwrap();
let navmesh = Some(Navmesh::new(&layout, from, to).ok()?);
let navmesh = Some(Navmesh::new(&layout, source, target).ok()?);
let this = Self {
ratlines_iter,
navmesh,
cur_ratline: Some(cur_ratline),
};
Some(this)
@ -59,35 +61,46 @@ impl Autoroute {
autorouter: &mut Autorouter<R>,
observer: &mut impl RouterObserverTrait<R>,
) -> bool {
let new_navmesh = if let Some(cur_edge) = self.ratlines_iter.next() {
let (from, to) = Self::edge_from_to(autorouter, cur_edge);
let (new_navmesh, new_ratline) = if let Some(cur_ratline) = self.ratlines_iter.next() {
let (source, target) = Self::ratline_endpoints(autorouter, cur_ratline);
let layout = autorouter.layout.lock().unwrap();
Some(Navmesh::new(&layout, from, to).ok().unwrap())
(
Some(Navmesh::new(&layout, source, target).ok().unwrap()),
Some(cur_ratline),
)
} else {
None
(None, None)
};
let router = Router::new_from_navmesh(
&mut autorouter.layout,
std::mem::replace(&mut self.navmesh, new_navmesh).unwrap(),
);
router.unwrap().route_band(100.0, observer);
let Ok(band) = router.unwrap().route_band(100.0, observer) else {
return false;
};
autorouter
.ratsnest
.assign_band_to_ratline(self.cur_ratline.unwrap(), band);
self.cur_ratline = new_ratline;
self.navmesh.is_some()
}
fn edge_from_to<R: RulesTrait>(
fn ratline_endpoints<R: RulesTrait>(
autorouter: &Autorouter<R>,
edge: EdgeIndex<usize>,
ratline: EdgeIndex<usize>,
) -> (FixedDotIndex, FixedDotIndex) {
let mut layout = autorouter.layout.lock().unwrap();
let (from, to) = autorouter.ratsnest.graph().edge_endpoints(edge).unwrap();
let (source, target) = autorouter.ratsnest.graph().edge_endpoints(ratline).unwrap();
let from_dot = match autorouter
let source_dot = match autorouter
.ratsnest
.graph()
.node_weight(from)
.node_weight(source)
.unwrap()
.vertex_index()
{
@ -95,10 +108,10 @@ impl Autoroute {
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
};
let to_dot = match autorouter
let target_dot = match autorouter
.ratsnest
.graph()
.node_weight(to)
.node_weight(target)
.unwrap()
.vertex_index()
{
@ -106,7 +119,7 @@ impl Autoroute {
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
};
(from_dot, to_dot)
(source_dot, target_dot)
}
pub fn navmesh(&self) -> &Option<Navmesh> {
@ -155,22 +168,22 @@ impl<R: RulesTrait> Autorouter<R> {
.graph()
.edge_indices()
.filter(|ratline| {
let (from, to) = self.ratsnest.graph().edge_endpoints(*ratline).unwrap();
let (source, target) = self.ratsnest.graph().edge_endpoints(*ratline).unwrap();
let from_vertex = self
let source_vertex = self
.ratsnest
.graph()
.node_weight(from)
.node_weight(source)
.unwrap()
.vertex_index();
let to_vertex = self
.ratsnest
.graph()
.node_weight(to)
.node_weight(target)
.unwrap()
.vertex_index();
selection.contains(&from_vertex.into()) && selection.contains(&to_vertex.into())
selection.contains(&source_vertex.into()) && selection.contains(&to_vertex.into())
})
.collect()
}

View File

@ -26,14 +26,20 @@ impl Execute {
pub struct Invoker<R: RulesTrait> {
autorouter: Autorouter<R>,
history: Vec<Command>,
undone_history: Vec<Command>,
}
impl<R: RulesTrait> Invoker<R> {
pub fn new(autorouter: Autorouter<R>) -> Self {
Self { autorouter }
Self {
autorouter,
history: vec![],
undone_history: vec![],
}
}
pub fn execute(&mut self, command: &Command, observer: &mut impl RouterObserverTrait<R>) {
pub fn execute(&mut self, command: Command, observer: &mut impl RouterObserverTrait<R>) {
let mut execute = self.execute_walk(command);
while execute.next(self, observer) {
@ -41,20 +47,32 @@ impl<R: RulesTrait> Invoker<R> {
}
}
pub fn execute_walk(&mut self, command: &Command) -> Execute {
match command {
Command::Autoroute(selection) => {
Execute::Autoroute(self.autorouter.autoroute_walk(&selection).unwrap())
pub fn execute_walk(&mut self, command: Command) -> Execute {
let execute = match command {
Command::Autoroute(ref selection) => {
Execute::Autoroute(self.autorouter.autoroute_walk(selection).unwrap())
}
}
};
self.history.push(command);
execute
}
pub fn undo(&mut self) {
todo!();
let command = self.history.pop().unwrap();
match command {
Command::Autoroute(ref selection) => {
self.autorouter.undo_autoroute(selection);
}
}
self.undone_history.push(command);
}
pub fn redo(&mut self) {
todo!();
pub fn redo(&mut self, observer: &mut impl RouterObserverTrait<R>) {
let command = self.undone_history.pop().unwrap();
self.execute(command, observer);
}
pub fn autorouter(&self) -> &Autorouter<R> {

View File

@ -4,7 +4,7 @@ use enum_dispatch::enum_dispatch;
use geo::Point;
use petgraph::{
data::{Element, FromElements},
graph::{NodeIndex, UnGraph},
graph::{EdgeIndex, NodeIndex, UnGraph},
unionfind::UnionFind,
visit::{EdgeRef, IntoEdgeReferences, NodeIndexable},
};
@ -153,10 +153,10 @@ impl Ratsnest {
}
this.graph.retain_edges(|g, i| {
if let Some((from, to)) = g.edge_endpoints(i) {
let from_index = g.node_weight(from).unwrap().vertex_index().node_index();
let to_index = g.node_weight(to).unwrap().vertex_index().node_index();
!unionfind.equiv(from_index, to_index)
if let Some((source, target)) = g.edge_endpoints(i) {
let source_index = g.node_weight(source).unwrap().vertex_index().node_index();
let target_index = g.node_weight(target).unwrap().vertex_index().node_index();
!unionfind.equiv(source_index, target_index)
} else {
true
}
@ -165,6 +165,10 @@ impl Ratsnest {
Ok(this)
}
pub fn assign_band_to_ratline(&mut self, ratline: EdgeIndex<usize>, band: BandIndex) {
self.graph.edge_weight_mut(ratline).unwrap().band = Some(band);
}
pub fn graph(&self) -> &UnGraph<VertexWeight, EdgeWeight, usize> {
&self.graph
}

View File

@ -34,7 +34,7 @@ use topola::{
draw::DrawException,
navmesh::{Navmesh, NavmeshEdgeReference, VertexIndex},
tracer::{Trace, Tracer},
RouterObserverTrait,
EmptyRouterObserver, RouterObserverTrait,
},
};
@ -205,13 +205,13 @@ impl eframe::App for App {
if ui.button("Autoroute").clicked() {
if let (Some(invoker_arc_mutex), Some(overlay)) = (&self.invoker, &self.overlay)
{
let invoker = invoker_arc_mutex.clone();
let invoker_arc_mutex = invoker_arc_mutex.clone();
let shared_data_arc_mutex = self.shared_data.clone();
let selection = overlay.selection().clone();
execute(async move {
let mut invoker = invoker.lock().unwrap();
let mut execute = invoker.execute_walk(&Command::Autoroute(selection));
let mut invoker = invoker_arc_mutex.lock().unwrap();
let mut execute = invoker.execute_walk(Command::Autoroute(selection));
if let Execute::Autoroute(ref mut autoroute) = execute {
let from = autoroute.navmesh().as_ref().unwrap().from();
@ -240,6 +240,27 @@ impl eframe::App for App {
}
}
if ui.button("Undo").clicked() {
if let Some(invoker_arc_mutex) = &self.invoker {
let invoker_arc_mutex = invoker_arc_mutex.clone();
execute(async move {
invoker_arc_mutex.lock().unwrap().undo();
});
}
}
if ui.button("Redo").clicked() {
if let Some(invoker_arc_mutex) = &self.invoker {
let invoker_arc_mutex = invoker_arc_mutex.clone();
execute(async move {
invoker_arc_mutex
.lock()
.unwrap()
.redo(&mut EmptyRouterObserver);
});
}
}
ui.separator();
egui::widgets::global_dark_light_mode_buttons(ui);

View File

@ -190,7 +190,6 @@ impl<'a, R: RulesTrait> Router<'a, R> {
observer: &mut impl RouterObserverTrait<R>,
) -> Result<BandIndex, RoutingError> {
let mut tracer = self.tracer();
let trace = tracer.start(self.navmesh.from(), width);
let (_cost, _path, band) = astar(

View File

@ -15,7 +15,7 @@ fn test() {
let layout_arc_mutex = Arc::new(Mutex::new(design.make_layout()));
let mut autorouter = Autorouter::new(layout_arc_mutex.clone()).unwrap();
autorouter.autoroute(0, &mut EmptyRouterObserver);
autorouter.autoroute(&mut EmptyRouterObserver);
let layout = layout_arc_mutex.lock().unwrap();
let mut unionfind = UnionFind::new(layout.drawing().geometry().graph().node_bound());