From 1bbb068af920ea5145e6a0fe65334ceaa10296c1 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Thu, 2 Oct 2025 17:05:50 +0200 Subject: [PATCH] feat(autorouter/compass_direction): Anteroute fanout vias in eight "compass" directions --- committed.toml | 1 + src/autorouter/anterouter.rs | 151 +++++++++++++++++++++------- src/autorouter/compass_direction.rs | 94 +++++++++++++++++ src/autorouter/mod.rs | 1 + src/autorouter/planner.rs | 2 +- 5 files changed, 209 insertions(+), 40 deletions(-) create mode 100644 src/autorouter/compass_direction.rs diff --git a/committed.toml b/committed.toml index 0b9e7a6..f6b6cc7 100644 --- a/committed.toml +++ b/committed.toml @@ -21,6 +21,7 @@ allowed_scopes = [ "autorouter/anterouter", "autorouter/autorouter", "autorouter/compare_detours", + "autorouter/compass_direction", "autorouter/conncomps", "autorouter/execution", "autorouter/history", diff --git a/src/autorouter/anterouter.rs b/src/autorouter/anterouter.rs index ccced61..35e288f 100644 --- a/src/autorouter/anterouter.rs +++ b/src/autorouter/anterouter.rs @@ -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, ratvertex: NodeIndex, - dot: FixedDotIndex, - to_layer: usize, - endpoint_dot_bbox_to_anchor: [f64; 2], + ratline: EdgeIndex, + 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, + ratvertex: NodeIndex, + 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, ratvertex: NodeIndex, 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(..)) diff --git a/src/autorouter/compass_direction.rs b/src/autorouter/compass_direction.rs new file mode 100644 index 0000000..568e813 --- /dev/null +++ b/src/autorouter/compass_direction.rs @@ -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 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, + } + } +} diff --git a/src/autorouter/mod.rs b/src/autorouter/mod.rs index 62d6ee3..4cfa123 100644 --- a/src/autorouter/mod.rs +++ b/src/autorouter/mod.rs @@ -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; diff --git a/src/autorouter/planner.rs b/src/autorouter/planner.rs index 9aef925..c34982d 100644 --- a/src/autorouter/planner.rs +++ b/src/autorouter/planner.rs @@ -99,7 +99,7 @@ impl Planner { None } }) - .map_or(TerminatingScheme::Anteroute([-2.0, 0.0]), |dot| { + .map_or(TerminatingScheme::Fanout, |dot| { TerminatingScheme::ExistingFixedDot(dot) }), )