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
/// 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.
///
pub fn get_rule(&self, net: usize) -> &SpecctraRule {
self.net_netclass
.get(&net)
@ -179,10 +178,10 @@ impl SpecctraMesadata {
impl AccessRules for SpecctraMesadata {
fn clearance(&self, conditions1: &Conditions<'_>, conditions2: &Conditions<'_>) -> f64 {
let clr1 = self.get_rule(conditions1.net).clearance;
let clr2 = self.get_rule(conditions2.net).clearance;
let clearance1 = self.get_rule(conditions1.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 {

View File

@ -63,10 +63,10 @@ impl SccIntersectionsAndLengthPresorter {
&& a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1)
{
a_length += ratline.ref_(autorouter).length();
a_intersector_count += ratline
.ref_(autorouter)
.interior_obstacle_ratlines()
.count();
a_intersector_count +=
ratline.ref_(autorouter).interiorly_cut_ratlines().count();
a_intersector_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_length += ratline.ref_(autorouter).length();
b_intersector_count += ratline
.ref_(autorouter)
.interior_obstacle_ratlines()
.count();
b_intersector_count +=
ratline.ref_(autorouter).interiorly_cut_ratlines().count();
b_intersector_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 crate::{
drawing::{band::BandTermsegIndex, dot::FixedDotIndex},
geometry::shape::MeasureLength,
drawing::{
band::BandTermsegIndex,
dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
},
geometry::{shape::MeasureLength, GetLayer},
graph::MakeRef,
triangulation::GetTrianvertexNodeIndex,
};
@ -84,16 +88,40 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
(source_dot, target_dot)
}
pub fn closure_obstacle_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ {
self.intersecting_ratlines()
pub fn layer(&self) -> usize {
self.endpoint_dots()
.0
.primitive(self.autorouter.board().layout().drawing())
.layer()
}
pub fn interior_obstacle_ratlines(&self) -> impl Iterator<Item = RatlineIndex> + '_ {
self.intersecting_ratlines()
.filter(|index| !self.is_pin_cutter(*index))
pub fn net(&self) -> usize {
self.endpoint_dots()
.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();
self.autorouter
@ -112,29 +140,18 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.filter(move |other| {
let other_line_segment = other.ref_(self.autorouter).line_segment();
line_intersection(self_line_segment, other_line_segment).is_some()
if let Some(LineIntersection::SinglePoint { is_proper, .. }) =
line_intersection(self_line_segment, other_line_segment)
{
// It would make more sense to check for non-internality only in
// self, but this gives me the result I want too for now.
!is_proper
} else {
false
}
})
}
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, .. }) =
line_intersection(self_line_segment, other_line_segment)
{
// It would make more sense to check for non-internality only in
// self, but this gives me the result I want too for now.
!is_proper
} else {
false
}
}
pub fn line_segment(&self) -> Line {
let (source, target) = self.endpoint_indices();
let source_pos = self

View File

@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: MIT
use geo::Line;
use petgraph::visit::Walker;
use specctra_core::rules::GetConditions;
@ -11,7 +12,10 @@ use crate::{
primitive::MakePrimitiveShape,
Collision, Infringement,
},
geometry::{primitive::AccessPrimitiveShape, GenericNode, GetLayer},
geometry::{
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
GenericNode, GetLayer,
},
graph::GenericIndex,
};
@ -112,6 +116,36 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
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>(
&'a self,
infringer: PrimitiveIndex,

View File

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