From a2c8ae7e70bff044cfaa8517dfe09b31d645a147 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sat, 4 May 2024 18:02:34 +0200 Subject: [PATCH] autorouter: put `Layout` in a mutex --- src/autorouter/autorouter.rs | 35 ++++--- src/bin/topola-egui/app.rs | 184 +++++++++++++++++------------------ src/router/router.rs | 60 +++++++----- src/router/tracer.rs | 34 ++++--- 4 files changed, 164 insertions(+), 149 deletions(-) diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index 7fda092..618cedc 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, Mutex}; + use geo::Point; use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use spade::InsertionError; @@ -16,9 +18,10 @@ pub struct Autorouter { } impl Autorouter { - pub fn new(layout: Layout) -> Result { + pub fn new(layout: Arc>>) -> Result { + let ratsnest = Ratsnest::new(&layout.lock().unwrap())?; Ok(Self { - ratsnest: Ratsnest::new(&layout)?, + ratsnest, router: Router::new(layout), }) } @@ -38,27 +41,27 @@ impl Autorouter { .unwrap() .vertex_index(); - let from_dot = match from { - RatsnestVertexIndex::FixedDot(dot) => dot, - RatsnestVertexIndex::Zone(zone) => self.router.layout_mut().zone_apex(zone), - }; + let (from_dot, to_dot) = { + let layout = self.router.layout(); + let mut layout = layout.lock().unwrap(); - let to_dot = match to { - RatsnestVertexIndex::FixedDot(dot) => dot, - RatsnestVertexIndex::Zone(zone) => self.router.layout_mut().zone_apex(zone), + let from_dot = match from { + RatsnestVertexIndex::FixedDot(dot) => dot, + RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone), + }; + + let to_dot = match to { + RatsnestVertexIndex::FixedDot(dot) => dot, + RatsnestVertexIndex::Zone(zone) => layout.zone_apex(zone), + }; + + (from_dot, to_dot) }; self.router.route_band(from_dot, to_dot, 100.0, observer); } } - fn terminating_dot(&mut self, vertex: RatsnestVertexIndex) -> FixedDotIndex { - match vertex { - RatsnestVertexIndex::FixedDot(dot) => dot, - RatsnestVertexIndex::Zone(zone) => self.router.layout_mut().zone_apex(zone), - } - } - pub fn router(&self) -> &Router { &self.router } diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 78f86b3..2456d1c 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -3,7 +3,10 @@ use geo::point; use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use std::{ future::Future, - sync::mpsc::{channel, Receiver, Sender}, + sync::{ + mpsc::{channel, Receiver, Sender}, + Arc, Mutex, + }, }; use topola::{ @@ -40,7 +43,7 @@ pub struct App { overlay: Option, #[serde(skip)] - autorouter: Option>, + layout: Option>>>, #[serde(skip)] text_channel: (Sender, Receiver), @@ -53,7 +56,7 @@ impl Default for App { fn default() -> Self { Self { overlay: None, - autorouter: None, + layout: None, text_channel: channel(), from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), } @@ -108,14 +111,14 @@ impl eframe::App for App { let design = DsnDesign::load_from_string(file_contents).unwrap(); let layout = design.make_layout(); self.overlay = Some(Overlay::new(&layout).unwrap()); - self.autorouter = Some(Autorouter::new(layout).unwrap()); + self.layout = Some(Arc::new(Mutex::new(layout))); } } else { if let Ok(path) = self.text_channel.1.try_recv() { let design = DsnDesign::load_from_file(&path).unwrap(); let layout = design.make_layout(); self.overlay = Some(Overlay::new(&layout).unwrap()); - self.autorouter = Some(Autorouter::new(layout).unwrap()); + self.layout = Some(Arc::new(Mutex::new(layout))); } } @@ -150,8 +153,13 @@ impl eframe::App for App { ui.separator(); if ui.button("Autoroute").clicked() { - if let Some(ref mut autorouter) = &mut self.autorouter { - autorouter.autoroute(&mut EmptyRouterObserver {}); + if let Some(layout_arc_mutex) = &self.layout { + let layout = layout_arc_mutex.clone(); + + execute(async move { + let mut autorouter = Autorouter::new(layout).unwrap(); + autorouter.autoroute(&mut EmptyRouterObserver {}); + }); } } @@ -192,97 +200,83 @@ impl eframe::App for App { let transform = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); let mut painter = Painter::new(ui, transform); - if let (Some(autorouter), Some(overlay)) = (&self.autorouter, &mut self.overlay) { - if ctx.input(|i| i.pointer.any_click()) { - overlay.click( - autorouter.router().layout(), - point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, - ); - } - - for primitive in autorouter - .router() - .layout() - .drawing() - .layer_primitive_nodes(1) + if let Some(layout_arc_mutex) = &self.layout { + if let (layout, Some(overlay)) = + (&layout_arc_mutex.lock().unwrap(), &mut self.overlay) { - let shape = primitive - .primitive(autorouter.router().layout().drawing()) - .shape(); + if ctx.input(|i| i.pointer.any_click()) { + overlay.click( + layout, + point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + ); + } - let color = if overlay - .selection() - .contains(&GenericNode::Primitive(primitive)) - { - egui::Color32::from_rgb(100, 100, 255) - } else { - egui::Color32::from_rgb(52, 52, 200) - }; - painter.paint_shape(&shape, color); + for primitive in layout.drawing().layer_primitive_nodes(1) { + let shape = primitive.primitive(layout.drawing()).shape(); + + let color = if overlay + .selection() + .contains(&GenericNode::Primitive(primitive)) + { + egui::Color32::from_rgb(100, 100, 255) + } else { + egui::Color32::from_rgb(52, 52, 200) + }; + painter.paint_shape(&shape, color); + } + + for zone in layout.layer_zone_nodes(1) { + let color = + if overlay.selection().contains(&GenericNode::Compound(zone)) { + egui::Color32::from_rgb(100, 100, 255) + } else { + egui::Color32::from_rgb(52, 52, 200) + }; + painter.paint_polygon(&layout.zone(zone).shape().polygon, color) + } + + for primitive in layout.drawing().layer_primitive_nodes(0) { + let shape = primitive.primitive(layout.drawing()).shape(); + + let color = if overlay + .selection() + .contains(&GenericNode::Primitive(primitive)) + { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(200, 52, 52) + }; + painter.paint_shape(&shape, color); + } + + for zone in layout.layer_zone_nodes(0) { + let color = + if overlay.selection().contains(&GenericNode::Compound(zone)) { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(200, 52, 52) + }; + painter.paint_polygon(&layout.zone(zone).shape().polygon, color) + } + + for edge in overlay.ratsnest().graph().edge_references() { + let from = overlay + .ratsnest() + .graph() + .node_weight(edge.source()) + .unwrap() + .pos; + let to = overlay + .ratsnest() + .graph() + .node_weight(edge.target()) + .unwrap() + .pos; + + painter.paint_edge(from, to, egui::Color32::from_rgb(90, 90, 200)); + } + //unreachable!(); } - - for zone in autorouter.router().layout().layer_zone_nodes(1) { - let color = if overlay.selection().contains(&GenericNode::Compound(zone)) { - egui::Color32::from_rgb(100, 100, 255) - } else { - egui::Color32::from_rgb(52, 52, 200) - }; - painter.paint_polygon( - &autorouter.router().layout().zone(zone).shape().polygon, - color, - ) - } - - for primitive in autorouter - .router() - .layout() - .drawing() - .layer_primitive_nodes(0) - { - let shape = primitive - .primitive(autorouter.router().layout().drawing()) - .shape(); - - let color = if overlay - .selection() - .contains(&GenericNode::Primitive(primitive)) - { - egui::Color32::from_rgb(255, 100, 100) - } else { - egui::Color32::from_rgb(200, 52, 52) - }; - painter.paint_shape(&shape, color); - } - - for zone in autorouter.router().layout().layer_zone_nodes(0) { - let color = if overlay.selection().contains(&GenericNode::Compound(zone)) { - egui::Color32::from_rgb(255, 100, 100) - } else { - egui::Color32::from_rgb(200, 52, 52) - }; - painter.paint_polygon( - &autorouter.router().layout().zone(zone).shape().polygon, - color, - ) - } - - for edge in overlay.ratsnest().graph().edge_references() { - let from = overlay - .ratsnest() - .graph() - .node_weight(edge.source()) - .unwrap() - .pos; - let to = overlay - .ratsnest() - .graph() - .node_weight(edge.target()) - .unwrap() - .pos; - - painter.paint_edge(from, to, egui::Color32::from_rgb(90, 90, 200)); - } - //unreachable!(); } }) }); diff --git a/src/router/router.rs b/src/router/router.rs index 59e6490..0825100 100644 --- a/src/router/router.rs +++ b/src/router/router.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, Mutex}; + use geo::geometry::Point; use geo::EuclideanDistance; use petgraph::visit::EdgeRef; @@ -56,7 +58,7 @@ pub trait RouterObserverTrait { } pub struct Router { - layout: Layout, + layout: Arc>>, } struct RouterAstarStrategy<'a, RO: RouterObserverTrait, R: RulesTrait> { @@ -103,14 +105,24 @@ impl<'a, RO: RouterObserverTrait, R: RulesTrait> AstarStrategy<&Navmesh, f64> return None; } - let before_probe_length = self.tracer.layout.band_length(self.trace.band); + let before_probe_length = self + .tracer + .layout + .lock() + .unwrap() + .band_length(self.trace.band); let width = self.trace.width; let result = self.tracer.step(&mut self.trace, edge.target(), width); self.observer .on_probe(&self.tracer, &self.trace, edge, result); - let probe_length = self.tracer.layout.band_length(self.trace.band); + let probe_length = self + .tracer + .layout + .lock() + .unwrap() + .band_length(self.trace.band); if result.is_ok() { self.tracer.undo_step(&mut self.trace); @@ -122,23 +134,20 @@ impl<'a, RO: RouterObserverTrait, R: RulesTrait> AstarStrategy<&Navmesh, f64> fn estimate_cost(&mut self, vertex: VertexIndex) -> f64 { self.observer.on_estimate(&self.tracer, vertex); + + let layout = self.tracer.layout.lock().unwrap(); let start_point = PrimitiveIndex::from(vertex) - .primitive(self.tracer.layout.drawing()) - .shape() - .center(); - let end_point = self - .tracer - .layout - .drawing() - .primitive(self.to) + .primitive(layout.drawing()) .shape() .center(); + let end_point = layout.drawing().primitive(self.to).shape().center(); + end_point.euclidean_distance(&start_point) } } impl Router { - pub fn new(layout: Layout) -> Self { + pub fn new(layout: Arc>>) -> Self { Router { layout } } @@ -152,13 +161,14 @@ impl Router { // XXX: Should we actually store the mesh? May be useful for debugging, but doesn't look // right. //self.mesh.triangulate(&self.layout)?; - let mesh = Navmesh::new(&self.layout).map_err(|err| RoutingError { + let mesh = Navmesh::new(&self.layout.lock().unwrap()).map_err(|err| RoutingError { from, to, source: err.into(), })?; let mut tracer = self.tracer(&mesh); + let trace = tracer.start(from, width); let band = trace.band; @@ -183,22 +193,24 @@ impl Router { width: f64, observer: &mut impl RouterObserverTrait, ) -> Result { - let from_dot = self.layout.band_from(band); - let to_dot = self.layout.band_to(band).unwrap(); - self.layout.remove_band(band); - self.layout.move_dot(to_dot.into(), to).unwrap(); // TODO: Remove `.unwrap()`. + let (from_dot, to_dot) = { + let mut layout = self.layout.lock().unwrap(); + + let from_dot = layout.band_from(band); + let to_dot = layout.band_to(band).unwrap(); + layout.remove_band(band); + layout.move_dot(to_dot.into(), to).unwrap(); // TODO: Remove `.unwrap()`. + (from_dot, to_dot) + }; + self.route_band(from_dot, to_dot, width, observer) } pub fn tracer<'a>(&'a mut self, mesh: &'a Navmesh) -> Tracer { - Tracer::new(&mut self.layout, mesh) + Tracer::new(self.layout.clone(), mesh) } - pub fn layout(&self) -> &Layout { - &self.layout - } - - pub fn layout_mut(&mut self) -> &mut Layout { - &mut self.layout + pub fn layout(&self) -> Arc>> { + self.layout.clone() } } diff --git a/src/router/tracer.rs b/src/router/tracer.rs index a39d9fe..906d713 100644 --- a/src/router/tracer.rs +++ b/src/router/tracer.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, Mutex}; + use contracts::debug_ensures; use crate::{ @@ -24,17 +26,17 @@ pub struct Trace { #[derive(Debug)] pub struct Tracer<'a, R: RulesTrait> { - pub layout: &'a mut Layout, + pub layout: Arc>>, pub mesh: &'a Navmesh, } impl<'a, R: RulesTrait> Tracer<'a, R> { - pub fn new(layout: &'a mut Layout, mesh: &'a Navmesh) -> Self { + pub fn new(layout: Arc>>, mesh: &'a Navmesh) -> Self { Tracer { layout, mesh } } pub fn start(&mut self, from: FixedDotIndex, width: f64) -> Trace { - let band = self.layout.start_band(from); + let band = self.layout.lock().unwrap().start_band(from); Trace { path: vec![from.into()], head: BareHead { dot: from }.into(), @@ -49,8 +51,8 @@ impl<'a, R: RulesTrait> Tracer<'a, R> { into: FixedDotIndex, width: f64, ) -> Result<(), DrawException> { - self.draw().finish_in_dot(trace.head, into, width)?; - Ok(self.layout.finish_band(trace.band, into)) + Draw::new(&mut self.layout.lock().unwrap()).finish_in_dot(trace.head, into, width)?; + Ok(self.layout.lock().unwrap().finish_band(trace.band, into)) } #[debug_ensures(ret.is_ok() -> trace.path.len() == path.len())] @@ -131,7 +133,11 @@ impl<'a, R: RulesTrait> Tracer<'a, R> { around: FixedDotIndex, width: f64, ) -> Result { - let head = self.draw().segbend_around_dot(head, around.into(), width)?; + let head = Draw::new(&mut self.layout.lock().unwrap()).segbend_around_dot( + head, + around.into(), + width, + )?; Ok(head) } @@ -141,9 +147,11 @@ impl<'a, R: RulesTrait> Tracer<'a, R> { around: LooseBendIndex, width: f64, ) -> Result { - let head = self - .draw() - .segbend_around_bend(head, around.into(), width)?; + let head = Draw::new(&mut self.layout.lock().unwrap()).segbend_around_bend( + head, + around.into(), + width, + )?; Ok(head) } @@ -151,15 +159,13 @@ impl<'a, R: RulesTrait> Tracer<'a, R> { #[debug_ensures(trace.path.len() == old(trace.path.len() - 1))] pub fn undo_step(&mut self, trace: &mut Trace) { if let Head::Segbend(head) = trace.head { - trace.head = self.draw().undo_segbend(head).unwrap(); + trace.head = Draw::new(&mut self.layout.lock().unwrap()) + .undo_segbend(head) + .unwrap(); } else { panic!(); } trace.path.pop(); } - - fn draw(&mut self) -> Draw { - Draw::new(&mut self.layout) - } }