autorouter: put `Layout` in a mutex

This commit is contained in:
Mikolaj Wielgus 2024-05-04 18:02:34 +02:00
parent ea854abfc7
commit a2c8ae7e70
4 changed files with 164 additions and 149 deletions

View File

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use geo::Point; use geo::Point;
use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use petgraph::visit::{EdgeRef, IntoEdgeReferences};
use spade::InsertionError; use spade::InsertionError;
@ -16,9 +18,10 @@ pub struct Autorouter<R: RulesTrait> {
} }
impl<R: RulesTrait> Autorouter<R> { impl<R: RulesTrait> Autorouter<R> {
pub fn new(layout: Layout<R>) -> Result<Self, InsertionError> { pub fn new(layout: Arc<Mutex<Layout<R>>>) -> Result<Self, InsertionError> {
let ratsnest = Ratsnest::new(&layout.lock().unwrap())?;
Ok(Self { Ok(Self {
ratsnest: Ratsnest::new(&layout)?, ratsnest,
router: Router::new(layout), router: Router::new(layout),
}) })
} }
@ -38,27 +41,27 @@ impl<R: RulesTrait> Autorouter<R> {
.unwrap() .unwrap()
.vertex_index(); .vertex_index();
let from_dot = match from { let (from_dot, to_dot) = {
RatsnestVertexIndex::FixedDot(dot) => dot, let layout = self.router.layout();
RatsnestVertexIndex::Zone(zone) => self.router.layout_mut().zone_apex(zone), let mut layout = layout.lock().unwrap();
};
let to_dot = match to { let from_dot = match from {
RatsnestVertexIndex::FixedDot(dot) => dot, RatsnestVertexIndex::FixedDot(dot) => dot,
RatsnestVertexIndex::Zone(zone) => self.router.layout_mut().zone_apex(zone), 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); 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<R> { pub fn router(&self) -> &Router<R> {
&self.router &self.router
} }

View File

@ -3,7 +3,10 @@ use geo::point;
use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use petgraph::visit::{EdgeRef, IntoEdgeReferences};
use std::{ use std::{
future::Future, future::Future,
sync::mpsc::{channel, Receiver, Sender}, sync::{
mpsc::{channel, Receiver, Sender},
Arc, Mutex,
},
}; };
use topola::{ use topola::{
@ -40,7 +43,7 @@ pub struct App {
overlay: Option<Overlay>, overlay: Option<Overlay>,
#[serde(skip)] #[serde(skip)]
autorouter: Option<Autorouter<DsnRules>>, layout: Option<Arc<Mutex<Layout<DsnRules>>>>,
#[serde(skip)] #[serde(skip)]
text_channel: (Sender<String>, Receiver<String>), text_channel: (Sender<String>, Receiver<String>),
@ -53,7 +56,7 @@ impl Default for App {
fn default() -> Self { fn default() -> Self {
Self { Self {
overlay: None, overlay: None,
autorouter: None, layout: None,
text_channel: channel(), text_channel: channel(),
from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), 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 design = DsnDesign::load_from_string(file_contents).unwrap();
let layout = design.make_layout(); let layout = design.make_layout();
self.overlay = Some(Overlay::new(&layout).unwrap()); self.overlay = Some(Overlay::new(&layout).unwrap());
self.autorouter = Some(Autorouter::new(layout).unwrap()); self.layout = Some(Arc::new(Mutex::new(layout)));
} }
} else { } else {
if let Ok(path) = self.text_channel.1.try_recv() { if let Ok(path) = self.text_channel.1.try_recv() {
let design = DsnDesign::load_from_file(&path).unwrap(); let design = DsnDesign::load_from_file(&path).unwrap();
let layout = design.make_layout(); let layout = design.make_layout();
self.overlay = Some(Overlay::new(&layout).unwrap()); 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(); ui.separator();
if ui.button("Autoroute").clicked() { if ui.button("Autoroute").clicked() {
if let Some(ref mut autorouter) = &mut self.autorouter { if let Some(layout_arc_mutex) = &self.layout {
autorouter.autoroute(&mut EmptyRouterObserver {}); 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 transform = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect);
let mut painter = Painter::new(ui, transform); let mut painter = Painter::new(ui, transform);
if let (Some(autorouter), Some(overlay)) = (&self.autorouter, &mut self.overlay) { if let Some(layout_arc_mutex) = &self.layout {
if ctx.input(|i| i.pointer.any_click()) { if let (layout, Some(overlay)) =
overlay.click( (&layout_arc_mutex.lock().unwrap(), &mut self.overlay)
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)
{ {
let shape = primitive if ctx.input(|i| i.pointer.any_click()) {
.primitive(autorouter.router().layout().drawing()) overlay.click(
.shape(); layout,
point! {x: latest_pos.x as f64, y: -latest_pos.y as f64},
);
}
let color = if overlay for primitive in layout.drawing().layer_primitive_nodes(1) {
.selection() let shape = primitive.primitive(layout.drawing()).shape();
.contains(&GenericNode::Primitive(primitive))
{ let color = if overlay
egui::Color32::from_rgb(100, 100, 255) .selection()
} else { .contains(&GenericNode::Primitive(primitive))
egui::Color32::from_rgb(52, 52, 200) {
}; egui::Color32::from_rgb(100, 100, 255)
painter.paint_shape(&shape, color); } 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!();
} }
}) })
}); });

View File

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use geo::geometry::Point; use geo::geometry::Point;
use geo::EuclideanDistance; use geo::EuclideanDistance;
use petgraph::visit::EdgeRef; use petgraph::visit::EdgeRef;
@ -56,7 +58,7 @@ pub trait RouterObserverTrait<R: RulesTrait> {
} }
pub struct Router<R: RulesTrait> { pub struct Router<R: RulesTrait> {
layout: Layout<R>, layout: Arc<Mutex<Layout<R>>>,
} }
struct RouterAstarStrategy<'a, RO: RouterObserverTrait<R>, R: RulesTrait> { struct RouterAstarStrategy<'a, RO: RouterObserverTrait<R>, R: RulesTrait> {
@ -103,14 +105,24 @@ impl<'a, RO: RouterObserverTrait<R>, R: RulesTrait> AstarStrategy<&Navmesh, f64>
return None; 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 width = self.trace.width;
let result = self.tracer.step(&mut self.trace, edge.target(), width); let result = self.tracer.step(&mut self.trace, edge.target(), width);
self.observer self.observer
.on_probe(&self.tracer, &self.trace, edge, result); .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() { if result.is_ok() {
self.tracer.undo_step(&mut self.trace); self.tracer.undo_step(&mut self.trace);
@ -122,23 +134,20 @@ impl<'a, RO: RouterObserverTrait<R>, R: RulesTrait> AstarStrategy<&Navmesh, f64>
fn estimate_cost(&mut self, vertex: VertexIndex) -> f64 { fn estimate_cost(&mut self, vertex: VertexIndex) -> f64 {
self.observer.on_estimate(&self.tracer, vertex); self.observer.on_estimate(&self.tracer, vertex);
let layout = self.tracer.layout.lock().unwrap();
let start_point = PrimitiveIndex::from(vertex) let start_point = PrimitiveIndex::from(vertex)
.primitive(self.tracer.layout.drawing()) .primitive(layout.drawing())
.shape()
.center();
let end_point = self
.tracer
.layout
.drawing()
.primitive(self.to)
.shape() .shape()
.center(); .center();
let end_point = layout.drawing().primitive(self.to).shape().center();
end_point.euclidean_distance(&start_point) end_point.euclidean_distance(&start_point)
} }
} }
impl<R: RulesTrait> Router<R> { impl<R: RulesTrait> Router<R> {
pub fn new(layout: Layout<R>) -> Self { pub fn new(layout: Arc<Mutex<Layout<R>>>) -> Self {
Router { layout } Router { layout }
} }
@ -152,13 +161,14 @@ impl<R: RulesTrait> Router<R> {
// XXX: Should we actually store the mesh? May be useful for debugging, but doesn't look // XXX: Should we actually store the mesh? May be useful for debugging, but doesn't look
// right. // right.
//self.mesh.triangulate(&self.layout)?; //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, from,
to, to,
source: err.into(), source: err.into(),
})?; })?;
let mut tracer = self.tracer(&mesh); let mut tracer = self.tracer(&mesh);
let trace = tracer.start(from, width); let trace = tracer.start(from, width);
let band = trace.band; let band = trace.band;
@ -183,22 +193,24 @@ impl<R: RulesTrait> Router<R> {
width: f64, width: f64,
observer: &mut impl RouterObserverTrait<R>, observer: &mut impl RouterObserverTrait<R>,
) -> Result<BandIndex, RoutingError> { ) -> Result<BandIndex, RoutingError> {
let from_dot = self.layout.band_from(band); let (from_dot, to_dot) = {
let to_dot = self.layout.band_to(band).unwrap(); let mut layout = self.layout.lock().unwrap();
self.layout.remove_band(band);
self.layout.move_dot(to_dot.into(), to).unwrap(); // TODO: Remove `.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) self.route_band(from_dot, to_dot, width, observer)
} }
pub fn tracer<'a>(&'a mut self, mesh: &'a Navmesh) -> Tracer<R> { pub fn tracer<'a>(&'a mut self, mesh: &'a Navmesh) -> Tracer<R> {
Tracer::new(&mut self.layout, mesh) Tracer::new(self.layout.clone(), mesh)
} }
pub fn layout(&self) -> &Layout<R> { pub fn layout(&self) -> Arc<Mutex<Layout<R>>> {
&self.layout self.layout.clone()
}
pub fn layout_mut(&mut self) -> &mut Layout<R> {
&mut self.layout
} }
} }

View File

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use contracts::debug_ensures; use contracts::debug_ensures;
use crate::{ use crate::{
@ -24,17 +26,17 @@ pub struct Trace {
#[derive(Debug)] #[derive(Debug)]
pub struct Tracer<'a, R: RulesTrait> { pub struct Tracer<'a, R: RulesTrait> {
pub layout: &'a mut Layout<R>, pub layout: Arc<Mutex<Layout<R>>>,
pub mesh: &'a Navmesh, pub mesh: &'a Navmesh,
} }
impl<'a, R: RulesTrait> Tracer<'a, R> { impl<'a, R: RulesTrait> Tracer<'a, R> {
pub fn new(layout: &'a mut Layout<R>, mesh: &'a Navmesh) -> Self { pub fn new(layout: Arc<Mutex<Layout<R>>>, mesh: &'a Navmesh) -> Self {
Tracer { layout, mesh } Tracer { layout, mesh }
} }
pub fn start(&mut self, from: FixedDotIndex, width: f64) -> Trace { 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 { Trace {
path: vec![from.into()], path: vec![from.into()],
head: BareHead { dot: from }.into(), head: BareHead { dot: from }.into(),
@ -49,8 +51,8 @@ impl<'a, R: RulesTrait> Tracer<'a, R> {
into: FixedDotIndex, into: FixedDotIndex,
width: f64, width: f64,
) -> Result<(), DrawException> { ) -> Result<(), DrawException> {
self.draw().finish_in_dot(trace.head, into, width)?; Draw::new(&mut self.layout.lock().unwrap()).finish_in_dot(trace.head, into, width)?;
Ok(self.layout.finish_band(trace.band, into)) Ok(self.layout.lock().unwrap().finish_band(trace.band, into))
} }
#[debug_ensures(ret.is_ok() -> trace.path.len() == path.len())] #[debug_ensures(ret.is_ok() -> trace.path.len() == path.len())]
@ -131,7 +133,11 @@ impl<'a, R: RulesTrait> Tracer<'a, R> {
around: FixedDotIndex, around: FixedDotIndex,
width: f64, width: f64,
) -> Result<SegbendHead, DrawException> { ) -> Result<SegbendHead, DrawException> {
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) Ok(head)
} }
@ -141,9 +147,11 @@ impl<'a, R: RulesTrait> Tracer<'a, R> {
around: LooseBendIndex, around: LooseBendIndex,
width: f64, width: f64,
) -> Result<SegbendHead, DrawException> { ) -> Result<SegbendHead, DrawException> {
let head = self let head = Draw::new(&mut self.layout.lock().unwrap()).segbend_around_bend(
.draw() head,
.segbend_around_bend(head, around.into(), width)?; around.into(),
width,
)?;
Ok(head) Ok(head)
} }
@ -151,15 +159,13 @@ impl<'a, R: RulesTrait> Tracer<'a, R> {
#[debug_ensures(trace.path.len() == old(trace.path.len() - 1))] #[debug_ensures(trace.path.len() == old(trace.path.len() - 1))]
pub fn undo_step(&mut self, trace: &mut Trace) { pub fn undo_step(&mut self, trace: &mut Trace) {
if let Head::Segbend(head) = trace.head { 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 { } else {
panic!(); panic!();
} }
trace.path.pop(); trace.path.pop();
} }
fn draw(&mut self) -> Draw<R> {
Draw::new(&mut self.layout)
}
} }