use petgraph::graph::EdgeIndex; use spade::InsertionError; use thiserror::Error; use crate::{ board::{mesadata::AccessMesadata, Board}, drawing::{dot::FixedDotIndex, Infringement}, layout::via::ViaWeight, router::{navmesh::NavmeshError, RouterError}, triangulation::GetTrianvertexNodeIndex, }; use super::{ autoroute::Autoroute, compare::Compare, place_via::PlaceVia, ratsnest::{Ratsnest, RatvertexIndex}, remove_bands::RemoveBands, selection::{BandSelection, PinSelection}, }; #[derive(Error, Debug, Clone)] pub enum AutorouterError { #[error("nothing to route")] NothingToRoute, #[error(transparent)] Navmesh(#[from] NavmeshError), #[error(transparent)] Router(#[from] RouterError), #[error("could not place via")] CouldNotPlaceVia(#[from] Infringement), #[error("need exactly two ratlines")] NeedExactlyTwoRatlines, } pub struct Autorouter { pub(super) board: Board, pub(super) ratsnest: Ratsnest, } impl Autorouter { pub fn new(board: Board) -> Result { let ratsnest = Ratsnest::new(board.layout())?; Ok(Self { board, ratsnest }) } pub fn autoroute(&mut self, selection: &PinSelection) -> Result { self.autoroute_ratlines(self.selected_ratlines(selection)) } pub(super) fn autoroute_ratlines( &mut self, ratlines: Vec>, ) -> Result { Autoroute::new(self, ratlines) } pub fn undo_autoroute(&mut self, selection: &PinSelection) { self.undo_autoroute_ratlines(self.selected_ratlines(selection)) } pub(super) fn undo_autoroute_ratlines(&mut self, ratlines: Vec>) { for ratline in ratlines.iter() { let band = self .ratsnest .graph() .edge_weight(*ratline) .unwrap() .band_termseg .unwrap(); self.board.layout_mut().remove_band(band); } } pub fn place_via(&self, weight: ViaWeight) -> Result { PlaceVia::new(weight) } pub fn undo_place_via(&mut self, _weight: ViaWeight) { todo!(); } pub fn remove_bands(&self, selection: &BandSelection) -> Result { RemoveBands::new(selection) } pub fn undo_remove_bands(&mut self, _selection: &BandSelection) { todo!(); } pub fn compare(&mut self, selection: &PinSelection) -> Result { self.compare_ratlines(self.selected_ratlines(selection)) } fn compare_ratlines( &mut self, ratlines: Vec>, ) -> Result { let ratline1 = *ratlines .get(0) .ok_or(AutorouterError::NeedExactlyTwoRatlines)?; let ratline2 = *ratlines .get(1) .ok_or(AutorouterError::NeedExactlyTwoRatlines)?; Compare::new(self, ratline1, ratline2) } pub fn ratline_endpoints( &mut self, ratline: EdgeIndex, ) -> (FixedDotIndex, FixedDotIndex) { let (source, target) = self.ratsnest.graph().edge_endpoints(ratline).unwrap(); let source_dot = match self .ratsnest .graph() .node_weight(source) .unwrap() .node_index() { RatvertexIndex::FixedDot(dot) => dot, RatvertexIndex::Poly(poly) => self.board.poly_apex(poly), }; let target_dot = match self .ratsnest .graph() .node_weight(target) .unwrap() .node_index() { RatvertexIndex::FixedDot(dot) => dot, RatvertexIndex::Poly(poly) => self.board.poly_apex(poly), }; (source_dot, target_dot) } fn selected_ratlines(&self, selection: &PinSelection) -> Vec> { self.ratsnest .graph() .edge_indices() .filter(|ratline| { let (source, target) = self.ratsnest.graph().edge_endpoints(*ratline).unwrap(); let source_navvertex = self .ratsnest .graph() .node_weight(source) .unwrap() .node_index(); let to_navvertex = self .ratsnest .graph() .node_weight(target) .unwrap() .node_index(); selection.contains_node(&self.board, source_navvertex.into()) && selection.contains_node(&self.board, to_navvertex.into()) }) .collect() } pub fn board(&self) -> &Board { &self.board } pub fn ratsnest(&self) -> &Ratsnest { &self.ratsnest } }