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 specctra_core::mesadata::AccessMesadata;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autorouter::{compass_direction::CompassDirection8, ratline::RatlineIndex, Autorouter},
|
autorouter::{compass_direction::CardinalDirection, ratline::RatlineIndex, Autorouter},
|
||||||
board::edit::BoardEdit,
|
board::edit::BoardEdit,
|
||||||
drawing::{
|
drawing::{
|
||||||
dot::FixedDotIndex,
|
dot::FixedDotIndex,
|
||||||
|
|
@ -18,12 +18,8 @@ use crate::{
|
||||||
primitive::MakePrimitiveShape,
|
primitive::MakePrimitiveShape,
|
||||||
},
|
},
|
||||||
geometry::{GenericNode, GetLayer},
|
geometry::{GenericNode, GetLayer},
|
||||||
graph::{GenericIndex, GetIndex, MakeRef},
|
graph::MakeRef,
|
||||||
layout::{
|
layout::{poly::MakePolygon, via::ViaWeight},
|
||||||
poly::{MakePolygon, PolyWeight},
|
|
||||||
via::ViaWeight,
|
|
||||||
CompoundWeight,
|
|
||||||
},
|
|
||||||
math::Circle,
|
math::Circle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -120,7 +116,7 @@ impl Anterouter {
|
||||||
ratline_delta = -ratline_delta;
|
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
|
if self
|
||||||
.anteroute_fanout_to_anchor(
|
.anteroute_fanout_to_anchor(
|
||||||
|
|
@ -128,15 +124,15 @@ impl Anterouter {
|
||||||
ratvertex,
|
ratvertex,
|
||||||
source_dot,
|
source_dot,
|
||||||
target_layer,
|
target_layer,
|
||||||
Point::from(initial_compass_direction8) * 1.2,
|
Point::from(cardinal_direction) * 1.4,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut counterclockwise_turning = initial_compass_direction8;
|
let mut counterclockwise_turning = cardinal_direction;
|
||||||
let mut clockwise_turning = initial_compass_direction8;
|
let mut clockwise_turning = cardinal_direction;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
counterclockwise_turning = counterclockwise_turning.turn_counterclockwise();
|
counterclockwise_turning = counterclockwise_turning.turn_counterclockwise();
|
||||||
|
|
@ -147,7 +143,7 @@ impl Anterouter {
|
||||||
ratvertex,
|
ratvertex,
|
||||||
source_dot,
|
source_dot,
|
||||||
target_layer,
|
target_layer,
|
||||||
Point::from(counterclockwise_turning) * 1.2,
|
Point::from(counterclockwise_turning) * 1.4,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
|
|
@ -162,15 +158,15 @@ impl Anterouter {
|
||||||
ratvertex,
|
ratvertex,
|
||||||
source_dot,
|
source_dot,
|
||||||
target_layer,
|
target_layer,
|
||||||
Point::from(clockwise_turning) * 1.2,
|
Point::from(clockwise_turning) * 1.4,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if counterclockwise_turning == initial_compass_direction8
|
if counterclockwise_turning == cardinal_direction
|
||||||
|| clockwise_turning == initial_compass_direction8
|
|| clockwise_turning == cardinal_direction
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
//panic!();
|
//panic!();
|
||||||
|
|
@ -211,25 +207,7 @@ impl Anterouter {
|
||||||
.primitive(dot)
|
.primitive(dot)
|
||||||
.maybe_net();
|
.maybe_net();
|
||||||
|
|
||||||
let pin_bbox = if let Some(poly) = autorouter
|
let pin_bbox = if let Some(poly) = autorouter.board().layout().primitive_poly(dot.into()) {
|
||||||
.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 bbox = poly.ref_(autorouter.board().layout()).shape().envelope();
|
let bbox = poly.ref_(autorouter.board().layout()).shape().envelope();
|
||||||
AABB::<[f64; 2]>::from_corners(
|
AABB::<[f64; 2]>::from_corners(
|
||||||
[bbox.lower().x(), bbox.lower().y()],
|
[bbox.lower().x(), bbox.lower().y()],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,66 @@
|
||||||
// SPDX-FileCopyrightText: 2025 Topola contributors
|
// SPDX-FileCopyrightText: 2025 Topola contributors
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[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,
|
North,
|
||||||
NorthWest,
|
NorthWest,
|
||||||
West,
|
West,
|
||||||
|
|
@ -16,52 +71,41 @@ pub enum CompassDirection8 {
|
||||||
NorthEast,
|
NorthEast,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CompassDirection8> for Point {
|
impl From<PrincipalWind> for Point {
|
||||||
fn from(compass_direction8: CompassDirection8) -> Point {
|
fn from(principal_wind: PrincipalWind) -> Point {
|
||||||
match compass_direction8 {
|
match principal_wind {
|
||||||
CompassDirection8::North => [0.0, -1.0].into(),
|
PrincipalWind::North => [0.0, -1.0].into(),
|
||||||
CompassDirection8::NorthWest => [-1.0, -1.0].into(),
|
PrincipalWind::NorthWest => [-1.0, -1.0].into(),
|
||||||
CompassDirection8::West => [-1.0, 0.0].into(),
|
PrincipalWind::West => [-1.0, 0.0].into(),
|
||||||
CompassDirection8::SouthWest => [-1.0, 1.0].into(),
|
PrincipalWind::SouthWest => [-1.0, 1.0].into(),
|
||||||
CompassDirection8::South => [0.0, 1.0].into(),
|
PrincipalWind::South => [0.0, 1.0].into(),
|
||||||
CompassDirection8::SouthEast => [1.0, 1.0].into(),
|
PrincipalWind::SouthEast => [1.0, 1.0].into(),
|
||||||
CompassDirection8::East => [1.0, 0.0].into(),
|
PrincipalWind::East => [1.0, 0.0].into(),
|
||||||
CompassDirection8::NorthEast => [1.0, -1.0].into(),
|
PrincipalWind::NorthEast => [1.0, -1.0].into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompassDirection8 {
|
impl PrincipalWind {
|
||||||
pub fn nearest_to_vector(vector: Point) -> Self {
|
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 {
|
if vector.x() == 0.0 && vector.y() == 0.0 {
|
||||||
panic!("Zero vector has no direction");
|
panic!("Zero vector has no direction");
|
||||||
}
|
}
|
||||||
|
|
||||||
let angle = vector.y().atan2(vector.x()); // atan2 gives angle in radians from -π to π
|
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 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 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 {
|
match ((compass_angle + 22.5) / 45.0).floor() as i32 % 8 {
|
||||||
0 => CompassDirection8::North,
|
0 => PrincipalWind::North,
|
||||||
1 => CompassDirection8::NorthEast,
|
1 => PrincipalWind::NorthEast,
|
||||||
2 => CompassDirection8::East,
|
2 => PrincipalWind::East,
|
||||||
3 => CompassDirection8::SouthEast,
|
3 => PrincipalWind::SouthEast,
|
||||||
4 => CompassDirection8::South,
|
4 => PrincipalWind::South,
|
||||||
5 => CompassDirection8::SouthWest,
|
5 => PrincipalWind::SouthWest,
|
||||||
6 => CompassDirection8::West,
|
6 => PrincipalWind::West,
|
||||||
7 => CompassDirection8::NorthWest,
|
7 => PrincipalWind::NorthWest,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,9 @@ impl<
|
||||||
where
|
where
|
||||||
I: Copy + GetIndex,
|
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 {
|
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
|
/// 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(
|
pub fn apex_of_compoundless_node(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue