// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT //! Manages autorouting of ratlines in a layout, tracking status and processed //! routing steps. use std::ops::ControlFlow; use petgraph::graph::EdgeIndex; use crate::{ board::AccessMesadata, drawing::{band::BandTermsegIndex, graph::PrimitiveIndex, Collect}, geometry::primitive::PrimitiveShape, layout::LayoutEdit, router::{ navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper, RouteStepper, Router, }, stepper::Step, }; use super::{invoker::GetDebugOverlayData, Autorouter, AutorouterError, AutorouterOptions}; /// Represents the current status of the autoroute operation. pub enum AutorouteContinueStatus { /// The autoroute is currently running and in progress. Running, /// A specific segment has been successfully routed. Routed(BandTermsegIndex), /// A specific segment was already routed and has been skipped. Skipped(BandTermsegIndex), } /// Manages the autorouting process across multiple ratlines. pub struct AutorouteExecutionStepper { /// An iterator over ratlines that tracks which segments still need to be routed. ratlines_iter: Box>>, /// The layer on which the routing should be done. layer: usize, /// The options for the autorouting process, defining how routing should be carried out. options: AutorouterOptions, /// Stores the current route being processed, if any. route: Option, /// Keeps track of the current ratline being routed, if one is active. curr_ratline: Option>, } impl AutorouteExecutionStepper { /// Initializes a new [`AutorouteExecutionStepper`] instance. /// /// This method sets up the routing process by accepting the execution properties. /// It prepares the first ratline to route /// and stores the associated data for future routing steps. pub fn new( autorouter: &mut Autorouter, ratlines: impl IntoIterator> + 'static, layer: usize, options: AutorouterOptions, ) -> Result { let mut ratlines_iter = Box::new(ratlines.into_iter()); let Some(curr_ratline) = ratlines_iter.next() else { return Err(AutorouterError::NothingToRoute); }; let (origin, destination) = autorouter.ratline_endpoints(curr_ratline); let mut router = Router::new(autorouter.board.layout_mut(), options.router_options); Ok(Self { ratlines_iter, layer, options, route: Some(router.route( LayoutEdit::new(), origin, destination, layer, options.router_options.routed_band_width, )?), curr_ratline: Some(curr_ratline), }) } } impl Step, Option, AutorouteContinueStatus> for AutorouteExecutionStepper { type Error = AutorouterError; fn step( &mut self, autorouter: &mut Autorouter, ) -> Result, AutorouteContinueStatus>, AutorouterError> { let Some(curr_ratline) = self.curr_ratline else { let recorder = if let Some(taken_route) = self.route.take() { let (_thetastar, navcord, ..) = taken_route.dissolve(); navcord.recorder } else { LayoutEdit::new() }; return Ok(ControlFlow::Break(Some(recorder))); }; let Some(ref mut route) = self.route else { // Shouldn't happen. return Ok(ControlFlow::Break(None)); }; let (source, target) = autorouter.ratline_endpoints(curr_ratline); let ret = if let Some(band_termseg) = autorouter.board.band_between_nodes(source, target) { AutorouteContinueStatus::Skipped(band_termseg[false]) } else { let band_termseg = { let mut router = Router::new(autorouter.board.layout_mut(), self.options.router_options); let ControlFlow::Break(band_termseg) = route.step(&mut router)? else { return Ok(ControlFlow::Continue(AutorouteContinueStatus::Running)); }; band_termseg }; let band = autorouter .board .layout() .drawing() .loose_band_uid(band_termseg.into()) .expect("a completely routed band should've Seg's as ends"); autorouter .ratsnest .assign_band_termseg_to_ratline(self.curr_ratline.unwrap(), band_termseg); autorouter .board .try_set_band_between_nodes(source, target, band); AutorouteContinueStatus::Routed(band_termseg) }; if let Some(new_ratline) = self.ratlines_iter.next() { let (source, target) = autorouter.ratline_endpoints(new_ratline); let mut router = Router::new(autorouter.board.layout_mut(), self.options.router_options); self.curr_ratline = Some(new_ratline); let recorder = if let Some(taken_route) = self.route.take() { let (_thetastar, navcord, ..) = taken_route.dissolve(); navcord.recorder } else { LayoutEdit::new() }; self.route = Some(router.route( recorder, source, target, self.layer, self.options.router_options.routed_band_width, )?); } else { self.curr_ratline = None; } Ok(ControlFlow::Continue(ret)) } } impl GetDebugOverlayData for AutorouteExecutionStepper { fn maybe_thetastar(&self) -> Option<&ThetastarStepper> { self.route.as_ref().map(|route| route.thetastar()) } fn maybe_navcord(&self) -> Option<&Navcord> { self.route.as_ref().map(|route| route.navcord()) } fn ghosts(&self) -> &[PrimitiveShape] { self.route.as_ref().map_or(&[], |route| route.ghosts()) } fn obstacles(&self) -> &[PrimitiveIndex] { self.route.as_ref().map_or(&[], |route| route.obstacles()) } }