feat(autorouter/autorouter): Have separate ratsnest for each (principal) layer

This commit is contained in:
Mikolaj Wielgus 2025-10-15 15:12:46 +02:00
parent 1b578f3063
commit 8f59319902
19 changed files with 215 additions and 147 deletions

View File

@ -36,6 +36,7 @@ allowed_scopes = [
"autorouter/pointroute",
"autorouter/presorter",
"autorouter/ratsnest",
"autorouter/ratsnests",
"autorouter/ratline",
"autorouter/remove_bands",
"autorouter/selection",

View File

@ -21,12 +21,15 @@ pub trait AccessMesadata: AccessRules + std::panic::RefUnwindSafe {
/// Renames a layer based on its index.
fn bename_layer(&mut self, layer: usize, layername: String);
/// Retrieves the name of a layer by its index.
/// Retrieves the name of a layer from its index.
fn layer_layername(&self, layer: usize) -> Option<&str>;
/// Retrieves the index of a layer by its name.
/// Retrieves the index of a layer from its name.
fn layername_layer(&self, layername: &str) -> Option<usize>;
/// Return the number of the layers.
fn layer_count(&self) -> usize;
/// Renames a net based on its index.
fn bename_net(&mut self, net: usize, netname: String);
@ -76,15 +79,18 @@ pub struct SpecctraMesadata {
/// These rules are applied to all nets belonging to the respective net clas
class_rules: BTreeMap<String, SpecctraRule>,
/// Number of layers.
layer_count: usize,
// layername <-> layer for Layout
/// A bidirectional map between layer indices and layer names, allowing translation
/// between index-based layers in the layout and user-defined layer names.
pub layer_layername: BiBTreeMap<usize, String>,
layer_layername: BiBTreeMap<usize, String>,
// netname <-> net for Layout
/// A bidirectional map between network indices and network names in the PCB layout,
/// providing an easy way to reference nets by name or index.
pub net_netname: BiBTreeMap<usize, String>,
net_netname: BiBTreeMap<usize, String>,
// net -> netclass
/// A map that associates network indices with their respective net class names.
@ -105,6 +111,7 @@ impl SpecctraMesadata {
.enumerate()
.map(|(index, layer)| (index, layer.name.clone())),
);
let layer_count = pcb.structure.layers.len();
// assign IDs to all nets named in pcb.network
let net_netname = {
@ -157,6 +164,7 @@ impl SpecctraMesadata {
structure_rule: SpecctraRule::from_dsn(&structure_rule),
class_rules,
layer_layername,
layer_count,
net_netname,
net_netclass,
}
@ -204,6 +212,10 @@ impl AccessMesadata for SpecctraMesadata {
self.layer_layername.get_by_right(layername).copied()
}
fn layer_count(&self) -> usize {
self.layer_count
}
fn bename_net(&mut self, net: usize, netname: String) {
self.net_netname.insert(net, netname);
}

View File

@ -52,7 +52,7 @@ impl<'a> Displayer<'a> {
self.display_layout(ctx);
if menu_bar.show_ratsnest {
self.display_ratsnest();
self.display_ratsnest(menu_bar);
}
if menu_bar.show_navmesh || menu_bar.show_guide_circles || menu_bar.show_guide_bitangents {
@ -161,14 +161,14 @@ impl<'a> Displayer<'a> {
}
}
fn display_ratsnest(&mut self) {
fn display_ratsnest(&mut self, menu_bar: &MenuBar) {
let graph = self
.workspace
.interactor
.invoker()
.autorouter()
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(menu_bar.multilayer_autoroute_options.planar.principal_layer)
.graph();
for edge in graph.edge_references() {
if edge.weight().band_termseg.is_some() {

View File

@ -5,7 +5,7 @@
use std::collections::BTreeMap;
use geo::{point, Distance, Euclidean, Point};
use petgraph::graph::{EdgeIndex, NodeIndex};
use petgraph::graph::NodeIndex;
use rstar::{Envelope, RTreeObject, AABB};
use serde::{Deserialize, Serialize};
use specctra_core::mesadata::AccessMesadata;
@ -13,7 +13,7 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
compass_direction::{CardinalDirection, CompassDirection, OrdinalDirection},
ratline::RatlineIndex,
ratline::RatlineUid,
Autorouter,
},
board::edit::BoardEdit,
@ -42,7 +42,7 @@ pub enum TerminatingScheme {
#[derive(Clone, Debug)]
pub struct AnterouterPlan {
pub layer_map: BTreeMap<RatlineIndex, usize>,
pub layer_map: BTreeMap<RatlineUid, usize>,
pub ratline_endpoint_dot_to_terminating_scheme: BTreeMap<FixedDotIndex, TerminatingScheme>,
}
@ -67,8 +67,8 @@ impl Anterouter {
autorouter
.ratsnests
.on_principal_layer_mut(0)
.assign_layer_to_ratline(*ratline, *layer);
.on_principal_layer_mut(ratline.principal_layer)
.assign_layer_to_ratline(ratline.index, *layer);
if let Some(terminating_scheme) = self
.plan
@ -126,7 +126,7 @@ impl Anterouter {
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
ratline: EdgeIndex<usize>,
ratline: RatlineUid,
source_dot: FixedDotIndex,
target_layer: usize,
options: &AnterouterOptions,

View File

@ -31,7 +31,7 @@ use super::{
place_via::PlaceViaExecutionStepper,
planar_autoroute::PlanarAutorouteExecutionStepper,
pointroute::PointrouteExecutionStepper,
ratline::RatlineIndex,
ratline::RatlineUid,
ratsnest::RatvertexNodeIndex,
remove_bands::RemoveBandsExecutionStepper,
selection::{BandSelection, PinSelection},
@ -89,7 +89,9 @@ impl<M: AccessMesadata> Autorouter<M> {
point: Point,
options: PlanarAutorouteOptions,
) -> Result<PointrouteExecutionStepper, AutorouterError> {
let ratvertex = self.find_selected_ratvertex(selection).unwrap();
let ratvertex = self
.find_selected_ratvertex(selection, options.principal_layer)
.unwrap();
let origin_dot = match self
.ratsnests
.on_principal_layer_mut(0)
@ -117,11 +119,14 @@ impl<M: AccessMesadata> Autorouter<M> {
selection: &PinSelection,
options: MultilayerAutorouteOptions,
) -> Result<MultilayerAutorouteExecutionStepper, AutorouterError> {
let planner = Planner::new(self, &self.selected_ratlines(selection));
let planner = Planner::new(
self,
&self.selected_ratlines(selection, options.planar.principal_layer),
);
MultilayerAutorouteExecutionStepper::new(
self,
self.selected_ratlines(selection),
self.selected_ratlines(selection, options.planar.principal_layer),
planner.plan().clone(),
options,
)
@ -132,27 +137,24 @@ impl<M: AccessMesadata> Autorouter<M> {
selection: &PinSelection,
options: PlanarAutorouteOptions,
) -> Result<PlanarAutorouteExecutionPermutator, AutorouterError> {
PlanarAutorouteExecutionPermutator::new(self, self.selected_ratlines(selection), options)
PlanarAutorouteExecutionPermutator::new(
self,
self.selected_ratlines(selection, options.principal_layer),
options,
)
}
pub(super) fn planar_autoroute_ratlines(
&mut self,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
options: PlanarAutorouteOptions,
) -> Result<PlanarAutorouteExecutionStepper, AutorouterError> {
PlanarAutorouteExecutionStepper::new(self, ratlines, options)
}
pub fn undo_planar_autoroute(
&mut self,
selection: &PinSelection,
) -> Result<(), AutorouterError> {
self.undo_planar_autoroute_ratlines(self.selected_ratlines(selection))
}
pub(super) fn undo_planar_autoroute_ratlines(
&mut self,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
) -> Result<(), AutorouterError> {
for ratline in ratlines.iter() {
let band = ratline.ref_(self).band_termseg();
@ -177,7 +179,7 @@ impl<M: AccessMesadata> Autorouter<M> {
M: Clone,
{
self.topo_autoroute_ratlines(
self.selected_ratlines(selection),
self.selected_ratlines(selection, active_layer),
allowed_edges,
active_layer,
width,
@ -187,7 +189,7 @@ impl<M: AccessMesadata> Autorouter<M> {
pub(super) fn topo_autoroute_ratlines(
&mut self,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
allowed_edges: BTreeSet<ng::PieEdgeIndex>,
active_layer: usize,
width: f64,
@ -281,7 +283,7 @@ impl<M: AccessMesadata> Autorouter<M> {
selection: &PinSelection,
options: PlanarAutorouteOptions,
) -> Result<CompareDetoursExecutionStepper, AutorouterError> {
let ratlines = self.selected_ratlines(selection);
let ratlines = self.selected_ratlines(selection, options.principal_layer);
if ratlines.len() < 2 {
return Err(AutorouterError::NeedExactlyTwoRatlines);
}
@ -290,8 +292,8 @@ impl<M: AccessMesadata> Autorouter<M> {
pub(super) fn compare_detours_ratlines(
&mut self,
ratline1: RatlineIndex,
ratline2: RatlineIndex,
ratline1: RatlineUid,
ratline2: RatlineUid,
options: PlanarAutorouteOptions,
) -> Result<CompareDetoursExecutionStepper, AutorouterError> {
CompareDetoursExecutionStepper::new(self, ratline1, ratline2, options)
@ -304,29 +306,33 @@ impl<M: AccessMesadata> Autorouter<M> {
MeasureLengthExecutionStepper::new(selection)
}
pub(super) fn selected_ratlines(&self, selection: &PinSelection) -> Vec<RatlineIndex> {
pub(super) fn selected_ratlines(
&self,
selection: &PinSelection,
principal_layer: usize,
) -> Vec<RatlineUid> {
self.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.edge_indices()
.filter(|ratline| {
.filter(|index| {
let (source, target) = self
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.edge_endpoints(*ratline)
.edge_endpoints(*index)
.unwrap();
let source_ratvertex = self
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.node_weight(source)
.unwrap()
.node_index();
let to_ratvertex = self
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.node_weight(target)
.unwrap()
@ -335,19 +341,27 @@ impl<M: AccessMesadata> Autorouter<M> {
selection.contains_node(&self.board, source_ratvertex.into())
&& selection.contains_node(&self.board, to_ratvertex.into())
})
.map(|index| RatlineUid {
principal_layer,
index,
})
.collect()
}
fn find_selected_ratvertex(&self, selection: &PinSelection) -> Option<NodeIndex<usize>> {
fn find_selected_ratvertex(
&self,
selection: &PinSelection,
principal_layer: usize,
) -> Option<NodeIndex<usize>> {
self.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.node_indices()
.find(|ratvertex| {
selection.contains_node(
&self.board,
self.ratsnests()
.on_principal_layer(0)
.on_principal_layer(principal_layer)
.graph()
.node_weight(*ratvertex)
.unwrap()

View File

@ -19,15 +19,15 @@ use crate::{
use super::{
invoker::GetDebugOverlayData,
planar_autoroute::{PlanarAutorouteContinueStatus, PlanarAutorouteExecutionStepper},
ratline::RatlineIndex,
ratline::RatlineUid,
Autorouter, AutorouterError, PlanarAutorouteOptions,
};
pub struct CompareDetoursExecutionStepper {
autoroute: PlanarAutorouteExecutionStepper,
next_autoroute: Option<PlanarAutorouteExecutionStepper>,
ratline1: RatlineIndex,
ratline2: RatlineIndex,
ratline1: RatlineUid,
ratline2: RatlineUid,
total_length1: f64,
total_length2: f64,
done: bool,
@ -36,8 +36,8 @@ pub struct CompareDetoursExecutionStepper {
impl CompareDetoursExecutionStepper {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratline1: RatlineIndex,
ratline2: RatlineIndex,
ratline1: RatlineUid,
ratline2: RatlineUid,
options: PlanarAutorouteOptions,
) -> Result<Self, AutorouterError> {
Ok(Self {

View File

@ -178,27 +178,27 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
Command::TopoAutoroute {
selection,
allowed_edges,
active_layer,
active_layer: active_layer_name,
routed_band_width,
} => {
let ratlines = self.autorouter.selected_ratlines(selection);
let active_layer = self
.autorouter
.board
.layout()
.rules()
.layername_layer(active_layer_name)
.unwrap();
let ratlines = self.autorouter.selected_ratlines(selection, active_layer);
// TODO: consider "presort by pairwise detours"
ExecutionStepper::TopoAutoroute(
self.autorouter.topo_autoroute_ratlines(
ratlines,
allowed_edges.clone(),
self.autorouter
.board
.layout()
.rules()
.layername_layer(active_layer)
.unwrap(),
*routed_band_width,
None,
)?,
)
ExecutionStepper::TopoAutoroute(self.autorouter.topo_autoroute_ratlines(
ratlines,
allowed_edges.clone(),
active_layer,
*routed_band_width,
None,
)?)
}
Command::PlaceVia(weight) => {
ExecutionStepper::PlaceVia(self.autorouter.place_via(*weight)?)

View File

@ -13,7 +13,7 @@ use crate::{
invoker::GetDebugOverlayData,
permutator::PlanarAutorouteExecutionPermutator,
planar_autoroute::PlanarAutorouteContinueStatus,
ratline::RatlineIndex,
ratline::RatlineUid,
Autorouter, AutorouterError, PlanarAutorouteOptions,
},
board::edit::BoardEdit,
@ -36,7 +36,7 @@ pub struct MultilayerAutorouteExecutionStepper {
impl MultilayerAutorouteExecutionStepper {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
plan: AnterouterPlan,
options: MultilayerAutorouteOptions,
) -> Result<Self, AutorouterError> {

View File

@ -12,7 +12,7 @@ use crate::{
permuter::{PermuteRatlines, RatlinesPermuter},
planar_autoroute::{PlanarAutorouteContinueStatus, PlanarAutorouteExecutionStepper},
presorter::{PresortParams, PresortRatlines, SccIntersectionsAndLengthPresorter},
ratline::RatlineIndex,
ratline::RatlineUid,
Autorouter, AutorouterError, PlanarAutorouteOptions,
},
board::edit::BoardEdit,
@ -31,7 +31,7 @@ pub struct PlanarAutorouteExecutionPermutator {
impl PlanarAutorouteExecutionPermutator {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
options: PlanarAutorouteOptions,
) -> Result<Self, AutorouterError> {
let presorter = SccIntersectionsAndLengthPresorter::new(

View File

@ -11,7 +11,7 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
planar_autoroute::PlanarAutorouteExecutionStepper,
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, scc::Scc, Autorouter,
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineUid, scc::Scc, Autorouter,
PlanarAutorouteOptions,
},
drawing::graph::MakePrimitiveRef,
@ -25,7 +25,7 @@ pub trait PermuteRatlines {
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>>;
) -> Option<Vec<RatlineUid>>;
}
#[enum_dispatch(PermuteRatlines)]
@ -37,7 +37,7 @@ pub enum RatlinesPermuter {
impl RatlinesPermuter {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
presorter: SccIntersectionsAndLengthPresorter,
options: &PlanarAutorouteOptions,
) -> Self {
@ -52,13 +52,13 @@ impl RatlinesPermuter {
pub struct SccPermutationsRatlinePermuter {
sccs_permutations_iter: Skip<Permutations<std::vec::IntoIter<Scc>>>,
original_ratlines: Vec<RatlineIndex>,
original_ratlines: Vec<RatlineUid>,
}
impl SccPermutationsRatlinePermuter {
pub fn new(
_autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
presorter: SccIntersectionsAndLengthPresorter,
_options: &PlanarAutorouteOptions,
) -> Self {
@ -79,7 +79,7 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
_stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
) -> Option<Vec<RatlineUid>> {
let scc_permutation = self.sccs_permutations_iter.next()?;
let mut ratlines = vec![];
@ -88,17 +88,17 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
if scc.node_indices().contains(
&autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(ratline.principal_layer)
.graph()
.edge_endpoints(*ratline)
.edge_endpoints(ratline.index)
.unwrap()
.0,
) && scc.node_indices().contains(
&autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(ratline.principal_layer)
.graph()
.edge_endpoints(*ratline)
.edge_endpoints(ratline.index)
.unwrap()
.1,
) {
@ -118,7 +118,7 @@ pub struct RatlineCutsRatlinePermuter {
impl RatlineCutsRatlinePermuter {
pub fn new(
_autorouter: &mut Autorouter<impl AccessMesadata>,
_ratlines: Vec<RatlineIndex>,
_ratlines: Vec<RatlineUid>,
_presorter: SccIntersectionsAndLengthPresorter,
_options: &PlanarAutorouteOptions,
) -> Self {
@ -134,7 +134,7 @@ impl PermuteRatlines for RatlineCutsRatlinePermuter {
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
) -> Option<Vec<RatlineUid>> {
let curr_ratline = stepper.ratlines()[*stepper.curr_ratline_index()];
let terminating_dots = curr_ratline.ref_(autorouter).terminating_dots();
let bands_cut_by_ratline: Vec<_> = autorouter

View File

@ -25,7 +25,7 @@ use crate::{
};
use super::{
invoker::GetDebugOverlayData, ratline::RatlineIndex, Autorouter, AutorouterError,
invoker::GetDebugOverlayData, ratline::RatlineUid, Autorouter, AutorouterError,
PlanarAutorouteOptions,
};
@ -43,7 +43,7 @@ pub enum PlanarAutorouteContinueStatus {
#[derive(Getters)]
pub struct PlanarAutorouteExecutionStepper {
/// The ratlines which we are routing.
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
/// Keeps track of the current ratline being routed, if one is active.
curr_ratline_index: usize,
/// Stores the current route being processed, if any.
@ -64,7 +64,7 @@ impl PlanarAutorouteExecutionStepper {
/// and stores the associated data for future routing steps.
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: Vec<RatlineUid>,
options: PlanarAutorouteOptions,
) -> Result<Self, AutorouterError> {
if ratlines.is_empty() {
@ -187,9 +187,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteCo
autorouter
.ratsnests
.on_principal_layer_mut(0)
.on_principal_layer_mut(self.options.principal_layer)
.assign_band_termseg_to_ratline(
self.ratlines[self.curr_ratline_index],
self.ratlines[self.curr_ratline_index].index,
band_termseg,
);
@ -236,13 +236,13 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for PlanarAutorouteExecutionStepper
}
impl<M: AccessMesadata> Permutate<Autorouter<M>> for PlanarAutorouteExecutionStepper {
type Index = RatlineIndex;
type Index = RatlineUid;
type Output = Result<(), AutorouterError>;
fn permutate(
&mut self,
autorouter: &mut Autorouter<M>,
permutation: Vec<RatlineIndex>,
permutation: Vec<RatlineUid>,
) -> Result<(), AutorouterError> {
let Some(new_index) = permutation
.iter()

View File

@ -10,7 +10,7 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
anterouter::{AnterouterPlan, TerminatingScheme},
ratline::RatlineIndex,
ratline::RatlineUid,
Autorouter,
},
drawing::{
@ -27,7 +27,7 @@ pub struct Planner {
}
impl Planner {
pub fn new(autorouter: &Autorouter<impl AccessMesadata>, ratlines: &[RatlineIndex]) -> Self {
pub fn new(autorouter: &Autorouter<impl AccessMesadata>, ratlines: &[RatlineUid]) -> Self {
let mut plan = AnterouterPlan {
layer_map: ratlines
.iter()

View File

@ -7,7 +7,7 @@ use enum_dispatch::enum_dispatch;
use petgraph::algo::tarjan_scc;
use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{ratline::RatlineIndex, scc::Scc, Autorouter};
use crate::autorouter::{ratline::RatlineUid, scc::Scc, Autorouter};
pub struct PresortParams {
pub intersector_count_weight: f64,
@ -19,8 +19,8 @@ pub trait PresortRatlines {
fn presort_ratlines(
&self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex>;
ratlines: &[RatlineUid],
) -> Vec<RatlineUid>;
}
#[enum_dispatch(PresortRatlines)]
@ -36,12 +36,16 @@ pub struct SccIntersectionsAndLengthPresorter {
impl SccIntersectionsAndLengthPresorter {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &[RatlineIndex],
ratlines: &[RatlineUid],
params: &PresortParams,
) -> Self {
// FIXME: Unnecessary copy.
let mut filtered_ratsnest = autorouter.ratsnests().on_principal_layer(0).graph().clone();
filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i));
let mut filtered_ratsnest = autorouter
.ratsnests()
.on_principal_layer(ratlines[0].principal_layer)
.graph()
.clone();
filtered_ratsnest.retain_edges(|_g, i| ratlines.iter().any(|ratline| ratline.index == i));
let mut sccs: Vec<_> = tarjan_scc(&filtered_ratsnest)
.into_iter()
@ -65,8 +69,8 @@ impl PresortRatlines for SccIntersectionsAndLengthPresorter {
fn presort_ratlines(
&self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex> {
ratlines: &[RatlineUid],
) -> Vec<RatlineUid> {
let mut presorted_ratlines = vec![];
for scc in self.sccs.iter() {

View File

@ -19,7 +19,11 @@ use crate::{
use super::{ratsnest::RatvertexNodeIndex, Autorouter};
pub type RatlineIndex = EdgeIndex<usize>;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct RatlineUid {
pub principal_layer: usize,
pub index: EdgeIndex<usize>,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct RatlineWeight {
@ -27,7 +31,7 @@ pub struct RatlineWeight {
pub band_termseg: Option<BandTermsegIndex>,
}
impl<'a, M: AccessMesadata + 'a> MakeRef<'a, Autorouter<M>> for RatlineIndex {
impl<'a, M: AccessMesadata + 'a> MakeRef<'a, Autorouter<M>> for RatlineUid {
type Output = RatlineRef<'a, M>;
fn ref_(&self, autorouter: &'a Autorouter<M>) -> RatlineRef<'a, M> {
RatlineRef::new(*self, autorouter)
@ -35,21 +39,24 @@ impl<'a, M: AccessMesadata + 'a> MakeRef<'a, Autorouter<M>> for RatlineIndex {
}
pub struct RatlineRef<'a, M: AccessMesadata> {
index: RatlineIndex,
uid: RatlineUid,
autorouter: &'a Autorouter<M>,
}
impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
pub fn new(index: RatlineIndex, autorouter: &'a Autorouter<M>) -> Self {
Self { index, autorouter }
pub fn new(index: RatlineUid, autorouter: &'a Autorouter<M>) -> Self {
Self {
uid: index,
autorouter,
}
}
pub fn band_termseg(&self) -> BandTermsegIndex {
self.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_weight(self.index)
.edge_weight(self.uid.index)
.unwrap()
.band_termseg
.unwrap()
@ -59,15 +66,15 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let (source, target) = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_endpoints(self.index)
.edge_endpoints(self.uid.index)
.unwrap();
let source_dot = match self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(source)
.unwrap()
@ -80,7 +87,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let target_dot = match self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(target)
.unwrap()
@ -97,15 +104,15 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let (source, target) = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_endpoints(self.index)
.edge_endpoints(self.uid.index)
.unwrap();
let source_dot = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(source)
.unwrap()
@ -116,7 +123,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let target_dot = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(target)
.unwrap()
@ -131,9 +138,9 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
pub fn layer(&self) -> usize {
self.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_weight(self.index)
.edge_weight(self.uid.index)
.unwrap()
.layer
}
@ -164,14 +171,18 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
})
}
pub fn interiorly_cut_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ {
pub fn interiorly_cut_ratlines(&self) -> impl Iterator<Item = RatlineUid> + '_ {
let self_line_segment = self.line_segment();
self.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_indices()
.map(|index| RatlineUid {
principal_layer: self.uid.principal_layer,
index,
})
.filter(move |other| {
let (self_source, self_target) = self.endpoint_indices();
let (other_source, other_target) = other.ref_(self.autorouter).endpoint_indices();
@ -201,7 +212,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let source_pos = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(source)
.unwrap()
@ -209,7 +220,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
let target_pos = self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.node_weight(target)
.unwrap()
@ -221,9 +232,9 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
pub fn endpoint_indices(&self) -> (NodeIndex<usize>, NodeIndex<usize>) {
self.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(self.uid.principal_layer)
.graph()
.edge_endpoints(self.index)
.edge_endpoints(self.uid.index)
.unwrap()
}
}

View File

@ -9,7 +9,11 @@ use std::{
use enum_dispatch::enum_dispatch;
use geo::Point;
use petgraph::{data::Element, graph::NodeIndex, prelude::StableUnGraph};
use petgraph::{
data::Element,
graph::{EdgeIndex, NodeIndex},
prelude::StableUnGraph,
};
use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2};
use specctra_core::mesadata::AccessMesadata;
@ -27,10 +31,7 @@ use crate::{
triangulation::{GetTrianvertexNodeIndex, Triangulation},
};
use super::{
conncomps::ConncompsWithPrincipalLayer,
ratline::{RatlineIndex, RatlineWeight},
};
use super::{conncomps::ConncompsWithPrincipalLayer, ratline::RatlineWeight};
#[enum_dispatch(GetIndex)]
#[derive(Debug, Clone, Copy, PartialEq)]
@ -108,8 +109,10 @@ pub struct Ratsnest {
}
impl Ratsnest {
pub fn new(board: &Board<impl AccessMesadata>) -> Result<Self, InsertionError> {
let principal_layer = 0;
pub fn new(
board: &Board<impl AccessMesadata>,
principal_layer: usize,
) -> Result<Self, InsertionError> {
let conncomps = ConncompsWithPrincipalLayer::new(board, principal_layer);
let mut this = Self {
@ -239,16 +242,19 @@ impl Ratsnest {
.insert(layer, terminating_dot);
}
pub fn assign_layer_to_ratline(&mut self, ratline: RatlineIndex, layer: usize) {
self.graph.edge_weight_mut(ratline).unwrap().layer = layer;
pub fn assign_layer_to_ratline(&mut self, ratline_index: EdgeIndex<usize>, layer: usize) {
self.graph.edge_weight_mut(ratline_index).unwrap().layer = layer;
}
pub fn assign_band_termseg_to_ratline(
&mut self,
ratline: RatlineIndex,
ratline_index: EdgeIndex<usize>,
termseg: BandTermsegIndex,
) {
self.graph.edge_weight_mut(ratline).unwrap().band_termseg = Some(termseg);
self.graph
.edge_weight_mut(ratline_index)
.unwrap()
.band_termseg = Some(termseg);
}
pub fn graph(&self) -> &StableUnGraph<RatvertexWeight, RatlineWeight, usize> {

View File

@ -11,7 +11,12 @@ pub struct Ratsnests(Box<[Ratsnest]>);
impl Ratsnests {
pub fn new(board: &Board<impl AccessMesadata>) -> Result<Self, InsertionError> {
Ok(Self(Box::new([Ratsnest::new(board)?])))
Ok(Self(
(0..board.mesadata().layer_count())
.map(|principal_layer| Ratsnest::new(board, principal_layer))
.collect::<Result<Vec<_>, _>>()
.map(Vec::into_boxed_slice)?,
))
}
pub fn on_principal_layer(&self, principal_layer: usize) -> &Ratsnest {

View File

@ -8,7 +8,7 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
ratline::{RatlineIndex, RatlineWeight},
ratline::{RatlineUid, RatlineWeight},
ratsnest::RatvertexWeight,
Autorouter,
},
@ -27,7 +27,7 @@ pub struct Scc {
impl Scc {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &[RatlineIndex],
ratlines: &[RatlineUid],
filtered_ratsnest: &StableUnGraph<RatvertexWeight, RatlineWeight, usize>,
node_indices: Vec<NodeIndex<usize>>,
) -> Self {
@ -40,10 +40,10 @@ impl Scc {
for ratline in ratlines.iter() {
if this
.node_indices
.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0)
.contains(&filtered_ratsnest.edge_endpoints(ratline.index).unwrap().0)
&& this
.node_indices
.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1)
.contains(&filtered_ratsnest.edge_endpoints(ratline.index).unwrap().1)
{
this.length += ratline.ref_(autorouter).length();
this.intersector_count +=
@ -74,23 +74,23 @@ impl<'a, M: AccessMesadata> SccRef<'a, M> {
Self { scc, autorouter }
}
pub fn contains(&self, ratline: RatlineIndex) -> bool {
pub fn contains(&self, ratline: RatlineUid) -> bool {
self.scc.node_indices().contains(
&self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(ratline.principal_layer)
.graph()
.edge_endpoints(ratline)
.edge_endpoints(ratline.index)
.unwrap()
.0,
) && self.scc.node_indices().contains(
&self
.autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(ratline.principal_layer)
.graph()
.edge_endpoints(ratline)
.edge_endpoints(ratline.index)
.unwrap()
.1,
)

View File

@ -9,6 +9,7 @@ use topola::{
conncomps::ConncompsWithPrincipalLayer,
history::{History, HistoryError},
invoker::{Invoker, InvokerError},
ratline::RatlineUid,
Autorouter,
},
board::{edit::BoardEdit, AccessMesadata, Board},
@ -125,7 +126,7 @@ pub fn assert_no_loose_nodes(autorouter: &Autorouter<impl AccessMesadata>) {
}
}
pub fn assert_navnode_count(
pub fn assert_layer_0_navnode_count(
autorouter: &mut Autorouter<SpecctraMesadata>,
origin_pin: &str,
destination_pin: &str,
@ -136,6 +137,10 @@ pub fn assert_navnode_count(
.on_principal_layer(0)
.graph()
.edge_indices()
.map(|index| RatlineUid {
principal_layer: 0,
index,
})
.collect::<Vec<_>>()
.iter()
.find_map(|ratline| {
@ -175,13 +180,23 @@ pub fn assert_that_all_single_layer_groundless_ratlines_are_autorouted(
autorouter: &mut Autorouter<impl AccessMesadata>,
layername: &str,
) {
let conncomps = ConncompsWithPrincipalLayer::new(autorouter.board(), 0);
let layer = autorouter
.board()
.layout()
.rules()
.layername_layer(layername)
.unwrap();
let conncomps = ConncompsWithPrincipalLayer::new(autorouter.board(), layer);
for ratline in autorouter
.ratsnests()
.on_principal_layer(0)
.on_principal_layer(layer)
.graph()
.edge_indices()
.map(|index| RatlineUid {
principal_layer: 0,
index,
})
{
let (origin_dot, destination_dot) = ratline.ref_(autorouter).endpoint_dots();

View File

@ -100,7 +100,7 @@ fn autoroute_tht_de9_to_tht_de9_in_predefined_order(variant: &str) {
#[apply(test_master)]
fn autoroute_0603_breakout(variant: &str) {
let mut autorouter = common::load_design("tests/single_layer/0603_breakout/0603_breakout.dsn");
common::assert_navnode_count(&mut autorouter, "R1-2", "J1-2", 22);
common::assert_layer_0_navnode_count(&mut autorouter, "R1-2", "J1-2", 22);
let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert_and_report(
&mut invoker,
@ -122,7 +122,7 @@ fn autoroute_tht_diode_bridge_rectifier(variant: &str) {
let mut autorouter = common::load_design(
"tests/single_layer/tht_diode_bridge_rectifier/tht_diode_bridge_rectifier.dsn",
);
common::assert_navnode_count(&mut autorouter, "J2-2", "D4-2", 68);
common::assert_layer_0_navnode_count(&mut autorouter, "J2-2", "D4-2", 68);
let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert_and_report(
&mut invoker,
@ -162,7 +162,7 @@ fn autoroute_4x_3rd_order_smd_lc_filters(variant: &str) {
let mut autorouter = common::load_design(
"tests/single_layer/4x_3rd_order_smd_lc_filters/4x_3rd_order_smd_lc_filters.dsn",
);
common::assert_navnode_count(&mut autorouter, "J1-1", "L1-1", 558);
common::assert_layer_0_navnode_count(&mut autorouter, "J1-1", "L1-1", 558);
let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert_and_report(
&mut invoker,
@ -206,7 +206,7 @@ fn test_tht_3pin_xlr_to_tht_3pin_xlr(#[case] variant: &str) {
fn autoroute_vga_dac_breakout(variant: &str) {
let mut autorouter =
common::load_design("tests/single_layer/vga_dac_breakout/vga_dac_breakout.dsn");
common::assert_navnode_count(&mut autorouter, "J1-2", "R4-1", 272);
common::assert_layer_0_navnode_count(&mut autorouter, "J1-2", "R4-1", 272);
let mut invoker = common::create_invoker_and_assert(autorouter);
common::replay_and_assert_and_report(
&mut invoker,