feat(autorouter/autorouter): Add presorting by ratline intersection count and length

Sadly, this does not work well for the DE-9 to DE-9 test, but it is a
step forward.
This commit is contained in:
Mikolaj Wielgus 2025-07-12 17:27:10 +02:00 committed by mikolaj
parent 0752817538
commit 43005eb864
8 changed files with 85 additions and 28 deletions

View File

@ -11,6 +11,7 @@ use topola::autorouter::invoker::Invoker;
use topola::autorouter::selection::PinSelection;
use topola::autorouter::Autorouter;
use topola::autorouter::AutorouterOptions;
use topola::autorouter::PresortBy;
use topola::layout::LayoutEdit;
use topola::router::RouterOptions;
use topola::specctra::design::SpecctraDesign;
@ -38,7 +39,7 @@ fn main() -> Result<(), std::io::Error> {
Command::Autoroute(
PinSelection::new_select_layer(&board, 0),
AutorouterOptions {
presort_by_pairwise_detours: false,
presort_by: PresortBy::RatlineIntersectionCountAndLength,
router_options: RouterOptions {
wrap_around_bands: true,
squeeze_through_under_bends: false,

View File

@ -10,7 +10,7 @@ use crate::{
};
use egui::{Context, Ui};
use topola::autorouter::AutorouterOptions;
use topola::autorouter::{AutorouterOptions, PresortBy};
pub struct FileActions {
pub open_design: Trigger,
@ -330,10 +330,12 @@ impl RouteActions {
ui.separator();
ui.menu_button(tr.text("tr-menu-options"), |ui| {
ui.checkbox(
&mut autorouter_options.presort_by_pairwise_detours,
tr.text("tr-menu-route-options-presort-by-pairwise-detours"),
);
egui::ComboBox::from_label(tr.text("tr-menu-route-options-presort-by"))
.selected_text(format!("{:?}", autorouter_options.presort_by))
.show_ui(ui, |ui| {
ui.selectable_value(&mut autorouter_options.presort_by, PresortBy::RatlineIntersectionCountAndLength, tr.text("tr-menu-route-options-presort-by-ratline-intersection-count-and-length"));
ui.selectable_value(&mut autorouter_options.presort_by, PresortBy::PairwiseDetours, tr.text("tr-menu-route-options-presort-by-pairwise-detours"));
});
ui.checkbox(
&mut autorouter_options
.router_options

View File

@ -7,6 +7,7 @@ use std::{collections::BTreeSet, ops::ControlFlow, path::Path, sync::mpsc::Sende
use topola::{
autorouter::{
execution::Command, invoker::InvokerError, selection::Selection, AutorouterOptions,
PresortBy,
},
board::AccessMesadata,
interactor::{interaction::InteractionStepper, route_plan::RoutePlan},
@ -42,7 +43,7 @@ impl MenuBar {
pub fn new() -> Self {
Self {
autorouter_options: AutorouterOptions {
presort_by_pairwise_detours: false,
presort_by: PresortBy::RatlineIntersectionCountAndLength,
router_options: RouterOptions {
routed_band_width: 100.0,
wrap_around_bands: true,

View File

@ -47,7 +47,9 @@ tr-menu-help-online-documentation = Online Documentation
# Misnamed tag, TODO fix this.
tr-menu-options = Options
tr-menu-route-options-presort-by-pairwise-detours = Presort by Pairwise Detours
tr-menu-route-options-presort-by = Presort by
tr-menu-route-options-presort-by-ratline-intersection-count-and-length = Intersection Count and Length
tr-menu-route-options-presort-by-pairwise-detours = Pairwise Detours
## Continuously applied, so use imperfective aspect if possible, e.g. in Polish
## it should be "przeciskaj pod taśmami" instead of "przeciśnij pod taśmami".

View File

@ -12,7 +12,7 @@ use thiserror::Error;
use crate::{
board::{AccessMesadata, Board},
drawing::{band::BandTermsegIndex, dot::FixedDotIndex, Infringement},
drawing::{band::BandTermsegIndex, Infringement},
graph::MakeRef,
layout::{via::ViaWeight, LayoutEdit},
router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions},
@ -31,9 +31,15 @@ use super::{
selection::{BandSelection, PinSelection},
};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum PresortBy {
RatlineIntersectionCountAndLength,
PairwiseDetours,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct AutorouterOptions {
pub presort_by_pairwise_detours: bool,
pub presort_by: PresortBy,
pub router_options: RouterOptions,
}

View File

@ -15,8 +15,8 @@ use thiserror::Error;
use crate::{
board::AccessMesadata,
drawing::graph::PrimitiveIndex,
geometry::{edit::ApplyGeometryEdit, primitive::PrimitiveShape},
graph::GenericIndex,
geometry::{edit::ApplyGeometryEdit, primitive::PrimitiveShape, shape::MeasureLength},
graph::{GenericIndex, MakeRef},
layout::poly::PolyWeight,
router::{
navcord::Navcord,
@ -35,7 +35,7 @@ use super::{
measure_length::MeasureLengthExecutionStepper,
place_via::PlaceViaExecutionStepper,
remove_bands::RemoveBandsExecutionStepper,
Autorouter, AutorouterError,
Autorouter, AutorouterError, PresortBy,
};
/// Trait for getting the information to display on the debug overlay,
@ -169,18 +169,43 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
Command::Autoroute(selection, options) => {
let mut ratlines = self.autorouter.selected_ratlines(selection);
if options.presort_by_pairwise_detours {
ratlines.sort_unstable_by(|a, b| {
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)?)

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use geo::{Distance, Euclidean};
use geo::{line_intersection::line_intersection, Distance, Euclidean, Line};
use petgraph::graph::EdgeIndex;
use specctra_core::mesadata::AccessMesadata;
@ -73,31 +73,51 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
(source_dot, target_dot)
}
}
impl<'a, M: AccessMesadata> MeasureLength for RatlineRef<'a, M> {
fn length(&self) -> f64 {
let (ratvertex0, ratvertex1) = self
pub fn find_intersecting_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ {
let self_line = self.line();
self.autorouter
.ratsnest()
.graph()
.edge_indices()
.filter(move |other| {
let other_line = other.ref_(self.autorouter).line();
line_intersection(self_line, other_line).is_some()
})
}
pub fn line(&self) -> Line {
let (source, target) = self
.autorouter
.ratsnest
.graph()
.edge_endpoints(self.index)
.unwrap();
let ratvertex0_pos = self
let source_pos = self
.autorouter
.ratsnest
.graph()
.node_weight(ratvertex0)
.node_weight(source)
.unwrap()
.pos;
let ratvertex1_pos = self
let target_pos = self
.autorouter
.ratsnest
.graph()
.node_weight(ratvertex1)
.node_weight(target)
.unwrap()
.pos;
Euclidean::distance(&ratvertex0_pos, &ratvertex1_pos)
Line::new(source_pos, target_pos)
}
}
impl<'a, M: AccessMesadata> MeasureLength for RatlineRef<'a, M> {
fn length(&self) -> f64 {
let line = self.line();
Euclidean::distance(&line.start_point(), &line.end_point())
}
}

View File

@ -84,7 +84,7 @@ pub fn assert_navnode_count(
.iter()
.find_map(|ratline| {
let (candidate_origin, candidate_destination) =
autorouter.ratline_endpoint_dots(*ratline);
ratline.ref_(autorouter).endpoint_dots();
let candidate_origin_pin = autorouter
.board()
.node_pinname(&GenericNode::Primitive(candidate_origin.into()))