mirror of https://codeberg.org/topola/topola.git
feat(autorouter/compass_direction): Anteroute fanout vias in eight "compass" directions
This commit is contained in:
parent
71feedc6ce
commit
1bbb068af9
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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(..))
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue