autorouter: if band removal fails, propagate error instead of panicking

This commit is contained in:
Mikolaj Wielgus 2024-08-30 16:32:37 +02:00
parent 2105e8c7cf
commit f7f2e8ea49
7 changed files with 60 additions and 38 deletions

View File

@ -4,7 +4,11 @@ use thiserror::Error;
use crate::{ use crate::{
board::{mesadata::AccessMesadata, Board}, board::{mesadata::AccessMesadata, Board},
drawing::{dot::FixedDotIndex, Infringement}, drawing::{
band::{BandTermsegIndex, BandUid},
dot::FixedDotIndex,
Infringement,
},
layout::via::ViaWeight, layout::via::ViaWeight,
router::{navmesh::NavmeshError, RouterError}, router::{navmesh::NavmeshError, RouterError},
triangulation::GetTrianvertexNodeIndex, triangulation::GetTrianvertexNodeIndex,
@ -30,6 +34,8 @@ pub enum AutorouterError {
Router(#[from] RouterError), Router(#[from] RouterError),
#[error("could not place via")] #[error("could not place via")]
CouldNotPlaceVia(#[from] Infringement), CouldNotPlaceVia(#[from] Infringement),
#[error("could not remove band")]
CouldNotRemoveBand(BandTermsegIndex),
#[error("need exactly two ratlines")] #[error("need exactly two ratlines")]
NeedExactlyTwoRatlines, NeedExactlyTwoRatlines,
} }
@ -56,11 +62,14 @@ impl<M: AccessMesadata> Autorouter<M> {
Autoroute::new(self, ratlines) Autoroute::new(self, ratlines)
} }
pub fn undo_autoroute(&mut self, selection: &PinSelection) { pub fn undo_autoroute(&mut self, selection: &PinSelection) -> Result<(), AutorouterError> {
self.undo_autoroute_ratlines(self.selected_ratlines(selection)) self.undo_autoroute_ratlines(self.selected_ratlines(selection))
} }
pub(super) fn undo_autoroute_ratlines(&mut self, ratlines: Vec<EdgeIndex<usize>>) { pub(super) fn undo_autoroute_ratlines(
&mut self,
ratlines: Vec<EdgeIndex<usize>>,
) -> Result<(), AutorouterError> {
for ratline in ratlines.iter() { for ratline in ratlines.iter() {
let band = self let band = self
.ratsnest .ratsnest
@ -69,8 +78,13 @@ impl<M: AccessMesadata> Autorouter<M> {
.unwrap() .unwrap()
.band_termseg .band_termseg
.unwrap(); .unwrap();
self.board.layout_mut().remove_band(band); self.board
.layout_mut()
.remove_band(band)
.map_err(|_| AutorouterError::CouldNotRemoveBand(band))?;
} }
Ok(())
} }
pub fn place_via(&self, weight: ViaWeight) -> Result<PlaceVia, AutorouterError> { pub fn place_via(&self, weight: ViaWeight) -> Result<PlaceVia, AutorouterError> {

View File

@ -289,11 +289,17 @@ impl<M: AccessMesadata> Invoker<M> {
let command = self.history.last_done()?; let command = self.history.last_done()?;
match command { match command {
Command::Autoroute(ref selection) => self.autorouter.undo_autoroute(selection), Command::Autoroute(ref selection) => {
Command::PlaceVia(weight) => self.autorouter.undo_place_via(*weight), self.autorouter.undo_autoroute(selection);
Command::RemoveBands(ref selection) => self.autorouter.undo_remove_bands(selection), }
Command::CompareDetours(..) => (), Command::PlaceVia(weight) => {
Command::MeasureLength(..) => (), self.autorouter.undo_place_via(*weight);
}
Command::RemoveBands(ref selection) => {
self.autorouter.undo_remove_bands(selection);
}
Command::CompareDetours(..) => {}
Command::MeasureLength(..) => {}
} }
Ok::<(), InvokerError>(self.history.undo()?) Ok::<(), InvokerError>(self.history.undo()?)

View File

@ -22,7 +22,7 @@ use topola::{
graph::{MakePrimitive, PrimitiveIndex}, graph::{MakePrimitive, PrimitiveIndex},
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules, rules::AccessRules,
Drawing, Infringement, LayoutException, Drawing, DrawingException, Infringement,
}, },
geometry::{ geometry::{
compound::ManageCompounds, compound::ManageCompounds,
@ -152,8 +152,6 @@ impl App {
invoker.replay(serde_json::from_reader(bufread).unwrap()) invoker.replay(serde_json::from_reader(bufread).unwrap())
} }
// TODO: Make Save History work too.
if let Some(ref mut execute) = self.maybe_execute { if let Some(ref mut execute) = self.maybe_execute {
let status = match execute.step(invoker) { let status = match execute.step(invoker) {
Ok(status) => status, Ok(status) => status,

View File

@ -42,7 +42,7 @@ use super::head::{Head, HeadRef};
#[enum_dispatch] #[enum_dispatch]
#[derive(Error, Debug, Clone, Copy)] #[derive(Error, Debug, Clone, Copy)]
pub enum LayoutException { pub enum DrawingException {
#[error(transparent)] #[error(transparent)]
NoTangents(#[from] NoTangents), NoTangents(#[from] NoTangents),
#[error(transparent)] #[error(transparent)]
@ -91,7 +91,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
} }
} }
pub fn remove_band(&mut self, band: BandTermsegIndex) { pub fn remove_band(&mut self, band: BandTermsegIndex) -> Result<(), DrawingException> {
match band { match band {
BandTermsegIndex::Straight(seg) => { BandTermsegIndex::Straight(seg) => {
self.geometry_with_rtree.remove_seg(seg.into()); self.geometry_with_rtree.remove_seg(seg.into());
@ -148,10 +148,12 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
} }
for outer in outers { for outer in outers {
self.update_this_and_outward_bows(outer).unwrap(); // Must never fail. self.update_this_and_outward_bows(outer)?;
} }
} }
} }
Ok(())
} }
#[debug_ensures(ret.is_ok() -> self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count() + 1))] #[debug_ensures(ret.is_ok() -> self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count() + 1))]
@ -272,7 +274,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
around: GearIndex, around: GearIndex,
weight: LooseBendWeight, weight: LooseBendWeight,
infringables: Option<&[PrimitiveIndex]>, infringables: Option<&[PrimitiveIndex]>,
) -> Result<LooseBendIndex, LayoutException> { ) -> Result<LooseBendIndex, DrawingException> {
// It makes no sense to wrap something around or under one of its connectables. // It makes no sense to wrap something around or under one of its connectables.
// //
if let Some(net) = weight.maybe_net { if let Some(net) = weight.maybe_net {
@ -399,7 +401,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
seg_weight: SeqLooseSegWeight, seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight, bend_weight: LooseBendWeight,
cw: bool, cw: bool,
) -> Result<Cane, LayoutException> { ) -> Result<Cane, DrawingException> {
let maybe_next_gear = around.ref_(self).next_gear(); let maybe_next_gear = around.ref_(self).next_gear();
let cane = self.add_cane_with_infringables( let cane = self.add_cane_with_infringables(
from, from,
@ -430,7 +432,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
return Err(collision.into()); return Err(collision.into());
} }
Ok::<Cane, LayoutException>(cane) Ok::<Cane, DrawingException>(cane)
} }
#[debug_ensures(self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count()))] #[debug_ensures(self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count()))]
@ -438,7 +440,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
fn update_this_and_outward_bows( fn update_this_and_outward_bows(
&mut self, &mut self,
around: LooseBendIndex, around: LooseBendIndex,
) -> Result<(), LayoutException> { ) -> Result<(), DrawingException> {
// FIXME: Fail gracefully on infringement. // FIXME: Fail gracefully on infringement.
let mut maybe_rail = Some(around); let mut maybe_rail = Some(around);
@ -536,7 +538,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
maybe_rail = self.primitive(rail).outer(); maybe_rail = self.primitive(rail).outer();
} }
Ok::<(), LayoutException>(()) Ok::<(), DrawingException>(())
} }
#[debug_ensures(ret.is_ok() -> self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count() + 4))] #[debug_ensures(ret.is_ok() -> self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count() + 4))]
@ -551,7 +553,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
seg_weight: SeqLooseSegWeight, seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight, bend_weight: LooseBendWeight,
cw: bool, cw: bool,
) -> Result<Cane, LayoutException> { ) -> Result<Cane, DrawingException> {
self.add_cane_with_infringables( self.add_cane_with_infringables(
from, from,
around, around,
@ -576,7 +578,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
bend_weight: LooseBendWeight, bend_weight: LooseBendWeight,
cw: bool, cw: bool,
infringables: Option<&[PrimitiveIndex]>, infringables: Option<&[PrimitiveIndex]>,
) -> Result<Cane, LayoutException> { ) -> Result<Cane, DrawingException> {
let seg_to = self.add_dot_with_infringables(dot_weight, infringables)?; let seg_to = self.add_dot_with_infringables(dot_weight, infringables)?;
let seg = self let seg = self
.add_seg_with_infringables(from, seg_to.into(), seg_weight, infringables) .add_seg_with_infringables(from, seg_to.into(), seg_weight, infringables)
@ -604,7 +606,7 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
err err
})?; })?;
Ok::<Cane, LayoutException>(Cane { Ok::<Cane, DrawingException>(Cane {
seg, seg,
dot: seg_to, dot: seg_to,
bend, bend,

View File

@ -17,7 +17,7 @@ use crate::{
FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SeqLooseSegIndex, FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SeqLooseSegIndex,
SeqLooseSegWeight, SeqLooseSegWeight,
}, },
Drawing, Infringement, LayoutException, Drawing, DrawingException, Infringement,
}, },
geometry::{compound::ManageCompounds, shape::MeasureLength, GenericNode}, geometry::{compound::ManageCompounds, shape::MeasureLength, GenericNode},
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
@ -54,7 +54,7 @@ impl<R: AccessRules> Layout<R> {
seg_weight: SeqLooseSegWeight, seg_weight: SeqLooseSegWeight,
bend_weight: LooseBendWeight, bend_weight: LooseBendWeight,
cw: bool, cw: bool,
) -> Result<Cane, LayoutException> { ) -> Result<Cane, DrawingException> {
self.drawing self.drawing
.insert_cane(from, around, dot_weight, seg_weight, bend_weight, cw) .insert_cane(from, around, dot_weight, seg_weight, bend_weight, cw)
} }
@ -204,8 +204,8 @@ impl<R: AccessRules> Layout<R> {
) )
} }
pub fn remove_band(&mut self, band: BandTermsegIndex) { pub fn remove_band(&mut self, band: BandTermsegIndex) -> Result<(), DrawingException> {
self.drawing.remove_band(band); self.drawing.remove_band(band)
} }
pub fn polys<W: 'static>( pub fn polys<W: 'static>(

View File

@ -14,7 +14,7 @@ use crate::{
primitive::GetOtherJoint, primitive::GetOtherJoint,
rules::AccessRules, rules::AccessRules,
seg::{LoneLooseSegWeight, SeqLooseSegWeight}, seg::{LoneLooseSegWeight, SeqLooseSegWeight},
Infringement, LayoutException, DrawingException, Infringement,
}, },
layout::Layout, layout::Layout,
math::{Circle, NoTangents}, math::{Circle, NoTangents},
@ -26,9 +26,9 @@ pub enum DrawException {
NoTangents(#[from] NoTangents), NoTangents(#[from] NoTangents),
// TODO add real error messages + these should eventually use Display // TODO add real error messages + these should eventually use Display
#[error("cannot finish in {0:?}")] #[error("cannot finish in {0:?}")]
CannotFinishIn(FixedDotIndex, #[source] LayoutException), CannotFinishIn(FixedDotIndex, #[source] DrawingException),
#[error("cannot wrap around {0:?}")] #[error("cannot wrap around {0:?}")]
CannotWrapAround(GearIndex, #[source] LayoutException), CannotWrapAround(GearIndex, #[source] DrawingException),
} }
pub struct Draw<'a, R: AccessRules> { pub struct Draw<'a, R: AccessRules> {
@ -158,7 +158,7 @@ impl<'a, R: AccessRules> Draw<'a, R> {
cw: bool, cw: bool,
width: f64, width: f64,
offset: f64, offset: f64,
) -> Result<CaneHead, LayoutException> { ) -> Result<CaneHead, DrawingException> {
let head = self.extend_head(head, from)?; let head = self.extend_head(head, from)?;
self.cane(head, around, to, cw, width, offset) self.cane(head, around, to, cw, width, offset)
} }
@ -183,7 +183,7 @@ impl<'a, R: AccessRules> Draw<'a, R> {
cw: bool, cw: bool,
width: f64, width: f64,
offset: f64, offset: f64,
) -> Result<CaneHead, LayoutException> { ) -> Result<CaneHead, DrawingException> {
let layer = head.face().primitive(self.layout.drawing()).layer(); let layer = head.face().primitive(self.layout.drawing()).layer();
let maybe_net = head.face().primitive(self.layout.drawing()).maybe_net(); let maybe_net = head.face().primitive(self.layout.drawing()).maybe_net();
let cane = self.layout.insert_cane( let cane = self.layout.insert_cane(
@ -210,7 +210,7 @@ impl<'a, R: AccessRules> Draw<'a, R> {
}, },
cw, cw,
)?; )?;
Ok::<CaneHead, LayoutException>(CaneHead { Ok::<CaneHead, DrawingException>(CaneHead {
face: self face: self
.layout .layout
.drawing() .drawing()

View File

@ -12,7 +12,7 @@ use crate::{
head::GetFace, head::GetFace,
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules, rules::AccessRules,
Collision, Infringement, LayoutException, Collision, DrawingException, Infringement,
}, },
geometry::{ geometry::{
primitive::{AccessPrimitiveShape, PrimitiveShape}, primitive::{AccessPrimitiveShape, PrimitiveShape},
@ -140,12 +140,14 @@ impl<'a, R: AccessRules> AstarStrategy<Navmesh, f64, BandTermsegIndex>
}; };
let (ghost, obstacle) = match layout_err { let (ghost, obstacle) = match layout_err {
LayoutException::NoTangents(..) => return None, DrawingException::NoTangents(..) => return None,
LayoutException::Infringement(Infringement(ghost, obstacle)) => { DrawingException::Infringement(Infringement(ghost, obstacle)) => {
(ghost, obstacle) (ghost, obstacle)
} }
LayoutException::Collision(Collision(ghost, obstacle)) => (ghost, obstacle), DrawingException::Collision(Collision(ghost, obstacle)) => {
LayoutException::AlreadyConnected(..) => return None, (ghost, obstacle)
}
DrawingException::AlreadyConnected(..) => return None,
}; };
self.probe_ghosts = vec![ghost]; self.probe_ghosts = vec![ghost];