diff --git a/src/autorouter/autoroute.rs b/src/autorouter/autoroute.rs index a36dd0b..a210125 100644 --- a/src/autorouter/autoroute.rs +++ b/src/autorouter/autoroute.rs @@ -91,7 +91,7 @@ impl AutorouteExecutionStepper { autorouter: &mut Autorouter, index: usize, ) -> Result<(), AutorouterError> { - if index > self.board_data_edits.len() { + if index >= self.board_data_edits.len() { return Err(AutorouterError::NothingToUndoForPermutation); } @@ -232,11 +232,13 @@ impl Permutate> for AutorouteExecutionStepper { autorouter: &mut Autorouter, permutation: Vec, ) -> Result<(), AutorouterError> { - let new_index = permutation + let Some(new_index) = permutation .iter() .zip(self.ratlines.iter()) .position(|(permuted, original)| *permuted != *original) - .unwrap(); + else { + return Err(AutorouterError::NothingToUndoForPermutation); + }; self.ratlines = permutation; self.backtrace_to_index(autorouter, new_index)?; diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index 5dfe92a..2a95a5d 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -7,18 +7,16 @@ use geo::Point; use petgraph::graph::NodeIndex; use serde::{Deserialize, Serialize}; use spade::InsertionError; -use std::{cmp::Ordering, collections::BTreeSet}; +use std::collections::BTreeSet; use thiserror::Error; use crate::{ autorouter::permutator::AutorouteExecutionPermutator, board::{AccessMesadata, Board}, drawing::{band::BandTermsegIndex, Infringement}, - geometry::shape::MeasureLength, graph::MakeRef, layout::{via::ViaWeight, LayoutEdit}, router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions}, - stepper::Step, triangulation::GetTrianvertexNodeIndex, }; diff --git a/src/autorouter/permutator.rs b/src/autorouter/permutator.rs index a1b8580..d2fc6f3 100644 --- a/src/autorouter/permutator.rs +++ b/src/autorouter/permutator.rs @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: MIT -use std::{cmp::Ordering, iter::Skip, ops::ControlFlow}; +use std::{cmp::Ordering, ops::ControlFlow}; use itertools::{Itertools, Permutations}; +use petgraph::{algo::tarjan_scc, graph::NodeIndex}; use specctra_core::mesadata::AccessMesadata; use crate::{ @@ -12,7 +13,7 @@ use crate::{ autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper}, invoker::GetDebugOverlayData, ratline::RatlineIndex, - Autorouter, AutorouterError, AutorouterOptions, PresortBy, + Autorouter, AutorouterError, AutorouterOptions, }, board::edit::BoardEdit, drawing::graph::PrimitiveIndex, @@ -22,54 +23,183 @@ use crate::{ 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, - permutations_iter: Skip>>, + permuter: RatlineSccPermuter, options: AutorouterOptions, } impl AutorouteExecutionPermutator { pub fn new( autorouter: &mut Autorouter, - mut ratlines: Vec, + ratlines: Vec, options: AutorouterOptions, ) -> Result { - let ratlines_len = ratlines.len(); - - match options.presort_by { - PresortBy::RatlineIntersectionCountAndLength => ratlines.sort_unstable_by(|a, b| { - let a_intersector_count = a.ref_(autorouter).interior_obstacle_ratlines().count(); - let b_intersector_count = b.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 a_length = a.ref_(autorouter).length(); - let b_length = b.ref_(autorouter).length(); - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - } - }), - PresortBy::PairwiseDetours => ratlines.sort_unstable_by(|a, b| { - let mut compare_detours = autorouter - .compare_detours_ratlines(*a, *b, options) - .unwrap(); - - if let Ok((al, bl)) = compare_detours.finish(autorouter) { - PartialOrd::partial_cmp(&al, &bl).unwrap() - } else { - Ordering::Equal - } - }), - } + let mut permuter = RatlineSccPermuter::new(autorouter, ratlines, &options); + let initially_sorted_ratlines = permuter.next_permutation(autorouter).unwrap(); Ok(Self { - stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?, + stepper: AutorouteExecutionStepper::new( + autorouter, + initially_sorted_ratlines, + options, + )?, // Note: I assume here that the first permutation is the same as the original order. - permutations_iter: ratlines.into_iter().permutations(ratlines_len).skip(1), + permuter, options, }) } @@ -92,7 +222,7 @@ impl Step, Option, AutorouteContinue } loop { - let Some(permutation) = self.permutations_iter.next() else { + let Some(permutation) = self.permuter.next_permutation(autorouter) else { return Ok(ControlFlow::Break(None)); }; @@ -111,7 +241,7 @@ impl Step, Option, AutorouteContinue impl Abort> for AutorouteExecutionPermutator { fn abort(&mut self, autorouter: &mut Autorouter) { - self.permutations_iter.all(|_| true); + //self.permutations_iter.all(|_| true); // Why did I add this code here??? self.stepper.abort(autorouter); } } diff --git a/src/autorouter/ratsnest.rs b/src/autorouter/ratsnest.rs index 1352a84..e1763d0 100644 --- a/src/autorouter/ratsnest.rs +++ b/src/autorouter/ratsnest.rs @@ -2,18 +2,14 @@ // // SPDX-License-Identifier: MIT -//! Defines data structures and methods for managing a graph -//! used in layout triangulation and routing tasks. It includes vertex and edge -//! structures for representing graph nodes and edges with associated metadata, -//! as well as functions for constructing and manipulating these graphs. - use std::collections::BTreeMap; use enum_dispatch::enum_dispatch; use geo::Point; use petgraph::{ data::Element, - graph::{NodeIndex, UnGraph}, + graph::NodeIndex, + prelude::StableUnGraph, unionfind::UnionFind, visit::{EdgeRef, IntoEdgeReferences, NodeIndexable}, }; @@ -74,7 +70,7 @@ impl HasPosition for RatvertexWeight { } pub struct Ratsnest { - graph: UnGraph, + graph: StableUnGraph, } impl Ratsnest { @@ -86,7 +82,7 @@ impl Ratsnest { } let mut this = Self { - graph: UnGraph::default(), + graph: StableUnGraph::default(), }; let mut triangulations = BTreeMap::new(); @@ -166,7 +162,7 @@ impl Ratsnest { self.graph.edge_weight_mut(ratline).unwrap().band_termseg = Some(termseg); } - pub fn graph(&self) -> &UnGraph { + pub fn graph(&self) -> &StableUnGraph { &self.graph } }