mirror of https://codeberg.org/topola/topola.git
autorouter: if band removal fails, propagate error instead of panicking
This commit is contained in:
parent
2105e8c7cf
commit
f7f2e8ea49
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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()?)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>(
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue