mirror of https://codeberg.org/topola/topola.git
autorouter: put `Layout` in a mutex
This commit is contained in:
parent
ea854abfc7
commit
a2c8ae7e70
|
|
@ -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, to_dot) = {
|
||||||
|
let layout = self.router.layout();
|
||||||
|
let mut layout = layout.lock().unwrap();
|
||||||
|
|
||||||
let from_dot = match from {
|
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 {
|
let to_dot = match to {
|
||||||
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),
|
||||||
|
};
|
||||||
|
|
||||||
|
(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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
let layout = layout_arc_mutex.clone();
|
||||||
|
|
||||||
|
execute(async move {
|
||||||
|
let mut autorouter = Autorouter::new(layout).unwrap();
|
||||||
autorouter.autoroute(&mut EmptyRouterObserver {});
|
autorouter.autoroute(&mut EmptyRouterObserver {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,23 +200,19 @@ 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 let (layout, Some(overlay)) =
|
||||||
|
(&layout_arc_mutex.lock().unwrap(), &mut self.overlay)
|
||||||
|
{
|
||||||
if ctx.input(|i| i.pointer.any_click()) {
|
if ctx.input(|i| i.pointer.any_click()) {
|
||||||
overlay.click(
|
overlay.click(
|
||||||
autorouter.router().layout(),
|
layout,
|
||||||
point! {x: latest_pos.x as f64, y: -latest_pos.y as f64},
|
point! {x: latest_pos.x as f64, y: -latest_pos.y as f64},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for primitive in autorouter
|
for primitive in layout.drawing().layer_primitive_nodes(1) {
|
||||||
.router()
|
let shape = primitive.primitive(layout.drawing()).shape();
|
||||||
.layout()
|
|
||||||
.drawing()
|
|
||||||
.layer_primitive_nodes(1)
|
|
||||||
{
|
|
||||||
let shape = primitive
|
|
||||||
.primitive(autorouter.router().layout().drawing())
|
|
||||||
.shape();
|
|
||||||
|
|
||||||
let color = if overlay
|
let color = if overlay
|
||||||
.selection()
|
.selection()
|
||||||
|
|
@ -221,27 +225,18 @@ impl eframe::App for App {
|
||||||
painter.paint_shape(&shape, color);
|
painter.paint_shape(&shape, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
for zone in autorouter.router().layout().layer_zone_nodes(1) {
|
for zone in layout.layer_zone_nodes(1) {
|
||||||
let color = if overlay.selection().contains(&GenericNode::Compound(zone)) {
|
let color =
|
||||||
|
if overlay.selection().contains(&GenericNode::Compound(zone)) {
|
||||||
egui::Color32::from_rgb(100, 100, 255)
|
egui::Color32::from_rgb(100, 100, 255)
|
||||||
} else {
|
} else {
|
||||||
egui::Color32::from_rgb(52, 52, 200)
|
egui::Color32::from_rgb(52, 52, 200)
|
||||||
};
|
};
|
||||||
painter.paint_polygon(
|
painter.paint_polygon(&layout.zone(zone).shape().polygon, color)
|
||||||
&autorouter.router().layout().zone(zone).shape().polygon,
|
|
||||||
color,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for primitive in autorouter
|
for primitive in layout.drawing().layer_primitive_nodes(0) {
|
||||||
.router()
|
let shape = primitive.primitive(layout.drawing()).shape();
|
||||||
.layout()
|
|
||||||
.drawing()
|
|
||||||
.layer_primitive_nodes(0)
|
|
||||||
{
|
|
||||||
let shape = primitive
|
|
||||||
.primitive(autorouter.router().layout().drawing())
|
|
||||||
.shape();
|
|
||||||
|
|
||||||
let color = if overlay
|
let color = if overlay
|
||||||
.selection()
|
.selection()
|
||||||
|
|
@ -254,16 +249,14 @@ impl eframe::App for App {
|
||||||
painter.paint_shape(&shape, color);
|
painter.paint_shape(&shape, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
for zone in autorouter.router().layout().layer_zone_nodes(0) {
|
for zone in layout.layer_zone_nodes(0) {
|
||||||
let color = if overlay.selection().contains(&GenericNode::Compound(zone)) {
|
let color =
|
||||||
|
if overlay.selection().contains(&GenericNode::Compound(zone)) {
|
||||||
egui::Color32::from_rgb(255, 100, 100)
|
egui::Color32::from_rgb(255, 100, 100)
|
||||||
} else {
|
} else {
|
||||||
egui::Color32::from_rgb(200, 52, 52)
|
egui::Color32::from_rgb(200, 52, 52)
|
||||||
};
|
};
|
||||||
painter.paint_polygon(
|
painter.paint_polygon(&layout.zone(zone).shape().polygon, color)
|
||||||
&autorouter.router().layout().zone(zone).shape().polygon,
|
|
||||||
color,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for edge in overlay.ratsnest().graph().edge_references() {
|
for edge in overlay.ratsnest().graph().edge_references() {
|
||||||
|
|
@ -284,6 +277,7 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
//unreachable!();
|
//unreachable!();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue