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 {
|
pub struct Autoroute {
|
||||||
ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>,
|
ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>,
|
||||||
navmesh: Option<Navmesh>, // Useful for debugging.
|
navmesh: Option<Navmesh>, // Useful for debugging.
|
||||||
|
cur_ratline: Option<EdgeIndex<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Autoroute {
|
impl Autoroute {
|
||||||
|
|
@ -38,17 +39,18 @@ impl Autoroute {
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let mut ratlines_iter = Box::new(ratlines.into_iter());
|
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;
|
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 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 {
|
let this = Self {
|
||||||
ratlines_iter,
|
ratlines_iter,
|
||||||
navmesh,
|
navmesh,
|
||||||
|
cur_ratline: Some(cur_ratline),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(this)
|
Some(this)
|
||||||
|
|
@ -59,35 +61,46 @@ impl Autoroute {
|
||||||
autorouter: &mut Autorouter<R>,
|
autorouter: &mut Autorouter<R>,
|
||||||
observer: &mut impl RouterObserverTrait<R>,
|
observer: &mut impl RouterObserverTrait<R>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let new_navmesh = if let Some(cur_edge) = self.ratlines_iter.next() {
|
let (new_navmesh, new_ratline) = if let Some(cur_ratline) = self.ratlines_iter.next() {
|
||||||
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 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 {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let router = Router::new_from_navmesh(
|
let router = Router::new_from_navmesh(
|
||||||
&mut autorouter.layout,
|
&mut autorouter.layout,
|
||||||
std::mem::replace(&mut self.navmesh, new_navmesh).unwrap(),
|
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()
|
self.navmesh.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edge_from_to<R: RulesTrait>(
|
fn ratline_endpoints<R: RulesTrait>(
|
||||||
autorouter: &Autorouter<R>,
|
autorouter: &Autorouter<R>,
|
||||||
edge: EdgeIndex<usize>,
|
ratline: EdgeIndex<usize>,
|
||||||
) -> (FixedDotIndex, FixedDotIndex) {
|
) -> (FixedDotIndex, FixedDotIndex) {
|
||||||
let mut layout = autorouter.layout.lock().unwrap();
|
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
|
.ratsnest
|
||||||
.graph()
|
.graph()
|
||||||
.node_weight(from)
|
.node_weight(source)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vertex_index()
|
.vertex_index()
|
||||||
{
|
{
|
||||||
|
|
@ -95,10 +108,10 @@ impl Autoroute {
|
||||||
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
|
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
|
||||||
};
|
};
|
||||||
|
|
||||||
let to_dot = match autorouter
|
let target_dot = match autorouter
|
||||||
.ratsnest
|
.ratsnest
|
||||||
.graph()
|
.graph()
|
||||||
.node_weight(to)
|
.node_weight(target)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vertex_index()
|
.vertex_index()
|
||||||
{
|
{
|
||||||
|
|
@ -106,7 +119,7 @@ impl Autoroute {
|
||||||
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
|
RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone),
|
||||||
};
|
};
|
||||||
|
|
||||||
(from_dot, to_dot)
|
(source_dot, target_dot)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn navmesh(&self) -> &Option<Navmesh> {
|
pub fn navmesh(&self) -> &Option<Navmesh> {
|
||||||
|
|
@ -155,22 +168,22 @@ impl<R: RulesTrait> Autorouter<R> {
|
||||||
.graph()
|
.graph()
|
||||||
.edge_indices()
|
.edge_indices()
|
||||||
.filter(|ratline| {
|
.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
|
.ratsnest
|
||||||
.graph()
|
.graph()
|
||||||
.node_weight(from)
|
.node_weight(source)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vertex_index();
|
.vertex_index();
|
||||||
let to_vertex = self
|
let to_vertex = self
|
||||||
.ratsnest
|
.ratsnest
|
||||||
.graph()
|
.graph()
|
||||||
.node_weight(to)
|
.node_weight(target)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.vertex_index();
|
.vertex_index();
|
||||||
|
|
||||||
selection.contains(&from_vertex.into()) && selection.contains(&to_vertex.into())
|
selection.contains(&source_vertex.into()) && selection.contains(&to_vertex.into())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,20 @@ impl Execute {
|
||||||
|
|
||||||
pub struct Invoker<R: RulesTrait> {
|
pub struct Invoker<R: RulesTrait> {
|
||||||
autorouter: Autorouter<R>,
|
autorouter: Autorouter<R>,
|
||||||
|
history: Vec<Command>,
|
||||||
|
undone_history: Vec<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: RulesTrait> Invoker<R> {
|
impl<R: RulesTrait> Invoker<R> {
|
||||||
pub fn new(autorouter: Autorouter<R>) -> Self {
|
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);
|
let mut execute = self.execute_walk(command);
|
||||||
|
|
||||||
while execute.next(self, observer) {
|
while execute.next(self, observer) {
|
||||||
|
|
@ -41,20 +47,32 @@ impl<R: RulesTrait> Invoker<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_walk(&mut self, command: &Command) -> Execute {
|
pub fn execute_walk(&mut self, command: Command) -> Execute {
|
||||||
match command {
|
let execute = match command {
|
||||||
Command::Autoroute(selection) => {
|
Command::Autoroute(ref selection) => {
|
||||||
Execute::Autoroute(self.autorouter.autoroute_walk(&selection).unwrap())
|
Execute::Autoroute(self.autorouter.autoroute_walk(selection).unwrap())
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.history.push(command);
|
||||||
|
execute
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self) {
|
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) {
|
pub fn redo(&mut self, observer: &mut impl RouterObserverTrait<R>) {
|
||||||
todo!();
|
let command = self.undone_history.pop().unwrap();
|
||||||
|
self.execute(command, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn autorouter(&self) -> &Autorouter<R> {
|
pub fn autorouter(&self) -> &Autorouter<R> {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use enum_dispatch::enum_dispatch;
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
use petgraph::{
|
use petgraph::{
|
||||||
data::{Element, FromElements},
|
data::{Element, FromElements},
|
||||||
graph::{NodeIndex, UnGraph},
|
graph::{EdgeIndex, NodeIndex, UnGraph},
|
||||||
unionfind::UnionFind,
|
unionfind::UnionFind,
|
||||||
visit::{EdgeRef, IntoEdgeReferences, NodeIndexable},
|
visit::{EdgeRef, IntoEdgeReferences, NodeIndexable},
|
||||||
};
|
};
|
||||||
|
|
@ -153,10 +153,10 @@ impl Ratsnest {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.graph.retain_edges(|g, i| {
|
this.graph.retain_edges(|g, i| {
|
||||||
if let Some((from, to)) = g.edge_endpoints(i) {
|
if let Some((source, target)) = g.edge_endpoints(i) {
|
||||||
let from_index = g.node_weight(from).unwrap().vertex_index().node_index();
|
let source_index = g.node_weight(source).unwrap().vertex_index().node_index();
|
||||||
let to_index = g.node_weight(to).unwrap().vertex_index().node_index();
|
let target_index = g.node_weight(target).unwrap().vertex_index().node_index();
|
||||||
!unionfind.equiv(from_index, to_index)
|
!unionfind.equiv(source_index, target_index)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -165,6 +165,10 @@ impl Ratsnest {
|
||||||
Ok(this)
|
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> {
|
pub fn graph(&self) -> &UnGraph<VertexWeight, EdgeWeight, usize> {
|
||||||
&self.graph
|
&self.graph
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ use topola::{
|
||||||
draw::DrawException,
|
draw::DrawException,
|
||||||
navmesh::{Navmesh, NavmeshEdgeReference, VertexIndex},
|
navmesh::{Navmesh, NavmeshEdgeReference, VertexIndex},
|
||||||
tracer::{Trace, Tracer},
|
tracer::{Trace, Tracer},
|
||||||
RouterObserverTrait,
|
EmptyRouterObserver, RouterObserverTrait,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -205,13 +205,13 @@ impl eframe::App for App {
|
||||||
if ui.button("Autoroute").clicked() {
|
if ui.button("Autoroute").clicked() {
|
||||||
if let (Some(invoker_arc_mutex), Some(overlay)) = (&self.invoker, &self.overlay)
|
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 shared_data_arc_mutex = self.shared_data.clone();
|
||||||
let selection = overlay.selection().clone();
|
let selection = overlay.selection().clone();
|
||||||
|
|
||||||
execute(async move {
|
execute(async move {
|
||||||
let mut invoker = invoker.lock().unwrap();
|
let mut invoker = invoker_arc_mutex.lock().unwrap();
|
||||||
let mut execute = invoker.execute_walk(&Command::Autoroute(selection));
|
let mut execute = invoker.execute_walk(Command::Autoroute(selection));
|
||||||
|
|
||||||
if let Execute::Autoroute(ref mut autoroute) = execute {
|
if let Execute::Autoroute(ref mut autoroute) = execute {
|
||||||
let from = autoroute.navmesh().as_ref().unwrap().from();
|
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();
|
ui.separator();
|
||||||
|
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,6 @@ impl<'a, R: RulesTrait> Router<'a, R> {
|
||||||
observer: &mut impl RouterObserverTrait<R>,
|
observer: &mut impl RouterObserverTrait<R>,
|
||||||
) -> Result<BandIndex, RoutingError> {
|
) -> Result<BandIndex, RoutingError> {
|
||||||
let mut tracer = self.tracer();
|
let mut tracer = self.tracer();
|
||||||
|
|
||||||
let trace = tracer.start(self.navmesh.from(), width);
|
let trace = tracer.start(self.navmesh.from(), width);
|
||||||
|
|
||||||
let (_cost, _path, band) = astar(
|
let (_cost, _path, band) = astar(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ fn test() {
|
||||||
let layout_arc_mutex = Arc::new(Mutex::new(design.make_layout()));
|
let layout_arc_mutex = Arc::new(Mutex::new(design.make_layout()));
|
||||||
|
|
||||||
let mut autorouter = Autorouter::new(layout_arc_mutex.clone()).unwrap();
|
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 layout = layout_arc_mutex.lock().unwrap();
|
||||||
let mut unionfind = UnionFind::new(layout.drawing().geometry().graph().node_bound());
|
let mut unionfind = UnionFind::new(layout.drawing().geometry().graph().node_bound());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue