// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT //! Manages the execution of routing commands within the autorouting system. use std::ops::ControlFlow; use contracts_try::debug_requires; use derive_getters::{Dissolve, Getters}; use enum_dispatch::enum_dispatch; use geo::geometry::LineString; use thiserror::Error; use crate::{ board::AccessMesadata, drawing::graph::PrimitiveIndex, geometry::{edit::Edit, primitive::PrimitiveShape}, graph::GenericIndex, layout::poly::PolyWeight, router::{ navcord::Navcord, navmesh::{Navmesh, NavnodeIndex}, ng, thetastar::ThetastarStepper, }, stepper::Step, }; use super::{ compare_detours::CompareDetoursExecutionStepper, execution::{Command, ExecutionStepper}, history::{History, HistoryError}, measure_length::MeasureLengthExecutionStepper, multilayer_reconfigurator::MultilayerAutorouteReconfigurator, place_via::PlaceViaExecutionStepper, planar_reconfigurator::PlanarAutorouteReconfigurator, remove_bands::RemoveBandsExecutionStepper, Autorouter, AutorouterError, }; /// Trait for getting the information to display on the debug overlay, #[enum_dispatch] pub trait GetDebugOverlayData { /// Get the Theta* stepper. Most importantly, this gives us the access to /// the navmesh. fn maybe_thetastar(&self) -> Option<&ThetastarStepper> { None } /// Obtain Topological/Planar Navigation Mesh, if present. fn maybe_topo_navmesh(&self) -> Option> { None } /// Get the navcord. This is useful for coloring the currently visited path /// on the navmesh. fn maybe_navcord(&self) -> Option<&Navcord> { None } fn active_polygons(&self) -> &[GenericIndex] { &[] } /// Get ghosts. Ghosts are the shapes that Topola attempted to create but /// failed due to them infringing on other shapes. fn ghosts(&self) -> &[PrimitiveShape] { &[] } /// Get the obstacles that prevented Topola from creating new objects (the /// shapes of these objects can be obtained from the above `.ghosts(...)`) /// method. This allows to highlight what prevented Topola's algorithm from /// going some way. fn obstacles(&self) -> &[PrimitiveIndex] { &[] } fn polygonal_blockers(&self) -> &[LineString] { &[] } /// Get a text string to show on the debug overlay near to a navnode. /// Usually returns None, this method exists only for quick and dirty /// debugging. fn navnode_debug_text(&self, _navnode: NavnodeIndex) -> Option<&str> { None } /// Get a text string to show on the debug overlay near to a navedge. /// Usually returns None, this method exists only for quick and dirty /// debugging. fn navedge_debug_text(&self, _navedge: (NavnodeIndex, NavnodeIndex)) -> Option<&str> { None } } /// Error types that can occur during the invocation of commands. #[derive(Error, Debug, Clone)] pub enum InvokerError { /// Wraps errors related to command history operations. #[error(transparent)] History(#[from] HistoryError), /// Wraps errors related to autorouter operations. #[error(transparent)] Autorouter(#[from] AutorouterError), } #[derive(Getters, Dissolve)] pub struct Invoker { /// Instance for executing desired autorouting commands. pub(super) autorouter: Autorouter, /// History of executed commands. pub(super) history: History, /// Currently ongoing command type. pub(super) ongoing_command: Option, } impl Invoker { /// Creates a new instance of Invoker with the given autorouter instance pub fn new(autorouter: Autorouter) -> Self { Self::new_with_history(autorouter, History::new()) } /// Creates a new instance of Invoker with the given autorouter and history pub fn new_with_history(autorouter: Autorouter, history: History) -> Self { Self { autorouter, history, ongoing_command: None, } } //#[debug_requires(self.ongoing_command.is_none())] /// Executes a command, managing the command status and history. /// /// This function is used to pass the [`Command`] to [`Invoker::execute_stepper`] /// function, and control its execution status. pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> { let mut execute = self.execute_stepper(command)?; loop { let status = execute.step(self)?; if let ControlFlow::Break(..) = status { self.history.set_undone(std::iter::empty()); return Ok(()); } } } /// Pass given command to be executed. /// /// Function used to set given [`Command`] to ongoing state, dispatch and execute it. #[debug_requires(self.ongoing_command.is_none())] pub fn execute_stepper( &mut self, command: Command, ) -> Result, InvokerError> { let execute = self.dispatch_command(&command)?; self.ongoing_command = Some(command); Ok(execute) } #[debug_requires(self.ongoing_command.is_none())] fn dispatch_command(&mut self, command: &Command) -> Result, InvokerError> { Ok(match command { Command::Autoroute(selection, options) => ExecutionStepper::PlanarAutoroute( self.autorouter.planar_autoroute(selection, *options)?, ), Command::MultilayerAutoroute(selection, options) => { ExecutionStepper::MultilayerAutoroute( self.autorouter.multilayer_autoroute(selection, *options)?, ) } Command::TopoAutoroute { selection, allowed_edges, active_layer: active_layer_name, routed_band_width, } => { let active_layer = self .autorouter .board .layout() .rules() .layername_layer(active_layer_name) .unwrap(); let ratlines = self.autorouter.selected_ratlines(selection, active_layer); // TODO: consider "presort by pairwise detours" ExecutionStepper::TopoAutoroute(self.autorouter.topo_autoroute_ratlines( ratlines, allowed_edges.clone(), active_layer, *routed_band_width, None, )?) } Command::PlaceVia(weight) => { ExecutionStepper::PlaceVia(self.autorouter.place_via(*weight)?) } Command::RemoveBands(selection) => { ExecutionStepper::RemoveBands(self.autorouter.remove_bands(selection)?) } Command::CompareDetours(selection, options) => ExecutionStepper::CompareDetours( self.autorouter.compare_detours(selection, *options)?, ), Command::MeasureLength(selection) => { ExecutionStepper::MeasureLength(self.autorouter.measure_length(selection)?) } }) } #[debug_requires(self.ongoing_command.is_none())] /// Undo last command. pub fn undo(&mut self) -> Result<(), InvokerError> { let last_done = self.history.last_done()?; if let Some(edit) = last_done.edit() { self.autorouter.board.apply_edit(&edit.reverse()); } Ok(self.history.undo()?) } //#[debug_requires(self.ongoing_command.is_none())] /// Redo last command. pub fn redo(&mut self) -> Result<(), InvokerError> { let last_undone = self.history.last_undone()?; if let Some(edit) = last_undone.edit() { self.autorouter.board.apply_edit(edit); } Ok(self.history.redo()?) } #[debug_requires(self.ongoing_command.is_none())] /// Replay last command. pub fn replay(&mut self, history: History) { let (done, undone) = history.dissolve(); for entry in done { self.execute(entry.command().clone()); } self.history.set_undone(undone); } }