From 2ebd7ce82d09df6b1f205ffe3829b9c55bc4086b Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Fri, 3 Oct 2025 13:09:21 +0200 Subject: [PATCH] feat(autorouter/anterouter): Fanout only in cardinal directions This works around the falsely positively fillet exclusions our current navmesh creation method causes. --- src/autorouter/anterouter.rs | 46 +++-------- src/autorouter/compass_direction.rs | 118 +++++++++++++++++++--------- src/geometry/with_rtree.rs | 4 +- src/layout/layout.rs | 13 +++ 4 files changed, 109 insertions(+), 72 deletions(-) diff --git a/src/autorouter/anterouter.rs b/src/autorouter/anterouter.rs index 35e288f..11dda98 100644 --- a/src/autorouter/anterouter.rs +++ b/src/autorouter/anterouter.rs @@ -10,7 +10,7 @@ use rstar::{Envelope, RTreeObject, AABB}; use specctra_core::mesadata::AccessMesadata; use crate::{ - autorouter::{compass_direction::CompassDirection8, ratline::RatlineIndex, Autorouter}, + autorouter::{compass_direction::CardinalDirection, ratline::RatlineIndex, Autorouter}, board::edit::BoardEdit, drawing::{ dot::FixedDotIndex, @@ -18,12 +18,8 @@ use crate::{ primitive::MakePrimitiveShape, }, geometry::{GenericNode, GetLayer}, - graph::{GenericIndex, GetIndex, MakeRef}, - layout::{ - poly::{MakePolygon, PolyWeight}, - via::ViaWeight, - CompoundWeight, - }, + graph::MakeRef, + layout::{poly::MakePolygon, via::ViaWeight}, math::Circle, }; @@ -120,7 +116,7 @@ impl Anterouter { ratline_delta = -ratline_delta; } - let initial_compass_direction8 = CompassDirection8::nearest_to_vector(ratline_delta); + let cardinal_direction = CardinalDirection::nearest_to_vector(ratline_delta); if self .anteroute_fanout_to_anchor( @@ -128,15 +124,15 @@ impl Anterouter { ratvertex, source_dot, target_layer, - Point::from(initial_compass_direction8) * 1.2, + Point::from(cardinal_direction) * 1.4, ) .is_ok() { return; } - let mut counterclockwise_turning = initial_compass_direction8; - let mut clockwise_turning = initial_compass_direction8; + let mut counterclockwise_turning = cardinal_direction; + let mut clockwise_turning = cardinal_direction; loop { counterclockwise_turning = counterclockwise_turning.turn_counterclockwise(); @@ -147,7 +143,7 @@ impl Anterouter { ratvertex, source_dot, target_layer, - Point::from(counterclockwise_turning) * 1.2, + Point::from(counterclockwise_turning) * 1.4, ) .is_ok() { @@ -162,15 +158,15 @@ impl Anterouter { ratvertex, source_dot, target_layer, - Point::from(clockwise_turning) * 1.2, + Point::from(clockwise_turning) * 1.4, ) .is_ok() { return; } - if counterclockwise_turning == initial_compass_direction8 - || clockwise_turning == initial_compass_direction8 + if counterclockwise_turning == cardinal_direction + || clockwise_turning == cardinal_direction { break; //panic!(); @@ -211,25 +207,7 @@ impl Anterouter { .primitive(dot) .maybe_net(); - let pin_bbox = if let Some(poly) = autorouter - .board() - .layout() - .drawing() - .compounds(GenericIndex::<()>::new(dot.index())) - .find_map(|(_, compound)| { - if let CompoundWeight::Poly(_) = autorouter - .board() - .layout() - .drawing() - .compound_weight(compound) - { - Some(compound) - } else { - None - } - }) - .map(|compound| GenericIndex::::new(compound.index())) - { + let pin_bbox = if let Some(poly) = autorouter.board().layout().primitive_poly(dot.into()) { let bbox = poly.ref_(autorouter.board().layout()).shape().envelope(); AABB::<[f64; 2]>::from_corners( [bbox.lower().x(), bbox.lower().y()], diff --git a/src/autorouter/compass_direction.rs b/src/autorouter/compass_direction.rs index 568e813..81c29e5 100644 --- a/src/autorouter/compass_direction.rs +++ b/src/autorouter/compass_direction.rs @@ -1,11 +1,66 @@ // SPDX-FileCopyrightText: 2025 Topola contributors // -// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-License-Identifier: MIT use geo::Point; #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum CompassDirection8 { +pub enum CardinalDirection { + North, + West, + South, + East, +} + +impl From for Point { + fn from(compass_direction: CardinalDirection) -> Point { + match compass_direction { + CardinalDirection::North => [0.0, -1.0].into(), + CardinalDirection::West => [-1.0, 0.0].into(), + CardinalDirection::South => [0.0, 1.0].into(), + CardinalDirection::East => [1.0, 0.0].into(), + } + } +} + +impl CardinalDirection { + pub fn nearest_to_vector(vector: Point) -> Self { + if vector.x().abs() > vector.y().abs() { + if vector.x() > 0.0 { + Self::East + } else { + Self::West + } + } else { + if vector.y() > 0.0 { + Self::North + } else { + Self::South + } + } + } + + pub fn turn_counterclockwise(self) -> Self { + match self { + Self::North => Self::West, + Self::West => Self::South, + Self::South => Self::East, + Self::East => Self::North, + } + } + + pub fn turn_clockwise(self) -> Self { + match self { + Self::North => Self::East, + Self::East => Self::South, + Self::South => Self::West, + Self::West => Self::North, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PrincipalWind { North, NorthWest, West, @@ -16,52 +71,41 @@ pub enum CompassDirection8 { 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 From for Point { + fn from(principal_wind: PrincipalWind) -> Point { + match principal_wind { + PrincipalWind::North => [0.0, -1.0].into(), + PrincipalWind::NorthWest => [-1.0, -1.0].into(), + PrincipalWind::West => [-1.0, 0.0].into(), + PrincipalWind::SouthWest => [-1.0, 1.0].into(), + PrincipalWind::South => [0.0, 1.0].into(), + PrincipalWind::SouthEast => [1.0, 1.0].into(), + PrincipalWind::East => [1.0, 0.0].into(), + PrincipalWind::NorthEast => [1.0, -1.0].into(), } } } -impl CompassDirection8 { +impl PrincipalWind { 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) + let angle = vector.y().atan2(vector.x()); // atan2 gives angle in radians from -pi to pi. + 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 deg = North). - // Each direction is 45 degrees wide; round to nearest + // 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, + 0 => PrincipalWind::North, + 1 => PrincipalWind::NorthEast, + 2 => PrincipalWind::East, + 3 => PrincipalWind::SouthEast, + 4 => PrincipalWind::South, + 5 => PrincipalWind::SouthWest, + 6 => PrincipalWind::West, + 7 => PrincipalWind::NorthWest, _ => unreachable!(), } } diff --git a/src/geometry/with_rtree.rs b/src/geometry/with_rtree.rs index bcf060d..928dc93 100644 --- a/src/geometry/with_rtree.rs +++ b/src/geometry/with_rtree.rs @@ -465,7 +465,9 @@ impl< where I: Copy + GetIndex, { - self.geometry.add_to_compound(primitive, label, compound); + todo!(); + // This is incorrect: R-tree bbox is not updated. + //self.geometry.add_to_compound(primitive, label, compound); } fn compound_weight(&self, compound: GenericIndex) -> &CW { diff --git a/src/layout/layout.rs b/src/layout/layout.rs index 010dcff..f6a6f01 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -347,6 +347,19 @@ impl Layout { } } + pub fn primitive_poly(&self, primitive: PrimitiveIndex) -> Option> { + self.drawing() + .compounds(GenericIndex::<()>::new(primitive.index())) + .find_map(|(_, compound)| { + if let CompoundWeight::Poly(_) = self.drawing().compound_weight(compound) { + Some(compound) + } else { + None + } + }) + .map(|compound| GenericIndex::::new(compound.index())) + } + /// Checks if a node is not a primitive part of a compound, and if yes, returns its apex and center pub fn apex_of_compoundless_node( &self,