feat(autorouter/anterouter): Don't anteroute fanout vias inside polys

This commit is contained in:
Mikolaj Wielgus 2025-10-06 13:09:53 +02:00
parent 901d6010c2
commit 8095c3a89a
6 changed files with 110 additions and 22 deletions

View File

@ -70,6 +70,7 @@ allowed_scopes = [
"layout/collect_bands",
"layout/layout",
"layout/poly",
"layout/query",
"layout/via",
"math/circle",
"math/cyclic_search",

View File

@ -16,9 +16,9 @@ use crate::{
permutator::PlanarAutorouteExecutionPermutator, planner::Planner,
},
board::{AccessMesadata, Board},
drawing::{band::BandTermsegIndex, Infringement},
drawing::band::BandTermsegIndex,
graph::MakeRef,
layout::{via::ViaWeight, LayoutEdit},
layout::{via::ViaWeight, LayoutEdit, LayoutException},
router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions},
triangulation::GetTrianvertexNodeIndex,
};
@ -59,7 +59,7 @@ pub enum AutorouterError {
#[error("TopoNavmesh generation failed: {0}")]
TopoNavmeshGeneration(#[from] ng::NavmeshCalculationError),
#[error("could not place via")]
CouldNotPlaceVia(#[from] Infringement),
CouldNotPlaceVia(#[from] LayoutException),
#[error("could not remove band")]
CouldNotRemoveBand(BandTermsegIndex),
#[error("need exactly two ratlines")]

View File

@ -21,11 +21,13 @@ use crate::{
dot::{FixedDotIndex, FixedDotWeight},
graph::PrimitiveIndex,
seg::{FixedSegIndex, FixedSegWeight},
DrawingException, Infringement,
DrawingException,
},
geometry::{edit::ApplyGeometryEdit, GenericNode, GetLayer},
graph::{GenericIndex, MakeRef},
layout::{poly::PolyWeight, via::ViaWeight, CompoundWeight, Layout, NodeIndex},
layout::{
poly::PolyWeight, via::ViaWeight, CompoundWeight, Layout, LayoutException, NodeIndex,
},
router::ng::EtchedPath,
};
@ -110,7 +112,7 @@ impl<M: AccessMesadata> Board<M> {
recorder: &mut BoardEdit,
weight: ViaWeight,
maybe_pin: Option<String>,
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), Infringement> {
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), LayoutException> {
let (weight, dots) = self.layout.add_via(&mut recorder.layout_edit, weight)?;
if let Some(pin) = maybe_pin {

View File

@ -5,8 +5,9 @@
use contracts_try::debug_ensures;
use derive_getters::Getters;
use enum_dispatch::enum_dispatch;
use geo::Point;
use geo::{Point, Polygon};
use rstar::AABB;
use thiserror::Error;
use crate::{
drawing::{
@ -40,13 +41,23 @@ use crate::{
math::RotationSense,
};
/// Represents a weight for various compounds
#[derive(Clone, Debug, Error)]
pub enum LayoutException {
#[error(transparent)]
HasPointInPoly(#[from] HasPointInPoly),
#[error(transparent)]
Infringement(#[from] Infringement),
}
#[derive(Clone, Debug, Error)]
#[error("(0:?) has point in {1:?}")]
pub struct HasPointInPoly(pub Polygon, pub Point);
/// Represents a weight for various compounds.
#[derive(Clone, Copy, Debug)]
#[enum_dispatch(GetMaybeNet, IsInLayer)]
pub enum CompoundWeight {
/// Represents the weight of a polygon compound, includes its basic [`Layout`] information
Poly(PolyWeight),
/// Represents Via weight properties, containing its [`Layout`] properties
Via(ViaWeight),
}
@ -57,12 +68,12 @@ pub enum CompoundEntryLabel {
Fillet,
}
/// The alias to differ node types
/// The alias to differentiate node types.
pub type NodeIndex = GenericNode<PrimitiveIndex, GenericIndex<CompoundWeight>>;
pub type LayoutEdit = DrawingEdit<CompoundWeight, CompoundEntryLabel>;
#[derive(Clone, Debug, Getters)]
/// Structure for managing the Layout design
/// Structure for managing the Layout design.
pub struct Layout<R> {
pub(super) drawing: Drawing<CompoundWeight, CompoundEntryLabel, R>,
}
@ -144,7 +155,7 @@ impl<R: AccessRules> Layout<R> {
&mut self,
recorder: &mut LayoutEdit,
weight: ViaWeight,
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), Infringement> {
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), LayoutException> {
let compound = self.drawing.add_compound(recorder, weight.into());
let mut dots = vec![];
@ -158,23 +169,34 @@ impl<R: AccessRules> Layout<R> {
}),
) {
Ok(dot) => {
dots.push(dot);
let maybe_enclosing_poly = self
.polys_enclosing_point_on_layers(weight.circle.pos, layer)
.next();
if let Some(enclosing_poly) = maybe_enclosing_poly {
// If a via is inside poly, it may not necessarily
// trigger an infringement on its primitives. To take
// this situation into account, we also check if the
// via's center is inside the poly's polygon.
self.remove_failed_via(recorder, compound, dots);
return Err(LayoutException::HasPointInPoly(HasPointInPoly(
enclosing_poly.ref_(self).shape(),
weight.circle.pos,
)));
}
self.drawing.add_to_compound(
recorder,
dot,
CompoundEntryLabel::Normal,
compound,
);
dots.push(dot);
}
Err(err) => {
// Remove inserted dots.
self.drawing.remove_compound(recorder, compound);
for dot in dots.iter().rev() {
self.drawing.remove_fixed_dot(recorder, *dot);
}
return Err(err);
self.remove_failed_via(recorder, compound, dots);
return Err(err.into());
}
}
}
@ -182,6 +204,20 @@ impl<R: AccessRules> Layout<R> {
Ok((GenericIndex::<ViaWeight>::new(compound.index()), dots))
}
fn remove_failed_via(
&mut self,
recorder: &mut LayoutEdit,
compound: GenericIndex<CompoundWeight>,
dots: Vec<FixedDotIndex>,
) {
self.drawing.remove_compound(recorder, compound);
// Remove inserted dots.
for dot in dots.iter().rev() {
self.drawing.remove_fixed_dot(recorder, *dot);
}
}
pub fn add_fixed_dot(
&mut self,
recorder: &mut LayoutEdit,

View File

@ -7,6 +7,7 @@
mod collect_bands;
mod layout;
pub mod poly;
pub mod query;
pub mod via;
pub use layout::*;

48
src/layout/query.rs Normal file
View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use geo::Point;
use rstar::AABB;
use specctra_core::rules::AccessRules;
use crate::{
drawing::graph::IsInLayer,
geometry::{shape::AccessShape, GenericNode},
graph::{GenericIndex, GetIndex},
layout::{poly::PolyWeight, CompoundWeight, Layout},
};
impl<R: AccessRules> Layout<R> {
pub fn polys_enclosing_point_on_layers(
&self,
point: Point,
layer: usize,
) -> impl Iterator<Item = GenericIndex<PolyWeight>> + '_ {
self.drawing()
.rtree()
.locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners(
[point.x(), point.y(), -f64::INFINITY],
[point.x(), point.y(), f64::INFINITY],
))
.filter_map(move |geom| {
let node = geom.data;
let GenericNode::Compound(compound) = node else {
return None;
};
let CompoundWeight::Poly(_) = self.drawing.compound_weight(compound) else {
return None;
};
if !self.drawing.compound_weight(compound).is_in_layer(layer)
|| !self.node_shape(node).contains_point(point)
{
return None;
}
Some(GenericIndex::<PolyWeight>::new(compound.index()))
})
}
}