feat(autorouter/presorter): Take into account ratline-pad intersections

This commit is contained in:
Mikolaj Wielgus 2025-09-11 23:25:03 +02:00
parent 1b485e81a6
commit aee8c42720
5 changed files with 92 additions and 41 deletions

View File

@ -168,7 +168,6 @@ impl SpecctraMesadata {
/// associated with a net class. If a net class is found, it retrieves the corresponding rule /// associated with a net class. If a net class is found, it retrieves the corresponding rule
/// from the class rules. If no class is associated, or if the class does not have a defined rule, /// from the class rules. If no class is associated, or if the class does not have a defined rule,
/// it defaults to the general structure rule. /// it defaults to the general structure rule.
///
pub fn get_rule(&self, net: usize) -> &SpecctraRule { pub fn get_rule(&self, net: usize) -> &SpecctraRule {
self.net_netclass self.net_netclass
.get(&net) .get(&net)
@ -179,10 +178,10 @@ impl SpecctraMesadata {
impl AccessRules for SpecctraMesadata { impl AccessRules for SpecctraMesadata {
fn clearance(&self, conditions1: &Conditions<'_>, conditions2: &Conditions<'_>) -> f64 { fn clearance(&self, conditions1: &Conditions<'_>, conditions2: &Conditions<'_>) -> f64 {
let clr1 = self.get_rule(conditions1.net).clearance; let clearance1 = self.get_rule(conditions1.net).clearance;
let clr2 = self.get_rule(conditions2.net).clearance; let clearance2 = self.get_rule(conditions2.net).clearance;
f64::max(clr1, clr2) f64::max(clearance1, clearance2)
} }
fn largest_clearance(&self, _maybe_net: Option<usize>) -> f64 { fn largest_clearance(&self, _maybe_net: Option<usize>) -> f64 {

View File

@ -63,10 +63,10 @@ impl SccIntersectionsAndLengthPresorter {
&& a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) && a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1)
{ {
a_length += ratline.ref_(autorouter).length(); a_length += ratline.ref_(autorouter).length();
a_intersector_count += ratline a_intersector_count +=
.ref_(autorouter) ratline.ref_(autorouter).interiorly_cut_ratlines().count();
.interior_obstacle_ratlines() a_intersector_count +=
.count(); ratline.ref_(autorouter).cut_other_net_primitives().count();
} }
} }
@ -75,10 +75,10 @@ impl SccIntersectionsAndLengthPresorter {
&& b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) && b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1)
{ {
b_length += ratline.ref_(autorouter).length(); b_length += ratline.ref_(autorouter).length();
b_intersector_count += ratline b_intersector_count +=
.ref_(autorouter) ratline.ref_(autorouter).interiorly_cut_ratlines().count();
.interior_obstacle_ratlines() b_intersector_count +=
.count(); ratline.ref_(autorouter).cut_other_net_primitives().count();
} }
} }

View File

@ -7,8 +7,12 @@ use petgraph::graph::{EdgeIndex, NodeIndex};
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::{ use crate::{
drawing::{band::BandTermsegIndex, dot::FixedDotIndex}, drawing::{
geometry::shape::MeasureLength, band::BandTermsegIndex,
dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
},
geometry::{shape::MeasureLength, GetLayer},
graph::MakeRef, graph::MakeRef,
triangulation::GetTrianvertexNodeIndex, triangulation::GetTrianvertexNodeIndex,
}; };
@ -84,16 +88,40 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
(source_dot, target_dot) (source_dot, target_dot)
} }
pub fn closure_obstacle_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ { pub fn layer(&self) -> usize {
self.intersecting_ratlines() self.endpoint_dots()
.0
.primitive(self.autorouter.board().layout().drawing())
.layer()
} }
pub fn interior_obstacle_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ { pub fn net(&self) -> usize {
self.intersecting_ratlines() self.endpoint_dots()
.filter(|index| !self.is_pin_cutter(*index)) .0
.primitive(self.autorouter.board().layout().drawing())
.maybe_net()
.unwrap()
} }
fn intersecting_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ { fn cut_primitives(&self) -> impl Iterator<Item = PrimitiveIndex> + '_ {
self.autorouter
.board()
.layout()
.drawing()
.cut(self.line_segment(), 0.0, self.layer())
}
pub fn cut_other_net_primitives(&self) -> impl Iterator<Item = PrimitiveIndex> + '_ {
self.cut_primitives().filter(|primitive_node| {
primitive_node
.primitive(self.autorouter.board().layout().drawing())
.maybe_net()
.map(|net| net != self.net())
.unwrap_or(true)
})
}
pub fn interiorly_cut_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ {
let self_line_segment = self.line_segment(); let self_line_segment = self.line_segment();
self.autorouter self.autorouter
@ -112,18 +140,6 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.filter(move |other| { .filter(move |other| {
let other_line_segment = other.ref_(self.autorouter).line_segment(); let other_line_segment = other.ref_(self.autorouter).line_segment();
line_intersection(self_line_segment, other_line_segment).is_some()
})
}
fn is_pin_cutter(&self, other: RatlineIndex) -> bool {
// TODO: For now, instead of detecting whether endpoint ratvertex pins
// are cut, we only check if the intersection between self and the
// supposed cutter is not internal.
let self_line_segment = self.line_segment();
let other_line_segment = other.ref_(self.autorouter).line_segment();
if let Some(LineIntersection::SinglePoint { is_proper, .. }) = if let Some(LineIntersection::SinglePoint { is_proper, .. }) =
line_intersection(self_line_segment, other_line_segment) line_intersection(self_line_segment, other_line_segment)
{ {
@ -133,6 +149,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
} else { } else {
false false
} }
})
} }
pub fn line_segment(&self) -> Line { pub fn line_segment(&self) -> Line {

View File

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use geo::Line;
use petgraph::visit::Walker; use petgraph::visit::Walker;
use specctra_core::rules::GetConditions; use specctra_core::rules::GetConditions;
@ -11,7 +12,10 @@ use crate::{
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
Collision, Infringement, Collision, Infringement,
}, },
geometry::{primitive::AccessPrimitiveShape, GenericNode, GetLayer}, geometry::{
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
GenericNode, GetLayer,
},
graph::GenericIndex, graph::GenericIndex,
}; };
@ -112,6 +116,36 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
v v
} }
pub fn cut(
&self,
line: Line,
width: f64,
layer: usize,
) -> impl Iterator<Item = PrimitiveIndex> + '_ {
let limiting_shape = PrimitiveShape::Seg(SegShape {
from: line.start_point(),
to: line.end_point(),
width,
})
.inflate(self.rules().largest_clearance(None));
self.recording_geometry_with_rtree()
.rtree()
.locate_in_envelope_intersecting(&limiting_shape.envelope_3d(width, layer))
.filter_map(|wrapper| {
if let GenericNode::Primitive(primitive_node) = wrapper.data {
Some(primitive_node)
} else {
None
}
})
.filter_map(move |primitive_node| {
limiting_shape
.intersects(&primitive_node.primitive(self).shape())
.then_some(primitive_node)
})
}
pub(super) fn find_infringement_except<'a>( pub(super) fn find_infringement_except<'a>(
&'a self, &'a self,
infringer: PrimitiveIndex, infringer: PrimitiveIndex,

View File

@ -38,6 +38,7 @@ pub trait AccessPrimitiveShape: AccessShape + GetWidth {
[ [
envelope.upper()[0], envelope.upper()[0],
envelope.upper()[1], envelope.upper()[1],
// XXX: Why isn't floating point infinity used here?
(layer_count - 1) as f64, (layer_count - 1) as f64,
], ],
) )