feat(autorouter/ratsnest): Have one terminating dot for each layer of ratvertex

Some routes projects still crash while autorouting, but we are
progressing.
This commit is contained in:
Mikolaj Wielgus 2025-09-29 18:16:27 +02:00
parent d53aa2a678
commit b50e58b0fa
11 changed files with 113 additions and 53 deletions

View File

@ -11,17 +11,18 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{ratline::RatlineIndex, Autorouter},
board::edit::BoardEdit,
drawing::{
dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitiveRef},
primitive::MakePrimitiveShape,
},
geometry::GetLayer,
geometry::{GenericNode, GetLayer},
graph::{GenericIndex, GetIndex, MakeRef},
layout::{
poly::{MakePolygon, PolyWeight},
via::ViaWeight,
CompoundWeight, LayoutEdit,
CompoundWeight,
},
math::Circle,
};
@ -53,15 +54,23 @@ impl Anterouter {
let endpoint_indices = ratline.ref_(autorouter).endpoint_indices();
let endpoint_dots = ratline.ref_(autorouter).endpoint_dots();
autorouter
.ratsnest
.assign_layer_to_ratline(*ratline, *layer);
if let Some(terminating_scheme) = self
.plan
.ratline_endpoint_dot_to_terminating_scheme
.get(&endpoint_dots.0)
{
match terminating_scheme {
TerminatingScheme::ExistingFixedDot(terminating_dot) => autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(endpoint_indices.0, *terminating_dot),
TerminatingScheme::ExistingFixedDot(terminating_dot) => {
autorouter.ratsnest.assign_terminating_dot_to_ratvertex(
endpoint_indices.0,
*layer,
*terminating_dot,
)
}
TerminatingScheme::Anteroute(pin_bbox_to_anchor) => self
.anteroute_dot_to_anchor(
autorouter,
@ -79,9 +88,13 @@ impl Anterouter {
.get(&endpoint_dots.1)
{
match terminating_scheme {
TerminatingScheme::ExistingFixedDot(terminating_dot) => autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(endpoint_indices.1, *terminating_dot),
TerminatingScheme::ExistingFixedDot(terminating_dot) => {
autorouter.ratsnest.assign_terminating_dot_to_ratvertex(
endpoint_indices.1,
*layer,
*terminating_dot,
)
}
TerminatingScheme::Anteroute(pin_bbox_to_anchor) => self
.anteroute_dot_to_anchor(
autorouter,
@ -170,10 +183,10 @@ impl Anterouter {
//let via_bbox_to_anchor = [-pin_bbox_to_anchor[0], -pin_bbox_to_anchor[1]];
let mut layout_edit = LayoutEdit::new();
let mut board_edit = BoardEdit::new();
if let Ok((.., dots)) = autorouter.board.layout_mut().add_via(
&mut layout_edit,
if let Ok((.., dots)) = autorouter.board.add_via(
&mut board_edit,
ViaWeight {
from_layer: std::cmp::min(pin_layer, to_layer),
to_layer: std::cmp::max(pin_layer, to_layer),
@ -183,6 +196,10 @@ impl Anterouter {
},
maybe_net: pin_maybe_net,
},
autorouter
.board()
.node_pinname(&GenericNode::Primitive(dot.into()))
.cloned(),
) {
let terminating_dot = dots
.iter()
@ -193,9 +210,11 @@ impl Anterouter {
.layer()
})
.unwrap();
autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(ratvertex, *terminating_dot);
autorouter.ratsnest.assign_terminating_dot_to_ratvertex(
ratvertex,
to_layer,
*terminating_dot,
);
}
/*let bbox = if let Some(poly) = autorouter.board().layout().drawing().geometry().compounds(dot).find(|(_, compound_weight)| {
matches!(compound_weight, CompoundWeight::Poly(..))

View File

@ -7,12 +7,12 @@ use geo::Point;
use petgraph::graph::NodeIndex;
use serde::{Deserialize, Serialize};
use spade::InsertionError;
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeSet;
use thiserror::Error;
use crate::{
autorouter::{
anterouter::AnterouterPlan, multilayer_autoroute::MultilayerAutorouteExecutionStepper,
multilayer_autoroute::MultilayerAutorouteExecutionStepper,
permutator::PlanarAutorouteExecutionPermutator, planner::Planner,
},
board::{AccessMesadata, Board},
@ -213,22 +213,22 @@ impl<M: AccessMesadata> Autorouter<M> {
active_layer,
allowed_edges,
ratlines.into_iter().filter_map(|ratline| {
let (source, target) = ratline.ref_(self).terminating_dots();
let (origin, destination) = ratline.ref_(self).terminating_dots();
if navmesh
.as_ref()
.node_data(&NavmeshIndex::Primal(source))
.node_data(&NavmeshIndex::Primal(origin))
.is_none()
|| navmesh
.as_ref()
.node_data(&NavmeshIndex::Primal(target))
.node_data(&NavmeshIndex::Primal(destination))
.is_none()
{
// e.g. due to wrong active layer
return None;
}
if self.board.band_between_nodes(source, target).is_some() {
if self.board.band_between_nodes(origin, destination).is_some() {
// already connected
return None;
}
@ -236,8 +236,8 @@ impl<M: AccessMesadata> Autorouter<M> {
got_any_valid_goals = true;
Some(ng::Goal {
source,
target,
source: origin,
target: destination,
width,
})
}),

View File

@ -86,7 +86,7 @@ impl<M: AccessMesadata + Clone> ExecutionStepper<M> {
for (ep, band) in &autoroute.last_bands {
let (source, target) = ep.end_points.into();
autorouter.board.try_set_band_between_nodes(
&mut autoroute.last_recorder.data_edit,
&mut autoroute.last_recorder.board_data_edit,
source,
target,
*band,

View File

@ -207,7 +207,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteCo
self.curr_ratline_index += 1;
if let Some(new_ratline) = self.ratlines.get(self.curr_ratline_index) {
let (source, target) = new_ratline.ref_(autorouter).terminating_dots();
let (origin, destination) = new_ratline.ref_(autorouter).terminating_dots();
let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options);
@ -216,8 +216,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteCo
self.route = Some(router.route(
recorder,
source,
target,
origin,
destination,
self.options.router_options.routed_band_width,
)?);
}

View File

@ -99,7 +99,7 @@ impl Planner {
None
}
})
.map_or(TerminatingScheme::Anteroute([-1.0, -1.0]), |dot| {
.map_or(TerminatingScheme::Anteroute([-2.0, 0.0]), |dot| {
TerminatingScheme::ExistingFixedDot(dot)
}),
)

View File

@ -12,7 +12,7 @@ use crate::{
dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex},
},
geometry::{shape::MeasureLength, GetLayer},
geometry::shape::MeasureLength,
graph::MakeRef,
triangulation::GetTrianvertexNodeIndex,
};
@ -23,6 +23,7 @@ pub type RatlineIndex = EdgeIndex<usize>;
#[derive(Debug, Default, Clone, Copy)]
pub struct RatlineWeight {
pub layer: usize,
pub band_termseg: Option<BandTermsegIndex>,
}
@ -102,7 +103,9 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.graph()
.node_weight(source)
.unwrap()
.maybe_terminating_dot
.layer_terminating_dots
.get(&self.layer())
.copied()
.unwrap_or(self.endpoint_dots().0);
let target_dot = self
.autorouter
@ -110,17 +113,25 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.graph()
.node_weight(target)
.unwrap()
.maybe_terminating_dot
.layer_terminating_dots
.get(&self.layer())
.copied()
.unwrap_or(self.endpoint_dots().1);
(source_dot, target_dot)
}
pub fn layer(&self) -> usize {
self.endpoint_dots()
self.autorouter
.ratsnest()
.graph()
.edge_weight(self.index)
.unwrap()
.layer
/*self.endpoint_dots()
.0
.primitive_ref(self.autorouter.board().layout().drawing())
.layer()
.layer()*/
}
pub fn net(&self) -> usize {

View File

@ -48,11 +48,11 @@ impl From<RatvertexNodeIndex> for crate::layout::NodeIndex {
}
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct RatvertexWeight {
vertex: RatvertexNodeIndex,
pub pos: Point,
pub maybe_terminating_dot: Option<FixedDotIndex>,
pub layer_terminating_dots: BTreeMap<usize, FixedDotIndex>,
}
impl GetTrianvertexNodeIndex<RatvertexNodeIndex> for RatvertexWeight {
@ -118,11 +118,11 @@ impl Ratsnest {
let mut triangulations = BTreeMap::new();
this.add_layer_to_ratsnest(board, &mut triangulations, principal_layer);
this.add_layer_to_ratsnest_triangulations(board, &mut triangulations, principal_layer);
for layer in 0..board.layout().drawing().layer_count() {
if layer != principal_layer {
this.add_layer_to_ratsnest(board, &mut triangulations, layer);
this.add_layer_to_ratsnest_triangulations(board, &mut triangulations, layer);
}
}
@ -158,7 +158,7 @@ impl Ratsnest {
Ok(this)
}
fn add_layer_to_ratsnest(
fn add_layer_to_ratsnest_triangulations(
&mut self,
board: &Board<impl AccessMesadata>,
triangulations: &mut BTreeMap<
@ -190,7 +190,7 @@ impl Ratsnest {
triangulation.add_vertex(RatvertexWeight {
vertex,
pos,
maybe_terminating_dot: None,
layer_terminating_dots: BTreeMap::new(),
})?;
Ok(())
};
@ -229,12 +229,18 @@ impl Ratsnest {
pub fn assign_terminating_dot_to_ratvertex(
&mut self,
node_index: NodeIndex<usize>,
layer: usize,
terminating_dot: FixedDotIndex,
) {
self.graph
.node_weight_mut(node_index)
.unwrap()
.maybe_terminating_dot = Some(terminating_dot)
.layer_terminating_dots
.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_band_termseg_to_ratline(

View File

@ -29,7 +29,7 @@ impl Edit for BoardDataEdit {
#[derive(Debug, Clone, Default)]
pub struct BoardEdit {
pub data_edit: BoardDataEdit,
pub board_data_edit: BoardDataEdit,
pub layout_edit: LayoutEdit,
}
@ -40,7 +40,7 @@ impl BoardEdit {
pub fn new_from_edits(data_edit: BoardDataEdit, layout_edit: LayoutEdit) -> Self {
Self {
data_edit,
board_data_edit: data_edit,
layout_edit,
}
}
@ -48,12 +48,12 @@ impl BoardEdit {
impl Edit for BoardEdit {
fn reverse_inplace(&mut self) {
self.data_edit.reverse_inplace();
self.board_data_edit.reverse_inplace();
self.layout_edit.reverse_inplace();
}
fn merge(&mut self, edit: Self) {
self.data_edit.merge(edit.data_edit);
self.board_data_edit.merge(edit.board_data_edit);
self.layout_edit.merge(edit.layout_edit);
}
}

View File

@ -21,11 +21,11 @@ use crate::{
dot::{FixedDotIndex, FixedDotWeight},
graph::PrimitiveIndex,
seg::{FixedSegIndex, FixedSegWeight},
DrawingException,
DrawingException, Infringement,
},
geometry::{edit::ApplyGeometryEdit, GenericNode, GetLayer},
graph::{GenericIndex, MakeRef},
layout::{poly::PolyWeight, CompoundWeight, Layout, NodeIndex},
layout::{poly::PolyWeight, via::ViaWeight, CompoundWeight, Layout, NodeIndex},
router::ng::EtchedPath,
};
@ -105,6 +105,24 @@ impl<M> Board<M> {
}
impl<M: AccessMesadata> Board<M> {
pub fn add_via(
&mut self,
recorder: &mut BoardEdit,
weight: ViaWeight,
maybe_pin: Option<String>,
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), Infringement> {
let (weight, dots) = self.layout.add_via(&mut recorder.layout_edit, weight)?;
if let Some(pin) = maybe_pin {
for dot in dots.clone() {
self.pinname_nodes
.insert(pin.clone(), GenericNode::Primitive(dot.into()));
}
}
Ok((weight, dots))
}
/// Adds a new fixed dot with an optional pin name.
///
/// Inserts the dot into the layout and, if a pin name is provided, maps it to the created dot's node.
@ -295,7 +313,7 @@ impl<M: AccessMesadata> Board<M> {
}
recorder
.data_edit
.board_data_edit
.bands
.insert(bandname, (maybe_band, None));
@ -328,7 +346,7 @@ impl<M: AccessMesadata> Board<M> {
}
pub fn apply_edit(&mut self, edit: &BoardEdit) {
for (bandname, (maybe_old_band_uid, ..)) in &edit.data_edit.bands {
for (bandname, (maybe_old_band_uid, ..)) in &edit.board_data_edit.bands {
if maybe_old_band_uid.is_some() {
self.band_bandname.remove_by_right(bandname);
}
@ -336,7 +354,7 @@ impl<M: AccessMesadata> Board<M> {
self.layout_mut().apply(&edit.layout_edit);
for (bandname, (.., maybe_new_band_uid)) in &edit.data_edit.bands {
for (bandname, (.., maybe_new_band_uid)) in &edit.board_data_edit.bands {
if let Some(band_uid) = maybe_new_band_uid {
self.band_bandname.insert(*band_uid, bandname.clone());
}

View File

@ -308,7 +308,7 @@ impl<
pub fn remove_primitive(&mut self, primitive: PI) {
let maybe_removed = self.graph.remove_node(primitive.index().into());
debug_assert!(maybe_removed.is_some());
assert!(maybe_removed.is_some());
}
pub fn move_dot(&mut self, dot: DI, to: Point) {
@ -356,7 +356,7 @@ impl<
.find(|edge| matches!(edge.weight(), GeometryLabel::Outer))
{
let maybe_removed = self.graph.remove_edge(old_inner_edge.id());
debug_assert!(maybe_removed.is_some());
assert!(maybe_removed.is_some());
}
if let Some(new_inner) = maybe_new_inner {
@ -656,7 +656,7 @@ impl<PW: Copy + Retag<Index = PI>, DW, SW, BW, CW: Clone, Cel: Copy, PI: Copy, D
fn remove_compound(&mut self, compound: GenericIndex<CW>) {
let maybe_removed = self.graph.remove_node(compound.index().into());
debug_assert!(maybe_removed.is_some());
assert!(maybe_removed.is_some());
}
fn add_to_compound<I>(&mut self, primitive: I, entry_label: Cel, compound: GenericIndex<CW>)

View File

@ -192,6 +192,8 @@ impl Prenavmesh {
let primitive = node.primitive_ref(layout.drawing());
let Some(primitive_net) = primitive.maybe_net() else {
assert_ne!(node, origin.into());
assert_ne!(node, destination.into());
continue;
};
@ -215,6 +217,10 @@ impl Prenavmesh {
// false positives in some cases, so in the future, instead of this,
// create a fillet compound type and check for compound membership.
if Self::is_fixed_dot_filleted(layout, dot) {
// FIXME: anteroute dot may get skipped here, which
// results in a panic.
assert_ne!(origin, dot);
assert_ne!(destination, dot);
continue;
}