refactor(autorouter/autoroute): Store vecs of edits instead of large edits

This results in a large copy, but it should be possible to refactor the
code later to get rid of this.
This commit is contained in:
Mikolaj Wielgus 2025-08-09 23:56:29 +02:00
parent 5bff83ecbd
commit ac7b24f939
1 changed files with 73 additions and 26 deletions

View File

@ -6,7 +6,7 @@
//! routing steps. //! routing steps.
use itertools::{Itertools, Permutations}; use itertools::{Itertools, Permutations};
use std::ops::ControlFlow; use std::{iter::Skip, ops::ControlFlow};
use crate::{ use crate::{
board::{ board::{
@ -46,9 +46,10 @@ pub struct AutorouteExecutionStepper {
curr_ratline_index: usize, curr_ratline_index: usize,
/// Stores the current route being processed, if any. /// Stores the current route being processed, if any.
route: Option<RouteStepper>, route: Option<RouteStepper>,
/// Records the changes to the board data (changes to layout data are /// Records the changes to the layout, one routed band per item.
/// recorded in navcord in route stepper). layout_edits: Vec<LayoutEdit>,
data_edit: BoardDataEdit, /// Records the changes to the board data, one routed band per item.
board_data_edits: Vec<BoardDataEdit>,
/// The options for the autorouting process, defining how routing should be carried out. /// The options for the autorouting process, defining how routing should be carried out.
options: AutorouterOptions, options: AutorouterOptions,
} }
@ -80,17 +81,60 @@ impl AutorouteExecutionStepper {
destination, destination,
options.router_options.routed_band_width, options.router_options.routed_band_width,
)?), )?),
data_edit: BoardDataEdit::new(), layout_edits: vec![],
board_data_edits: vec![],
options, options,
}) })
} }
fn dissolve_route_stepper_into_layout_edit(&mut self) -> LayoutEdit { fn permute(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
permutation: Vec<RatlineIndex>,
) -> Result<(), AutorouterError> {
let new_index = permutation
.iter()
.zip(self.ratlines.iter())
.position(|(permuted, original)| *permuted != *original)
.unwrap();
self.ratlines = permutation;
self.backtrace_to_index(autorouter, new_index)?;
Ok(())
}
fn backtrace_to_index(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
index: usize,
) -> Result<(), AutorouterError> {
self.dissolve_route_stepper_and_push_layout_edit();
let board_edit = BoardEdit::new_from_edits(
BoardDataEdit::merge_iter(self.board_data_edits.split_off(index)),
LayoutEdit::merge_iter(self.layout_edits.split_off(index)),
);
autorouter.board.apply_edit(&board_edit.reverse());
let (origin, destination) = self.ratlines[index].ref_(autorouter).endpoint_dots();
let mut router = Router::new(autorouter.board.layout_mut(), self.options.router_options);
self.route = Some(router.route(
LayoutEdit::new(),
origin,
destination,
self.options.router_options.routed_band_width,
)?);
self.curr_ratline_index = index;
Ok(())
}
fn dissolve_route_stepper_and_push_layout_edit(&mut self) {
if let Some(taken_route) = self.route.take() { if let Some(taken_route) = self.route.take() {
let (_thetastar, navcord, ..) = taken_route.dissolve(); let (_thetastar, navcord, ..) = taken_route.dissolve();
navcord.recorder self.layout_edits.push(navcord.recorder);
} else {
LayoutEdit::new()
} }
} }
} }
@ -107,10 +151,14 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
// TODO: Use a proper state machine here for better readability? // TODO: Use a proper state machine here for better readability?
if self.curr_ratline_index >= self.ratlines.len() { if self.curr_ratline_index >= self.ratlines.len() {
let recorder = self.dissolve_route_stepper_into_layout_edit(); self.dissolve_route_stepper_and_push_layout_edit();
return Ok(ControlFlow::Break(Some(BoardEdit::new_from_edits( return Ok(ControlFlow::Break(Some(BoardEdit::new_from_edits(
self.data_edit.clone(), BoardDataEdit::merge_iter(self.board_data_edits.iter().cloned()),
recorder, // FIXME: This is a large clone. We probably need some
// high-level, say `AutorouteEdit`, struct to store sequences
// of edits.
LayoutEdit::merge_iter(self.layout_edits.iter().cloned()),
)))); ))));
} }
@ -148,9 +196,13 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
band_termseg, band_termseg,
); );
let mut board_data_edit = BoardDataEdit::new();
autorouter autorouter
.board .board
.try_set_band_between_nodes(&mut self.data_edit, source, target, band); .try_set_band_between_nodes(&mut board_data_edit, source, target, band);
self.board_data_edits.push(board_data_edit);
AutorouteContinueStatus::Routed(band_termseg) AutorouteContinueStatus::Routed(band_termseg)
}; };
@ -161,7 +213,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
let (source, target) = new_ratline.ref_(autorouter).endpoint_dots(); let (source, target) = new_ratline.ref_(autorouter).endpoint_dots();
let mut router = let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options); Router::new(autorouter.board.layout_mut(), self.options.router_options);
let recorder = self.dissolve_route_stepper_into_layout_edit();
self.dissolve_route_stepper_and_push_layout_edit();
let recorder = LayoutEdit::new();
self.route = Some(router.route( self.route = Some(router.route(
recorder, recorder,
@ -177,10 +231,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionStepper { impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionStepper {
fn abort(&mut self, autorouter: &mut Autorouter<M>) { fn abort(&mut self, autorouter: &mut Autorouter<M>) {
let layout_edit = self.dissolve_route_stepper_into_layout_edit(); self.backtrace_to_index(autorouter, 0);
let board_edit = BoardEdit::new_from_edits(self.data_edit.clone(), layout_edit);
autorouter.board.apply_edit(&board_edit.reverse());
self.curr_ratline_index = self.ratlines.len(); self.curr_ratline_index = self.ratlines.len();
} }
} }
@ -220,7 +271,7 @@ impl GetDebugOverlayData for AutorouteExecutionStepper {
pub struct AutorouteExecutionPermutator { pub struct AutorouteExecutionPermutator {
stepper: AutorouteExecutionStepper, stepper: AutorouteExecutionStepper,
permutations_iter: Permutations<std::vec::IntoIter<RatlineIndex>>, permutations_iter: Skip<Permutations<std::vec::IntoIter<RatlineIndex>>>,
options: AutorouterOptions, options: AutorouterOptions,
} }
@ -235,7 +286,7 @@ impl AutorouteExecutionPermutator {
Ok(Self { Ok(Self {
stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?, stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?,
// Note: I assume here that the first permutation is the same as the original order. // Note: I assume here that the first permutation is the same as the original order.
permutations_iter: ratlines.into_iter().permutations(ratlines_len), permutations_iter: ratlines.into_iter().permutations(ratlines_len).skip(1),
options, options,
}) })
} }
@ -257,15 +308,11 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
return Err(err); return Err(err);
} }
self.stepper.abort(autorouter); let Some(permutation) = self.permutations_iter.next() else {
let Some(new_permutation) = self.permutations_iter.next() else {
return Ok(ControlFlow::Break(None)); return Ok(ControlFlow::Break(None));
}; };
self.stepper = self.stepper.permute(autorouter, permutation);
AutorouteExecutionStepper::new(autorouter, new_permutation, self.options)?;
self.stepper.step(autorouter) self.stepper.step(autorouter)
} }
} }