feat(autorouter/compass_direction): Anteroute fanout vias in eight "compass" directions

This commit is contained in:
Mikolaj Wielgus 2025-10-02 17:05:50 +02:00
parent 71feedc6ce
commit 1bbb068af9
5 changed files with 209 additions and 40 deletions

View File

@ -21,6 +21,7 @@ allowed_scopes = [
"autorouter/anterouter",
"autorouter/autorouter",
"autorouter/compare_detours",
"autorouter/compass_direction",
"autorouter/conncomps",
"autorouter/execution",
"autorouter/history",

View File

@ -4,13 +4,13 @@
use std::collections::BTreeMap;
use geo::point;
use petgraph::graph::NodeIndex;
use geo::{point, Point};
use petgraph::graph::{EdgeIndex, NodeIndex};
use rstar::{Envelope, RTreeObject, AABB};
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{ratline::RatlineIndex, Autorouter},
autorouter::{compass_direction::CompassDirection8, ratline::RatlineIndex, Autorouter},
board::edit::BoardEdit,
drawing::{
dot::FixedDotIndex,
@ -30,7 +30,7 @@ use crate::{
#[derive(Clone, Copy, Debug)]
pub enum TerminatingScheme {
ExistingFixedDot(FixedDotIndex),
Anteroute([f64; 2]),
Fanout,
}
#[derive(Clone, Debug)]
@ -71,14 +71,13 @@ impl Anterouter {
*terminating_dot,
)
}
TerminatingScheme::Anteroute(pin_bbox_to_anchor) => self
.anteroute_dot_to_anchor(
autorouter,
endpoint_indices.0,
endpoint_dots.0,
*layer,
*pin_bbox_to_anchor,
),
TerminatingScheme::Fanout => self.anteroute_fanout(
autorouter,
endpoint_indices.0,
*ratline,
endpoint_dots.0,
*layer,
),
}
}
@ -95,45 +94,116 @@ impl Anterouter {
*terminating_dot,
)
}
TerminatingScheme::Anteroute(pin_bbox_to_anchor) => self
.anteroute_dot_to_anchor(
autorouter,
endpoint_indices.1,
endpoint_dots.1,
*layer,
*pin_bbox_to_anchor,
),
TerminatingScheme::Fanout => self.anteroute_fanout(
autorouter,
endpoint_indices.1,
*ratline,
endpoint_dots.1,
*layer,
),
}
}
}
}
fn anteroute_dot_to_anchor(
fn anteroute_fanout(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
dot: FixedDotIndex,
to_layer: usize,
endpoint_dot_bbox_to_anchor: [f64; 2],
ratline: EdgeIndex<usize>,
source_dot: FixedDotIndex,
target_layer: usize,
) {
self.place_assignment_via_on_anchor(
let mut ratline_delta: Point = ratline.ref_(autorouter).line_segment().delta().into();
if ratvertex == ratline.ref_(autorouter).endpoint_indices().1 {
ratline_delta = -ratline_delta;
}
let initial_compass_direction8 = CompassDirection8::nearest_to_vector(ratline_delta);
if self
.anteroute_fanout_to_anchor(
autorouter,
ratvertex,
source_dot,
target_layer,
Point::from(initial_compass_direction8) * 1.2,
)
.is_ok()
{
return;
}
let mut counterclockwise_turning = initial_compass_direction8;
let mut clockwise_turning = initial_compass_direction8;
loop {
counterclockwise_turning = counterclockwise_turning.turn_counterclockwise();
if self
.anteroute_fanout_to_anchor(
autorouter,
ratvertex,
source_dot,
target_layer,
Point::from(counterclockwise_turning) * 1.2,
)
.is_ok()
{
return;
}
clockwise_turning = clockwise_turning.turn_clockwise();
if self
.anteroute_fanout_to_anchor(
autorouter,
ratvertex,
source_dot,
target_layer,
Point::from(clockwise_turning) * 1.2,
)
.is_ok()
{
return;
}
if counterclockwise_turning == initial_compass_direction8
|| clockwise_turning == initial_compass_direction8
{
break;
//panic!();
}
}
}
fn anteroute_fanout_to_anchor(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
source_dot: FixedDotIndex,
target_layer: usize,
bbox_to_anchor: Point,
) -> Result<(), ()> {
self.place_fanout_via_on_anchor(
autorouter,
ratvertex,
dot,
to_layer,
endpoint_dot_bbox_to_anchor,
source_dot,
target_layer,
bbox_to_anchor,
)
}
fn place_assignment_via_on_anchor(
fn place_fanout_via_on_anchor(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
dot: FixedDotIndex,
to_layer: usize,
endpoint_dot_bbox_to_anchor: [f64; 2],
) {
let pin_layer = autorouter.board().layout().drawing().primitive(dot).layer();
target_layer: usize,
endpoint_dot_bbox_to_anchor: Point,
) -> Result<(), ()> {
let source_layer = autorouter.board().layout().drawing().primitive(dot).layer();
let pin_maybe_net = autorouter
.board()
.layout()
@ -177,8 +247,8 @@ impl Anterouter {
//let pin_bbox_anchor = pin_bbox.center() + (pin_bbox.upper() - pin_bbox.lower()) * pin_bbox_to_anchor;
let pin_bbox_anchor = point! {
x: pin_bbox.center()[0] + (pin_bbox.upper()[0] - pin_bbox.lower()[0]) / 2.0 * endpoint_dot_bbox_to_anchor[0],
y: pin_bbox.center()[1] + (pin_bbox.upper()[1] - pin_bbox.lower()[1]) / 2.0 * endpoint_dot_bbox_to_anchor[1],
x: pin_bbox.center()[0] + (pin_bbox.upper()[0] - pin_bbox.lower()[0]) / 2.0 * endpoint_dot_bbox_to_anchor.x(),
y: pin_bbox.center()[1] + (pin_bbox.upper()[1] - pin_bbox.lower()[1]) / 2.0 * endpoint_dot_bbox_to_anchor.y(),
};
//let via_bbox_to_anchor = [-pin_bbox_to_anchor[0], -pin_bbox_to_anchor[1]];
@ -188,8 +258,8 @@ impl Anterouter {
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),
from_layer: std::cmp::min(source_layer, target_layer),
to_layer: std::cmp::max(source_layer, target_layer),
circle: Circle {
pos: pin_bbox_anchor,
r: 100.0,
@ -204,7 +274,7 @@ impl Anterouter {
let terminating_dot = dots
.iter()
.find(|dot| {
to_layer
target_layer
== dot
.primitive_ref(autorouter.board().layout().drawing())
.layer()
@ -212,9 +282,12 @@ impl Anterouter {
.unwrap();
autorouter.ratsnest.assign_terminating_dot_to_ratvertex(
ratvertex,
to_layer,
target_layer,
*terminating_dot,
);
Ok(())
} else {
Err(())
}
/*let bbox = if let Some(poly) = autorouter.board().layout().drawing().geometry().compounds(dot).find(|(_, compound_weight)| {
matches!(compound_weight, CompoundWeight::Poly(..))

View File

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use geo::Point;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CompassDirection8 {
North,
NorthWest,
West,
SouthWest,
South,
SouthEast,
East,
NorthEast,
}
impl From<CompassDirection8> for Point {
fn from(compass_direction8: CompassDirection8) -> Point {
match compass_direction8 {
CompassDirection8::North => [0.0, -1.0].into(),
CompassDirection8::NorthWest => [-1.0, -1.0].into(),
CompassDirection8::West => [-1.0, 0.0].into(),
CompassDirection8::SouthWest => [-1.0, 1.0].into(),
CompassDirection8::South => [0.0, 1.0].into(),
CompassDirection8::SouthEast => [1.0, 1.0].into(),
CompassDirection8::East => [1.0, 0.0].into(),
CompassDirection8::NorthEast => [1.0, -1.0].into(),
}
}
}
impl CompassDirection8 {
pub fn nearest_to_vector(vector: Point) -> Self {
/*match (vector.x().signum(), vector.y().signum()) {
(0.0, -1.0) => Self::North,
(-1.0, -1.0) => Self::NorthWest,
(-1.0, 0.0) => Self::West,
(-1.0, 1.0) => Self::SouthWest,
(0.0, 1.0) => Self::South,
(1.0, 1.0) => Self::SouthEast,
(1.0, 0.0) => Self::East,
(1.0, -1.0) => Self::NorthEast,
(_, _) => panic!(),
}*/
if vector.x() == 0.0 && vector.y() == 0.0 {
panic!("Zero vector has no direction");
}
let angle = vector.y().atan2(vector.x()); // atan2 gives angle in radians from -π to π
let angle_deg = angle.to_degrees(); // Convert to degrees for easier reasoning
let compass_angle = (450.0 - angle_deg) % 360.0; // Convert to compass heading (0° = North)
// Each direction is 45 degrees wide; round to nearest
match ((compass_angle + 22.5) / 45.0).floor() as i32 % 8 {
0 => CompassDirection8::North,
1 => CompassDirection8::NorthEast,
2 => CompassDirection8::East,
3 => CompassDirection8::SouthEast,
4 => CompassDirection8::South,
5 => CompassDirection8::SouthWest,
6 => CompassDirection8::West,
7 => CompassDirection8::NorthWest,
_ => unreachable!(),
}
}
pub fn turn_counterclockwise(self) -> Self {
match self {
Self::North => Self::NorthWest,
Self::NorthWest => Self::West,
Self::West => Self::SouthWest,
Self::SouthWest => Self::South,
Self::South => Self::SouthEast,
Self::SouthEast => Self::East,
Self::East => Self::NorthEast,
Self::NorthEast => Self::North,
}
}
pub fn turn_clockwise(self) -> Self {
match self {
Self::North => Self::NorthEast,
Self::NorthEast => Self::East,
Self::East => Self::SouthEast,
Self::SouthEast => Self::South,
Self::South => Self::SouthWest,
Self::SouthWest => Self::West,
Self::West => Self::NorthWest,
Self::NorthWest => Self::North,
}
}
}

View File

@ -5,6 +5,7 @@
pub mod anterouter;
mod autorouter;
pub mod compare_detours;
pub mod compass_direction;
pub mod conncomps;
pub mod execution;
pub mod history;

View File

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