mirror of https://codeberg.org/topola/topola.git
feat(autorouter/anterouter): Fanout only in cardinal directions
This works around the falsely positively fillet exclusions our current navmesh creation method causes.
This commit is contained in:
parent
d0417926f5
commit
2ebd7ce82d
|
|
@ -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::<PolyWeight>::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()],
|
||||
|
|
|
|||
|
|
@ -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<CardinalDirection> 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<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 From<PrincipalWind> 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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>) -> &CW {
|
||||
|
|
|
|||
|
|
@ -347,6 +347,19 @@ impl<R: AccessRules> Layout<R> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn primitive_poly(&self, primitive: PrimitiveIndex) -> Option<GenericIndex<PolyWeight>> {
|
||||
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::<PolyWeight>::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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue