From 12ee2f398459aac9004de2d8f6072b39293e2894 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sat, 6 Sep 2025 23:59:45 +0200 Subject: [PATCH] refactor(autorouter/permutator): Move permuter code to new `permuter.rs` file --- committed.toml | 3 +- src/autorouter/mod.rs | 1 + src/autorouter/permutator.rs | 167 ++----------------------------- src/autorouter/permuter.rs | 185 +++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 162 deletions(-) create mode 100644 src/autorouter/permuter.rs diff --git a/committed.toml b/committed.toml index fd92a66..2bc8d2a 100644 --- a/committed.toml +++ b/committed.toml @@ -16,7 +16,7 @@ allowed_scopes = [ "topola-cli", "topola-egui", - # Generated using + # Originally generated using # `find src -type f | awk '!/lib.rs|mod.rs/ { print "\"" substr($1, 1 + 4, length($1) - 4 - 3) "\","; }' | sort`. "autorouter/autoroute", "autorouter/autorouter", @@ -26,6 +26,7 @@ allowed_scopes = [ "autorouter/invoker", "autorouter/measure_length", "autorouter/permutator", + "autorouter/permuter", "autorouter/place_via", "autorouter/pointroute", "autorouter/ratsnest", diff --git a/src/autorouter/mod.rs b/src/autorouter/mod.rs index 8609eb4..a6e3cbf 100644 --- a/src/autorouter/mod.rs +++ b/src/autorouter/mod.rs @@ -10,6 +10,7 @@ pub mod history; pub mod invoker; pub mod measure_length; pub mod permutator; +pub mod permuter; pub mod place_via; pub mod pointroute; pub mod ratline; diff --git a/src/autorouter/permutator.rs b/src/autorouter/permutator.rs index d2fc6f3..852b69b 100644 --- a/src/autorouter/permutator.rs +++ b/src/autorouter/permutator.rs @@ -2,184 +2,28 @@ // // SPDX-License-Identifier: MIT -use std::{cmp::Ordering, ops::ControlFlow}; +use std::ops::ControlFlow; -use itertools::{Itertools, Permutations}; -use petgraph::{algo::tarjan_scc, graph::NodeIndex}; use specctra_core::mesadata::AccessMesadata; use crate::{ autorouter::{ autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper}, invoker::GetDebugOverlayData, + permuter::{PermuteRatlines, RatlinePermuter, SccRatlinePermuter}, ratline::RatlineIndex, Autorouter, AutorouterError, AutorouterOptions, }, board::edit::BoardEdit, drawing::graph::PrimitiveIndex, - geometry::{primitive::PrimitiveShape, shape::MeasureLength}, - graph::MakeRef, + geometry::primitive::PrimitiveShape, router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper}, stepper::{Abort, EstimateProgress, Permutate, Step}, }; -struct RatlineSccPermuter { - sccs_permutations_iter: Permutations>>>, - ratlines: Vec, -} - -impl RatlineSccPermuter { - pub fn new( - autorouter: &mut Autorouter, - ratlines: Vec, - _options: &AutorouterOptions, - ) -> Self { - // FIXME: Unnecessary copy. - let mut filtered_ratsnest = autorouter.ratsnest().graph().clone(); - filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i)); - - let mut sccs = tarjan_scc(&filtered_ratsnest); - let sccs_len = sccs.len(); - - sccs.sort_unstable_by(|a, b| { - // FIXME: These calculations should probably be stored somewhere - // instead of being done every time. - - let mut a_intersector_count = 0; - let mut b_intersector_count = 0; - let mut a_length = 0.0; - let mut b_length = 0.0; - - // FIXME: It's inefficient to iterate over the ratlines on every - // sort comparison. But this is the simplest solution I arrived - // at after realizing that `.tarjan_scc(...)` does not sort nodes - // inside components. - for ratline in ratlines.iter() { - if a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) - && a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) - { - a_length += ratline.ref_(autorouter).length(); - a_intersector_count += ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count(); - } - } - - for ratline in ratlines.iter() { - if b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) - && b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) - { - b_length += ratline.ref_(autorouter).length(); - b_intersector_count += ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count(); - } - } - - let primary_ordering = a_intersector_count.cmp(&b_intersector_count); - - if primary_ordering != Ordering::Equal { - primary_ordering - } else { - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - } - - // Below is how I tried to do this before I realized that - // `.tarjan_scc(...)` does not sort nodes inside components. - - /*let a_intersector_count: usize = a - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count() - }) - .sum(); - let b_intersector_count: usize = b - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count() - }) - .sum(); - - let primary_ordering = a_intersector_count.cmp(&b_intersector_count); - - if primary_ordering != Ordering::Equal { - primary_ordering - } else { - let a_length: f64 = a - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline.ref_(autorouter).length() - }) - .sum(); - let b_length: f64 = b - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline.ref_(autorouter).length() - }) - .sum(); - - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - }*/ - }); - - Self { - sccs_permutations_iter: sccs.into_iter().permutations(sccs_len), - ratlines, - } - } - - pub fn next_permutation( - &mut self, - autorouter: &mut Autorouter, - ) -> Option> { - let scc_permutation = self.sccs_permutations_iter.next()?; - let mut ratlines = vec![]; - - for scc in scc_permutation { - for ratline in self.ratlines.iter() { - if scc.contains( - &autorouter - .ratsnest() - .graph() - .edge_endpoints(*ratline) - .unwrap() - .0, - ) && scc.contains( - &autorouter - .ratsnest() - .graph() - .edge_endpoints(*ratline) - .unwrap() - .1, - ) { - ratlines.push(*ratline); - } - } - } - - Some(ratlines) - } -} - pub struct AutorouteExecutionPermutator { stepper: AutorouteExecutionStepper, - permuter: RatlineSccPermuter, + permuter: RatlinePermuter, options: AutorouterOptions, } @@ -189,7 +33,8 @@ impl AutorouteExecutionPermutator { ratlines: Vec, options: AutorouterOptions, ) -> Result { - let mut permuter = RatlineSccPermuter::new(autorouter, ratlines, &options); + let mut permuter = + RatlinePermuter::Scc(SccRatlinePermuter::new(autorouter, ratlines, &options)); let initially_sorted_ratlines = permuter.next_permutation(autorouter).unwrap(); Ok(Self { diff --git a/src/autorouter/permuter.rs b/src/autorouter/permuter.rs new file mode 100644 index 0000000..72f2508 --- /dev/null +++ b/src/autorouter/permuter.rs @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: 2025 Topola contributors +// +// SPDX-License-Identifier: MIT + +use std::cmp::Ordering; + +use enum_dispatch::enum_dispatch; +use itertools::{Itertools, Permutations}; +use petgraph::{algo::tarjan_scc, graph::NodeIndex}; +use specctra_core::mesadata::AccessMesadata; + +use crate::{ + autorouter::{ratline::RatlineIndex, Autorouter, AutorouterOptions}, + geometry::shape::MeasureLength, + graph::MakeRef, +}; + +#[enum_dispatch] +pub trait PermuteRatlines { + fn next_permutation( + &mut self, + autorouter: &mut Autorouter, + ) -> Option>; +} + +#[enum_dispatch(PermuteRatlines)] +pub enum RatlinePermuter { + Scc(SccRatlinePermuter), +} + +pub struct SccRatlinePermuter { + sccs_permutations_iter: Permutations>>>, + ratlines: Vec, +} + +impl SccRatlinePermuter { + pub fn new( + autorouter: &mut Autorouter, + ratlines: Vec, + _options: &AutorouterOptions, + ) -> Self { + // FIXME: Unnecessary copy. + let mut filtered_ratsnest = autorouter.ratsnest().graph().clone(); + filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i)); + + let mut sccs = tarjan_scc(&filtered_ratsnest); + let sccs_len = sccs.len(); + + sccs.sort_unstable_by(|a, b| { + // FIXME: These calculations should probably be stored somewhere + // instead of being done every time. + + let mut a_intersector_count = 0; + let mut b_intersector_count = 0; + let mut a_length = 0.0; + let mut b_length = 0.0; + + // FIXME: It's inefficient to iterate over the ratlines on every + // sort comparison. But this is the simplest solution I arrived + // at after realizing that `.tarjan_scc(...)` does not sort nodes + // inside components. + for ratline in ratlines.iter() { + if a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) + && a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) + { + a_length += ratline.ref_(autorouter).length(); + a_intersector_count += ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count(); + } + } + + for ratline in ratlines.iter() { + if b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) + && b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) + { + b_length += ratline.ref_(autorouter).length(); + b_intersector_count += ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count(); + } + } + + let primary_ordering = a_intersector_count.cmp(&b_intersector_count); + + if primary_ordering != Ordering::Equal { + primary_ordering + } else { + let secondary_ordering = a_length.total_cmp(&b_length); + + secondary_ordering + } + + // Below is how I tried to do this before I realized that + // `.tarjan_scc(...)` does not sort nodes inside components. + + /*let a_intersector_count: usize = a + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count() + }) + .sum(); + let b_intersector_count: usize = b + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count() + }) + .sum(); + + let primary_ordering = a_intersector_count.cmp(&b_intersector_count); + + if primary_ordering != Ordering::Equal { + primary_ordering + } else { + let a_length: f64 = a + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline.ref_(autorouter).length() + }) + .sum(); + let b_length: f64 = b + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline.ref_(autorouter).length() + }) + .sum(); + + let secondary_ordering = a_length.total_cmp(&b_length); + + secondary_ordering + }*/ + }); + + Self { + sccs_permutations_iter: sccs.into_iter().permutations(sccs_len), + ratlines, + } + } +} + +impl PermuteRatlines for SccRatlinePermuter { + fn next_permutation( + &mut self, + autorouter: &mut Autorouter, + ) -> Option> { + let scc_permutation = self.sccs_permutations_iter.next()?; + let mut ratlines = vec![]; + + for scc in scc_permutation { + for ratline in self.ratlines.iter() { + if scc.contains( + &autorouter + .ratsnest() + .graph() + .edge_endpoints(*ratline) + .unwrap() + .0, + ) && scc.contains( + &autorouter + .ratsnest() + .graph() + .edge_endpoints(*ratline) + .unwrap() + .1, + ) { + ratlines.push(*ratline); + } + } + } + + Some(ratlines) + } +}