refactor(autorouter): store edits in history entries

This commit is contained in:
Mikolaj Wielgus 2024-11-29 03:45:48 +01:00
parent e0de008f51
commit db9d897315
6 changed files with 47 additions and 31 deletions

View File

@ -9,6 +9,7 @@ use crate::{
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::{band::BandTermsegIndex, graph::PrimitiveIndex}, drawing::{band::BandTermsegIndex, graph::PrimitiveIndex},
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
layout::LayoutEdit,
router::{navcord::NavcordStepper, navmesh::Navmesh, route::RouteStepper, Router}, router::{navcord::NavcordStepper, navmesh::Navmesh, route::RouteStepper, Router},
stepper::Step, stepper::Step,
}; };
@ -71,7 +72,7 @@ impl AutorouteExecutionStepper {
} }
} }
impl<M: AccessMesadata> Step<Autorouter<M>, (), AutorouteContinueStatus> impl<M: AccessMesadata> Step<Autorouter<M>, Option<LayoutEdit>, AutorouteContinueStatus>
for AutorouteExecutionStepper for AutorouteExecutionStepper
{ {
type Error = AutorouterError; type Error = AutorouterError;
@ -79,14 +80,14 @@ impl<M: AccessMesadata> Step<Autorouter<M>, (), AutorouteContinueStatus>
fn step( fn step(
&mut self, &mut self,
autorouter: &mut Autorouter<M>, autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<(), AutorouteContinueStatus>, AutorouterError> { ) -> Result<ControlFlow<Option<LayoutEdit>, AutorouteContinueStatus>, AutorouterError> {
let Some(curr_ratline) = self.curr_ratline else { let Some(curr_ratline) = self.curr_ratline else {
return Ok(ControlFlow::Break(())); return Ok(ControlFlow::Break(None));
}; };
let Some(ref mut route) = self.route else { let Some(ref mut route) = self.route else {
// Shouldn't happen. // Shouldn't happen.
return Ok(ControlFlow::Break(())); return Ok(ControlFlow::Break(None));
}; };
let (source, target) = autorouter.ratline_endpoints(curr_ratline); let (source, target) = autorouter.ratline_endpoints(curr_ratline);

View File

@ -79,7 +79,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, (f64, f64)> for CompareDetoursExecut
Ok(ControlFlow::Continue(())) Ok(ControlFlow::Continue(()))
} }
ControlFlow::Break(()) => { ControlFlow::Break(..) => {
if let Some(next_autoroute) = self.next_autoroute.take() { if let Some(next_autoroute) = self.next_autoroute.take() {
autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?; autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?;
self.autoroute = next_autoroute; self.autoroute = next_autoroute;

View File

@ -3,7 +3,11 @@ use std::ops::ControlFlow;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{board::mesadata::AccessMesadata, layout::via::ViaWeight, stepper::Step}; use crate::{
board::mesadata::AccessMesadata,
layout::{via::ViaWeight, LayoutEdit},
stepper::Step,
};
use super::{ use super::{
autoroute::AutorouteExecutionStepper, autoroute::AutorouteExecutionStepper,
@ -40,34 +44,37 @@ impl ExecutionStepper {
fn step_catch_err<M: AccessMesadata>( fn step_catch_err<M: AccessMesadata>(
&mut self, &mut self,
autorouter: &mut Autorouter<M>, autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<String>, InvokerError> { ) -> Result<ControlFlow<(Option<LayoutEdit>, String)>, InvokerError> {
Ok(match self { Ok(match self {
ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? { ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? {
ControlFlow::Continue(..) => ControlFlow::Continue(()), ControlFlow::Continue(..) => ControlFlow::Continue(()),
ControlFlow::Break(..) => ControlFlow::Break("finished autorouting".to_string()), ControlFlow::Break(edit) => {
ControlFlow::Break((edit, "finished autorouting".to_string()))
}
}, },
ExecutionStepper::PlaceVia(place_via) => { ExecutionStepper::PlaceVia(place_via) => {
place_via.doit(autorouter)?; place_via.doit(autorouter)?;
ControlFlow::Break("finished placing via".to_string()) ControlFlow::Break((None, "finished placing via".to_string()))
} }
ExecutionStepper::RemoveBands(remove_bands) => { ExecutionStepper::RemoveBands(remove_bands) => {
remove_bands.doit(autorouter)?; remove_bands.doit(autorouter)?;
ControlFlow::Break("finished removing bands".to_string()) ControlFlow::Break((None, "finished removing bands".to_string()))
} }
ExecutionStepper::CompareDetours(compare_detours) => { ExecutionStepper::CompareDetours(compare_detours) => {
match compare_detours.step(autorouter)? { match compare_detours.step(autorouter)? {
ControlFlow::Continue(()) => ControlFlow::Continue(()), ControlFlow::Continue(()) => ControlFlow::Continue(()),
ControlFlow::Break((total_length1, total_length2)) => { ControlFlow::Break((total_length1, total_length2)) => ControlFlow::Break((
ControlFlow::Break(format!( None,
format!(
"total detour lengths are {} and {}", "total detour lengths are {} and {}",
total_length1, total_length2 total_length1, total_length2
)) ),
} )),
} }
} }
ExecutionStepper::MeasureLength(measure_length) => { ExecutionStepper::MeasureLength(measure_length) => {
let length = measure_length.doit(autorouter)?; let length = measure_length.doit(autorouter)?;
ControlFlow::Break(format!("Total length of selected bands: {}", length)) ControlFlow::Break((None, format!("Total length of selected bands: {}", length)))
} }
}) })
} }
@ -79,9 +86,9 @@ impl<M: AccessMesadata> Step<Invoker<M>, String> for ExecutionStepper {
fn step(&mut self, invoker: &mut Invoker<M>) -> Result<ControlFlow<String>, InvokerError> { fn step(&mut self, invoker: &mut Invoker<M>) -> Result<ControlFlow<String>, InvokerError> {
match self.step_catch_err(&mut invoker.autorouter) { match self.step_catch_err(&mut invoker.autorouter) {
Ok(ControlFlow::Continue(())) => Ok(ControlFlow::Continue(())), Ok(ControlFlow::Continue(())) => Ok(ControlFlow::Continue(())),
Ok(ControlFlow::Break(msg)) => { Ok(ControlFlow::Break((maybe_edit, msg))) => {
if let Some(command) = invoker.ongoing_command.take() { if let (Some(command), Some(edit)) = (invoker.ongoing_command.take(), maybe_edit) {
invoker.history.do_(command); invoker.history.do_(command, Some(edit));
} }
Ok(ControlFlow::Break(msg)) Ok(ControlFlow::Break(msg))

View File

@ -6,7 +6,7 @@ use derive_getters::{Dissolve, Getters};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use crate::autorouter::execution::Command; use crate::{autorouter::execution::Command, layout::LayoutEdit};
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
pub enum HistoryError { pub enum HistoryError {
@ -16,10 +16,18 @@ pub enum HistoryError {
NoNextCommand, NoNextCommand,
} }
#[derive(Debug, Clone, Getters, Serialize, Deserialize)]
#[serde(transparent)]
pub struct HistoryEntry {
command: Command,
#[serde(skip)]
edit: Option<LayoutEdit>,
}
#[derive(Debug, Default, Clone, Getters, Dissolve, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Getters, Dissolve, Serialize, Deserialize)]
pub struct History { pub struct History {
done: Vec<Command>, done: Vec<HistoryEntry>,
undone: Vec<Command>, undone: Vec<HistoryEntry>,
} }
impl History { impl History {
@ -27,8 +35,8 @@ impl History {
Self::default() Self::default()
} }
pub fn do_(&mut self, command: Command) { pub fn do_(&mut self, command: Command, edit: Option<LayoutEdit>) {
self.done.push(command); self.done.push(HistoryEntry { command, edit });
} }
pub fn undo(&mut self) -> Result<(), HistoryError> { pub fn undo(&mut self) -> Result<(), HistoryError> {
@ -49,15 +57,15 @@ impl History {
Ok(()) Ok(())
} }
pub fn set_undone(&mut self, iter: impl IntoIterator<Item = Command>) { pub fn set_undone(&mut self, iter: impl IntoIterator<Item = HistoryEntry>) {
self.undone = Vec::from_iter(iter); self.undone = Vec::from_iter(iter);
} }
pub fn last_done(&self) -> Result<&Command, HistoryError> { pub fn last_done(&self) -> Result<&HistoryEntry, HistoryError> {
self.done.last().ok_or(HistoryError::NoPreviousCommand) self.done.last().ok_or(HistoryError::NoPreviousCommand)
} }
pub fn last_undone(&self) -> Result<&Command, HistoryError> { pub fn last_undone(&self) -> Result<&HistoryEntry, HistoryError> {
self.undone.last().ok_or(HistoryError::NoNextCommand) self.undone.last().ok_or(HistoryError::NoNextCommand)
} }
} }

View File

@ -172,7 +172,7 @@ impl<M: AccessMesadata> Invoker<M> {
#[debug_requires(self.ongoing_command.is_none())] #[debug_requires(self.ongoing_command.is_none())]
/// Undo last command /// Undo last command
pub fn undo(&mut self) -> Result<(), InvokerError> { pub fn undo(&mut self) -> Result<(), InvokerError> {
let command = self.history.last_done()?; let command = self.history.last_done()?.command();
match command { match command {
Command::Autoroute(ref selection, ..) => { Command::Autoroute(ref selection, ..) => {
@ -194,7 +194,7 @@ impl<M: AccessMesadata> Invoker<M> {
//#[debug_requires(self.ongoing_command.is_none())] //#[debug_requires(self.ongoing_command.is_none())]
/// Redo last command /// Redo last command
pub fn redo(&mut self) -> Result<(), InvokerError> { pub fn redo(&mut self) -> Result<(), InvokerError> {
let command = self.history.last_undone()?.clone(); let command = self.history.last_undone()?.command().clone();
let mut execute = self.execute_stepper(command)?; let mut execute = self.execute_stepper(command)?;
loop { loop {
@ -214,8 +214,8 @@ impl<M: AccessMesadata> Invoker<M> {
pub fn replay(&mut self, history: History) { pub fn replay(&mut self, history: History) {
let (done, undone) = history.dissolve(); let (done, undone) = history.dissolve();
for command in done { for entry in done {
self.execute(command); self.execute(entry.command().clone());
} }
self.history.set_undone(undone.into_iter()); self.history.set_undone(undone.into_iter());

View File

@ -22,7 +22,7 @@ pub trait ApplyGeometryEdit<
fn apply(&mut self, edit: GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>); fn apply(&mut self, edit: GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>);
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct GeometryEdit< pub struct GeometryEdit<
PW: GetWidth + GetLayer + TryInto<DW> + TryInto<SW> + TryInto<BW> + Retag<PI> + Copy, PW: GetWidth + GetLayer + TryInto<DW> + TryInto<SW> + TryInto<BW> + Retag<PI> + Copy,
DW: AccessDotWeight<PW> + GetLayer, DW: AccessDotWeight<PW> + GetLayer,