mirror of https://codeberg.org/topola/topola.git
egui,autorouter: implement undo/redo in GUI
This commit is contained in:
parent
114fb747c8
commit
4a4f18f558
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in New Issue