diff --git a/crates/topola-cli/src/main.rs b/crates/topola-cli/src/main.rs index 8b660fd..1168674 100644 --- a/crates/topola-cli/src/main.rs +++ b/crates/topola-cli/src/main.rs @@ -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, diff --git a/crates/topola-egui/src/actions.rs b/crates/topola-egui/src/actions.rs index bb4d538..724508e 100644 --- a/crates/topola-egui/src/actions.rs +++ b/crates/topola-egui/src/actions.rs @@ -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 diff --git a/crates/topola-egui/src/menu_bar.rs b/crates/topola-egui/src/menu_bar.rs index 098e60b..c949199 100644 --- a/crates/topola-egui/src/menu_bar.rs +++ b/crates/topola-egui/src/menu_bar.rs @@ -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, diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index 37376c6..084f42a 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -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". diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index 6e07ef2..b30005c 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -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, } diff --git a/src/autorouter/invoker.rs b/src/autorouter/invoker.rs index 530ca1b..3a7b4f9 100644 --- a/src/autorouter/invoker.rs +++ b/src/autorouter/invoker.rs @@ -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 Invoker { 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)?) diff --git a/src/autorouter/ratline.rs b/src/autorouter/ratline.rs index 4d80d66..831e788 100644 --- a/src/autorouter/ratline.rs +++ b/src/autorouter/ratline.rs @@ -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 + '_ { + 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()) } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 7dd9106..3abd495 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -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()))