mirror of https://codeberg.org/topola/topola.git
feat(autorouter/autoroute): Implement basic brute-force permutator
Permutates if and until a solution is found. Does not do optimization so far. Thanks to this, it is now possible to route the DE-9 to DE-9 test without having to find the correct sequence of autorouting jobs.
This commit is contained in:
parent
f09aa053b6
commit
d181c7df1b
|
|
@ -53,6 +53,7 @@ contracts-try = "0.7"
|
|||
derive-getters.workspace = true
|
||||
enum_dispatch = "0.3"
|
||||
geo.workspace = true
|
||||
itertools = "0.14"
|
||||
log.workspace = true
|
||||
petgraph.workspace = true
|
||||
ron = "0.10"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
//! Manages autorouting of ratlines in a layout, tracking status and processed
|
||||
//! routing steps.
|
||||
|
||||
use itertools::{Itertools, Permutations};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -33,7 +34,7 @@ pub enum AutorouteContinueStatus {
|
|||
Running,
|
||||
/// A specific segment has been successfully routed.
|
||||
Routed(BandTermsegIndex),
|
||||
/// A specific segment was already routed and has been skipped.
|
||||
/// A specific segment had been already routed and has been skipped.
|
||||
Skipped(BandTermsegIndex),
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +104,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
|
|||
&mut self,
|
||||
autorouter: &mut Autorouter<M>,
|
||||
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
|
||||
// TODO: Use a proper state machine here for better readability?
|
||||
|
||||
if self.curr_ratline_index >= self.ratlines.len() {
|
||||
let recorder = self.dissolve_route_stepper_into_layout_edit();
|
||||
return Ok(ControlFlow::Break(Some(BoardEdit::new_from_edits(
|
||||
|
|
@ -214,3 +217,91 @@ impl GetDebugOverlayData for AutorouteExecutionStepper {
|
|||
self.route.as_ref().map_or(&[], |route| route.obstacles())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AutorouteExecutionPermutator {
|
||||
stepper: AutorouteExecutionStepper,
|
||||
permutations_iter: Permutations<std::vec::IntoIter<RatlineIndex>>,
|
||||
options: AutorouterOptions,
|
||||
}
|
||||
|
||||
impl AutorouteExecutionPermutator {
|
||||
pub fn new(
|
||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||
ratlines: Vec<RatlineIndex>,
|
||||
options: AutorouterOptions,
|
||||
) -> Result<Self, AutorouterError> {
|
||||
let ratlines_len = ratlines.len();
|
||||
|
||||
Ok(Self {
|
||||
stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?,
|
||||
permutations_iter: ratlines.into_iter().permutations(ratlines_len),
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinueStatus>
|
||||
for AutorouteExecutionPermutator
|
||||
{
|
||||
type Error = AutorouterError;
|
||||
|
||||
fn step(
|
||||
&mut self,
|
||||
autorouter: &mut Autorouter<M>,
|
||||
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
|
||||
match self.stepper.step(autorouter) {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(..) => {
|
||||
self.stepper.abort(autorouter);
|
||||
|
||||
let Some(new_permutation) = self.permutations_iter.next() else {
|
||||
return Ok(ControlFlow::Break(None));
|
||||
};
|
||||
|
||||
self.stepper =
|
||||
AutorouteExecutionStepper::new(autorouter, new_permutation, self.options)?;
|
||||
|
||||
self.stepper.step(autorouter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionPermutator {
|
||||
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
|
||||
self.permutations_iter.all(|_| true);
|
||||
self.stepper.abort(autorouter);
|
||||
}
|
||||
}
|
||||
|
||||
impl EstimateProgress for AutorouteExecutionPermutator {
|
||||
type Value = f64;
|
||||
|
||||
fn estimate_progress_value(&self) -> f64 {
|
||||
// TODO.
|
||||
self.stepper.estimate_progress_value()
|
||||
}
|
||||
|
||||
fn estimate_progress_maximum(&self) -> f64 {
|
||||
// TODO.
|
||||
self.stepper.estimate_progress_maximum()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetDebugOverlayData for AutorouteExecutionPermutator {
|
||||
fn maybe_thetastar(&self) -> Option<&ThetastarStepper<Navmesh, f64>> {
|
||||
self.stepper.maybe_thetastar()
|
||||
}
|
||||
|
||||
fn maybe_navcord(&self) -> Option<&Navcord> {
|
||||
self.stepper.maybe_navcord()
|
||||
}
|
||||
|
||||
fn ghosts(&self) -> &[PrimitiveShape] {
|
||||
self.stepper.ghosts()
|
||||
}
|
||||
|
||||
fn obstacles(&self) -> &[PrimitiveIndex] {
|
||||
self.stepper.obstacles()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,22 @@ use geo::Point;
|
|||
use petgraph::graph::NodeIndex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spade::InsertionError;
|
||||
use std::collections::BTreeSet;
|
||||
use std::{cmp::Ordering, collections::BTreeSet};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
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,
|
||||
};
|
||||
|
||||
use super::{
|
||||
autoroute::AutorouteExecutionStepper,
|
||||
autoroute::{AutorouteExecutionPermutator, AutorouteExecutionStepper},
|
||||
compare_detours::CompareDetoursExecutionStepper,
|
||||
measure_length::MeasureLengthExecutionStepper,
|
||||
place_via::PlaceViaExecutionStepper,
|
||||
|
|
@ -105,8 +107,38 @@ impl<M: AccessMesadata> Autorouter<M> {
|
|||
&mut self,
|
||||
selection: &PinSelection,
|
||||
options: AutorouterOptions,
|
||||
) -> Result<AutorouteExecutionStepper, AutorouterError> {
|
||||
self.autoroute_ratlines(self.selected_ratlines(selection), options)
|
||||
) -> Result<AutorouteExecutionPermutator, AutorouterError> {
|
||||
let mut ratlines = self.selected_ratlines(selection);
|
||||
|
||||
match options.presort_by {
|
||||
PresortBy::RatlineIntersectionCountAndLength => ratlines.sort_unstable_by(|a, b| {
|
||||
let a_intersector_count = a.ref_(self).find_intersecting_ratlines().count();
|
||||
let b_intersector_count = b.ref_(self).find_intersecting_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_(self).length();
|
||||
let b_length = b.ref_(self).length();
|
||||
let secondary_ordering = a_length.total_cmp(&b_length);
|
||||
|
||||
secondary_ordering
|
||||
}
|
||||
}),
|
||||
PresortBy::PairwiseDetours => ratlines.sort_unstable_by(|a, b| {
|
||||
let mut compare_detours = self.compare_detours_ratlines(*a, *b, options).unwrap();
|
||||
|
||||
if let Ok((al, bl)) = compare_detours.finish(self) {
|
||||
PartialOrd::partial_cmp(&al, &bl).unwrap()
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
AutorouteExecutionPermutator::new(self, ratlines, options)
|
||||
}
|
||||
|
||||
pub(super) fn autoroute_ratlines(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
autoroute::AutorouteExecutionStepper,
|
||||
autoroute::AutorouteExecutionPermutator,
|
||||
compare_detours::CompareDetoursExecutionStepper,
|
||||
invoker::{GetDebugOverlayData, Invoker, InvokerError},
|
||||
measure_length::MeasureLengthExecutionStepper,
|
||||
|
|
@ -45,7 +45,7 @@ pub enum Command {
|
|||
|
||||
#[enum_dispatch(GetDebugOverlayData)]
|
||||
pub enum ExecutionStepper<M> {
|
||||
Autoroute(AutorouteExecutionStepper),
|
||||
Autoroute(AutorouteExecutionPermutator),
|
||||
TopoAutoroute(ng::AutorouteExecutionStepper<M>),
|
||||
PlaceVia(PlaceViaExecutionStepper),
|
||||
RemoveBands(RemoveBandsExecutionStepper),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
//! Manages the execution of routing commands within the autorouting system.
|
||||
|
||||
use std::{cmp::Ordering, ops::ControlFlow};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use contracts_try::debug_requires;
|
||||
use derive_getters::{Dissolve, Getters};
|
||||
|
|
@ -15,8 +15,8 @@ use thiserror::Error;
|
|||
use crate::{
|
||||
board::AccessMesadata,
|
||||
drawing::graph::PrimitiveIndex,
|
||||
geometry::{primitive::PrimitiveShape, shape::MeasureLength},
|
||||
graph::{GenericIndex, MakeRef},
|
||||
geometry::primitive::PrimitiveShape,
|
||||
graph::GenericIndex,
|
||||
layout::poly::PolyWeight,
|
||||
router::{
|
||||
navcord::Navcord,
|
||||
|
|
@ -28,14 +28,14 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
autoroute::AutorouteExecutionStepper,
|
||||
autoroute::AutorouteExecutionPermutator,
|
||||
compare_detours::CompareDetoursExecutionStepper,
|
||||
execution::{Command, ExecutionStepper},
|
||||
history::{History, HistoryError},
|
||||
measure_length::MeasureLengthExecutionStepper,
|
||||
place_via::PlaceViaExecutionStepper,
|
||||
remove_bands::RemoveBandsExecutionStepper,
|
||||
Autorouter, AutorouterError, PresortBy,
|
||||
Autorouter, AutorouterError,
|
||||
};
|
||||
|
||||
/// Trait for getting the information to display on the debug overlay,
|
||||
|
|
@ -167,48 +167,7 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
|
|||
fn dispatch_command(&mut self, command: &Command) -> Result<ExecutionStepper<M>, InvokerError> {
|
||||
Ok(match command {
|
||||
Command::Autoroute(selection, options) => {
|
||||
let mut ratlines = self.autorouter.selected_ratlines(selection);
|
||||
|
||||
match options.presort_by {
|
||||
PresortBy::RatlineIntersectionCountAndLength => {
|
||||
ratlines.sort_unstable_by(|a, b| {
|
||||
let a_intersector_count = a
|
||||
.ref_(self.autorouter())
|
||||
.find_intersecting_ratlines()
|
||||
.count();
|
||||
let b_intersector_count = b
|
||||
.ref_(self.autorouter())
|
||||
.find_intersecting_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_(self.autorouter()).length();
|
||||
let b_length = b.ref_(self.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 = self
|
||||
.autorouter
|
||||
.compare_detours_ratlines(*a, *b, *options)
|
||||
.unwrap();
|
||||
|
||||
if let Ok((al, bl)) = compare_detours.finish(&mut self.autorouter) {
|
||||
PartialOrd::partial_cmp(&al, &bl).unwrap()
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
ExecutionStepper::Autoroute(self.autorouter.autoroute_ratlines(ratlines, *options)?)
|
||||
ExecutionStepper::Autoroute(self.autorouter.autoroute(selection, *options)?)
|
||||
}
|
||||
Command::TopoAutoroute {
|
||||
selection,
|
||||
|
|
|
|||
Loading…
Reference in New Issue