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/anterouter",
"autorouter/autorouter", "autorouter/autorouter",
"autorouter/compare_detours", "autorouter/compare_detours",
"autorouter/compass_direction",
"autorouter/conncomps", "autorouter/conncomps",
"autorouter/execution", "autorouter/execution",
"autorouter/history", "autorouter/history",

View File

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

View File

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