feat(autorouter/autorouter): Implement germ of multilayer autorouting

This commit is contained in:
Mikolaj Wielgus 2025-09-27 15:41:15 +02:00
parent 471c12f657
commit 9a5b2095d5
22 changed files with 689 additions and 149 deletions

View File

@ -18,7 +18,7 @@ allowed_scopes = [
# Originally generated using
# `find src -type f | awk '!/lib.rs|mod.rs/ { print "\"" substr($1, 1 + 4, length($1) - 4 - 3) "\","; }' | sort`.
"autorouter/autoroute",
"autorouter/anterouter",
"autorouter/autorouter",
"autorouter/compare_detours",
"autorouter/conncomps",
@ -26,9 +26,12 @@ allowed_scopes = [
"autorouter/history",
"autorouter/invoker",
"autorouter/measure_length",
"autorouter/multilayer_autoroute",
"autorouter/permutator",
"autorouter/permuter",
"autorouter/place_via",
"autorouter/planar_autoroute",
"autorouter/planner",
"autorouter/pointroute",
"autorouter/presorter",
"autorouter/ratsnest",

View File

@ -299,6 +299,7 @@ impl PlaceActions {
pub struct RouteActions {
pub autoroute: Trigger,
pub planar_autoroute: Trigger,
pub topo_autoroute: Trigger,
}
@ -311,6 +312,12 @@ impl RouteActions {
egui::Key::R,
)
.into_trigger(),
planar_autoroute: Action::new(
tr.text("tr-menu-route-planar-autoroute"),
egui::Modifiers::CTRL,
egui::Key::P,
)
.into_trigger(),
topo_autoroute: Action::new(
tr.text("tr-menu-route-topo-autoroute"),
egui::Modifiers::CTRL | egui::Modifiers::SHIFT,
@ -332,6 +339,7 @@ impl RouteActions {
ui.add_enabled_ui(have_workspace, |ui| {
ui.add_enabled_ui(workspace_activities_enabled, |ui| {
self.autoroute.button(ctx, ui);
self.planar_autoroute.button(ctx, ui);
self.topo_autoroute.button(ctx, ui);
});
ui.separator();

View File

@ -354,6 +354,17 @@ impl MenuBar {
});
}
} else if actions.route.autoroute.consume_key_triggered(ctx, ui) {
schedule(error_dialog, workspace, |selection| {
Command::MultilayerAutoroute(
selection.pin_selection,
self.autorouter_options,
)
});
} else if actions
.route
.planar_autoroute
.consume_key_triggered(ctx, ui)
{
schedule(error_dialog, workspace, |selection| {
Command::Autoroute(selection.pin_selection, self.autorouter_options)
});

View File

@ -41,6 +41,7 @@ tr-menu-place-place-route-plan = Place Route Plan
tr-menu-route = Route
tr-menu-route-autoroute = Autoroute
tr-menu-route-planar-autoroute = Planar Autoroute
tr-menu-route-topo-autoroute = Topological single-layer Autoroute
tr-menu-route-routed-band-width = Routed Band Width

View File

@ -0,0 +1,208 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::collections::BTreeMap;
use geo::point;
use petgraph::graph::NodeIndex;
use rstar::{Envelope, RTreeObject, AABB};
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{ratline::RatlineIndex, Autorouter},
drawing::{
dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitiveRef},
primitive::MakePrimitiveShape,
},
geometry::GetLayer,
graph::{GenericIndex, GetIndex, MakeRef},
layout::{
poly::{MakePolygon, PolyWeight},
via::ViaWeight,
CompoundWeight, LayoutEdit,
},
math::Circle,
};
#[derive(Clone, Copy, Debug)]
pub enum TerminatingScheme {
ExistingFixedDot(FixedDotIndex),
Anteroute([f64; 2]),
}
#[derive(Clone, Debug)]
pub struct AnterouterPlan {
pub layer_map: BTreeMap<RatlineIndex, usize>,
pub ratline_endpoint_dot_to_terminating_scheme: BTreeMap<FixedDotIndex, TerminatingScheme>,
}
pub struct Anterouter {
plan: AnterouterPlan,
}
impl Anterouter {
pub fn new(plan: AnterouterPlan) -> Self {
Self { plan }
}
pub fn anteroute(&mut self, autorouter: &mut Autorouter<impl AccessMesadata>) {
// PERF: Unnecessary clone.
for (ratline, layer) in self.plan.layer_map.clone().iter() {
let endpoint_indices = ratline.ref_(autorouter).endpoint_indices();
let endpoint_dots = ratline.ref_(autorouter).endpoint_dots();
if let Some(terminating_scheme) = self
.plan
.ratline_endpoint_dot_to_terminating_scheme
.get(&endpoint_dots.0)
{
match terminating_scheme {
TerminatingScheme::ExistingFixedDot(terminating_dot) => autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(endpoint_indices.0, *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,
),
}
}
if let Some(terminating_scheme) = self
.plan
.ratline_endpoint_dot_to_terminating_scheme
.get(&endpoint_dots.1)
{
match terminating_scheme {
TerminatingScheme::ExistingFixedDot(terminating_dot) => autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(endpoint_indices.1, *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,
),
}
}
}
}
fn anteroute_dot_to_anchor(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
dot: FixedDotIndex,
to_layer: usize,
endpoint_dot_bbox_to_anchor: [f64; 2],
) {
self.place_assignment_via_on_anchor(
autorouter,
ratvertex,
dot,
to_layer,
endpoint_dot_bbox_to_anchor,
)
}
fn place_assignment_via_on_anchor(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratvertex: NodeIndex<usize>,
dot: FixedDotIndex,
to_layer: usize,
endpoint_dot_bbox_to_anchor: [f64; 2],
) {
let pin_layer = autorouter.board().layout().drawing().primitive(dot).layer();
let pin_maybe_net = autorouter
.board()
.layout()
.drawing()
.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 bbox = poly.ref_(autorouter.board().layout()).shape().envelope();
AABB::<[f64; 2]>::from_corners(
[bbox.lower().x(), bbox.lower().y()],
[bbox.upper().x(), bbox.upper().y()],
)
} else {
autorouter
.board()
.layout()
.drawing()
.primitive(dot)
.shape()
.envelope()
};
//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],
};
//let via_bbox_to_anchor = [-pin_bbox_to_anchor[0], -pin_bbox_to_anchor[1]];
let mut layout_edit = LayoutEdit::new();
if let Ok((.., dots)) = autorouter.board.layout_mut().add_via(
&mut layout_edit,
ViaWeight {
from_layer: std::cmp::min(pin_layer, to_layer),
to_layer: std::cmp::max(pin_layer, to_layer),
circle: Circle {
pos: pin_bbox_anchor,
r: 100.0,
},
maybe_net: pin_maybe_net,
},
) {
let terminating_dot = dots
.iter()
.find(|dot| {
to_layer
== dot
.primitive_ref(autorouter.board().layout().drawing())
.layer()
})
.unwrap();
autorouter
.ratsnest
.assign_terminating_dot_to_ratvertex(ratvertex, *terminating_dot);
}
/*let bbox = if let Some(poly) = autorouter.board().layout().drawing().geometry().compounds(dot).find(|(_, compound_weight)| {
matches!(compound_weight, CompoundWeight::Poly(..))
}) {
poly.ref_(autorouter.board().layout()).polygon()
} else {
//
}*/
}
}

View File

@ -7,11 +7,14 @@ use geo::Point;
use petgraph::graph::NodeIndex;
use serde::{Deserialize, Serialize};
use spade::InsertionError;
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use thiserror::Error;
use crate::{
autorouter::permutator::AutorouteExecutionPermutator,
autorouter::{
anterouter::AnterouterPlan, multilayer_autoroute::MultilayerAutorouteExecutionStepper,
permutator::PlanarAutorouteExecutionPermutator, planner::Planner,
},
board::{AccessMesadata, Board},
drawing::{band::BandTermsegIndex, Infringement},
graph::MakeRef,
@ -21,13 +24,13 @@ use crate::{
};
use super::{
autoroute::AutorouteExecutionStepper,
compare_detours::CompareDetoursExecutionStepper,
measure_length::MeasureLengthExecutionStepper,
place_via::PlaceViaExecutionStepper,
planar_autoroute::PlanarAutorouteExecutionStepper,
pointroute::PointrouteExecutionStepper,
ratline::RatlineIndex,
ratsnest::{Ratsnest, RatvertexIndex},
ratsnest::{Ratsnest, RatvertexNodeIndex},
remove_bands::RemoveBandsExecutionStepper,
selection::{BandSelection, PinSelection},
};
@ -91,8 +94,8 @@ impl<M: AccessMesadata> Autorouter<M> {
.unwrap()
.node_index()
{
RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => poly.ref_(self.board.layout()).apex(),
RatvertexNodeIndex::FixedDot(dot) => dot,
RatvertexNodeIndex::Poly(poly) => poly.ref_(self.board.layout()).apex(),
};
PointrouteExecutionStepper::new(self, origin_dot, point, options)
@ -105,27 +108,45 @@ impl<M: AccessMesadata> Autorouter<M> {
.map_err(|_| AutorouterError::CouldNotRemoveBand(band))
}
pub fn autoroute(
pub fn multilayer_autoroute(
&mut self,
selection: &PinSelection,
options: AutorouterOptions,
) -> Result<AutorouteExecutionPermutator, AutorouterError> {
AutorouteExecutionPermutator::new(self, self.selected_ratlines(selection), options)
) -> Result<MultilayerAutorouteExecutionStepper, AutorouterError> {
let planner = Planner::new(self, &self.selected_ratlines(selection));
MultilayerAutorouteExecutionStepper::new(
self,
self.selected_ratlines(selection),
planner.plan().clone(),
options,
)
}
pub(super) fn autoroute_ratlines(
pub fn planar_autoroute(
&mut self,
selection: &PinSelection,
options: AutorouterOptions,
) -> Result<PlanarAutorouteExecutionPermutator, AutorouterError> {
PlanarAutorouteExecutionPermutator::new(self, self.selected_ratlines(selection), options)
}
pub(super) fn planar_autoroute_ratlines(
&mut self,
ratlines: Vec<RatlineIndex>,
options: AutorouterOptions,
) -> Result<AutorouteExecutionStepper, AutorouterError> {
AutorouteExecutionStepper::new(self, ratlines, options)
) -> Result<PlanarAutorouteExecutionStepper, AutorouterError> {
PlanarAutorouteExecutionStepper::new(self, ratlines, options)
}
pub fn undo_autoroute(&mut self, selection: &PinSelection) -> Result<(), AutorouterError> {
self.undo_autoroute_ratlines(self.selected_ratlines(selection))
pub fn undo_planar_autoroute(
&mut self,
selection: &PinSelection,
) -> Result<(), AutorouterError> {
self.undo_planar_autoroute_ratlines(self.selected_ratlines(selection))
}
pub(super) fn undo_autoroute_ratlines(
pub(super) fn undo_planar_autoroute_ratlines(
&mut self,
ratlines: Vec<RatlineIndex>,
) -> Result<(), AutorouterError> {
@ -192,7 +213,7 @@ impl<M: AccessMesadata> Autorouter<M> {
active_layer,
allowed_edges,
ratlines.into_iter().filter_map(|ratline| {
let (source, target) = ratline.ref_(self).endpoint_dots();
let (source, target) = ratline.ref_(self).terminating_dots();
if navmesh
.as_ref()

View File

@ -17,15 +17,15 @@ use crate::{
};
use super::{
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
invoker::GetDebugOverlayData,
planar_autoroute::{PlanarAutorouteContinueStatus, PlanarAutorouteExecutionStepper},
ratline::RatlineIndex,
Autorouter, AutorouterError, AutorouterOptions,
};
pub struct CompareDetoursExecutionStepper {
autoroute: AutorouteExecutionStepper,
next_autoroute: Option<AutorouteExecutionStepper>,
autoroute: PlanarAutorouteExecutionStepper,
next_autoroute: Option<PlanarAutorouteExecutionStepper>,
ratline1: RatlineIndex,
ratline2: RatlineIndex,
total_length1: f64,
@ -41,8 +41,10 @@ impl CompareDetoursExecutionStepper {
options: AutorouterOptions,
) -> Result<Self, AutorouterError> {
Ok(Self {
autoroute: autorouter.autoroute_ratlines(vec![ratline1, ratline2], options)?,
next_autoroute: Some(autorouter.autoroute_ratlines(vec![ratline2, ratline1], options)?),
autoroute: autorouter.planar_autoroute_ratlines(vec![ratline1, ratline2], options)?,
next_autoroute: Some(
autorouter.planar_autoroute_ratlines(vec![ratline2, ratline1], options)?,
),
ratline1,
ratline2,
total_length1: 0.0,
@ -67,9 +69,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, (f64, f64)> for CompareDetoursExecut
match self.autoroute.step(autorouter)? {
ControlFlow::Continue(
AutorouteContinueStatus::Running | AutorouteContinueStatus::Skipped(_),
PlanarAutorouteContinueStatus::Running | PlanarAutorouteContinueStatus::Skipped(_),
) => Ok(ControlFlow::Continue(())),
ControlFlow::Continue(AutorouteContinueStatus::Routed(band_termseg)) => {
ControlFlow::Continue(PlanarAutorouteContinueStatus::Routed(band_termseg)) => {
let length = band_termseg
.ref_(autorouter.board.layout().drawing())
.length();
@ -84,13 +86,15 @@ impl<M: AccessMesadata> Step<Autorouter<M>, (f64, f64)> for CompareDetoursExecut
}
ControlFlow::Break(..) => {
if let Some(next_autoroute) = self.next_autoroute.take() {
autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?;
autorouter
.undo_planar_autoroute_ratlines(vec![self.ratline1, self.ratline2])?;
self.autoroute = next_autoroute;
Ok(ControlFlow::Continue(()))
} else {
self.done = true;
autorouter.undo_autoroute_ratlines(vec![self.ratline2, self.ratline1])?;
autorouter
.undo_planar_autoroute_ratlines(vec![self.ratline2, self.ratline1])?;
Ok(ControlFlow::Break((self.total_length1, self.total_length2)))
}

View File

@ -8,7 +8,10 @@ use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize};
use crate::{
autorouter::permutator::AutorouteExecutionPermutator,
autorouter::{
multilayer_autoroute::MultilayerAutorouteExecutionStepper,
permutator::PlanarAutorouteExecutionPermutator,
},
board::{edit::BoardEdit, AccessMesadata},
layout::via::ViaWeight,
router::ng,
@ -29,7 +32,8 @@ type Type = PinSelection;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Command {
Autoroute(PinSelection, AutorouterOptions),
Autoroute(PinSelection, AutorouterOptions), // TODO: Rename to PlanarAutoroute.
MultilayerAutoroute(PinSelection, AutorouterOptions),
TopoAutoroute {
selection: PinSelection,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
@ -45,7 +49,8 @@ pub enum Command {
#[enum_dispatch(GetDebugOverlayData)]
pub enum ExecutionStepper<M> {
Autoroute(AutorouteExecutionPermutator),
MultilayerAutoroute(MultilayerAutorouteExecutionStepper),
PlanarAutoroute(PlanarAutorouteExecutionPermutator),
TopoAutoroute(ng::AutorouteExecutionStepper<M>),
PlaceVia(PlaceViaExecutionStepper),
RemoveBands(RemoveBandsExecutionStepper),
@ -59,10 +64,16 @@ impl<M: AccessMesadata + Clone> ExecutionStepper<M> {
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<(Option<BoardEdit>, String)>, InvokerError> {
Ok(match self {
ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? {
ExecutionStepper::MultilayerAutoroute(autoroute) => match autoroute.step(autorouter)? {
ControlFlow::Continue(..) => ControlFlow::Continue(()),
ControlFlow::Break(edit) => {
ControlFlow::Break((edit, "finished autorouting".to_string()))
ControlFlow::Break((edit, "finished multilayer autorouting".to_string()))
}
},
ExecutionStepper::PlanarAutoroute(autoroute) => match autoroute.step(autorouter)? {
ControlFlow::Continue(..) => ControlFlow::Continue(()),
ControlFlow::Break(edit) => {
ControlFlow::Break((edit, "finished planar autorouting".to_string()))
}
},
ExecutionStepper::TopoAutoroute(autoroute) => {
@ -156,12 +167,17 @@ impl<M: AccessMesadata + Clone> Step<Invoker<M>, String> for ExecutionStepper<M>
impl<M: AccessMesadata + Clone> Abort<Invoker<M>> for ExecutionStepper<M> {
fn abort(&mut self, invoker: &mut Invoker<M>) {
match self {
ExecutionStepper::MultilayerAutoroute(autoroute) => {
autoroute.abort(&mut invoker.autorouter)
}
ExecutionStepper::PlanarAutoroute(autoroute) => {
autoroute.abort(&mut invoker.autorouter)
}
ExecutionStepper::TopoAutoroute(autoroute) => {
autoroute.abort(&mut ());
// TODO: maintain topo-navmesh just like layout
*invoker.autorouter.board.layout_mut() = autoroute.last_layout.clone();
}
ExecutionStepper::Autoroute(autoroute) => autoroute.abort(&mut invoker.autorouter),
ExecutionStepper::PlaceVia(_place_via) => (), //place_via.abort(),
ExecutionStepper::RemoveBands(_remove_bands) => (), //remove_bands.abort(),
ExecutionStepper::CompareDetours(_compare_detours) => (), //compare_detours.abort(),
@ -177,7 +193,8 @@ impl<M> EstimateProgress for ExecutionStepper<M> {
fn estimate_progress_value(&self) -> f64 {
match self {
ExecutionStepper::Autoroute(autoroute) => autoroute.estimate_progress_value(),
ExecutionStepper::MultilayerAutoroute(autoroute) => autoroute.estimate_progress_value(),
ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_progress_value(),
ExecutionStepper::TopoAutoroute(toporoute) => toporoute.estimate_progress_value(),
ExecutionStepper::PlaceVia(place_via) => place_via.estimate_progress_value(),
ExecutionStepper::RemoveBands(remove_bands) => remove_bands.estimate_progress_value(),
@ -192,7 +209,10 @@ impl<M> EstimateProgress for ExecutionStepper<M> {
fn estimate_progress_maximum(&self) -> f64 {
match self {
ExecutionStepper::Autoroute(autoroute) => autoroute.estimate_progress_maximum(),
ExecutionStepper::MultilayerAutoroute(autoroute) => {
autoroute.estimate_progress_maximum()
}
ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_progress_maximum(),
ExecutionStepper::TopoAutoroute(toporoute) => toporoute.estimate_progress_maximum(),
ExecutionStepper::PlaceVia(place_via) => place_via.estimate_progress_maximum(),
ExecutionStepper::RemoveBands(remove_bands) => remove_bands.estimate_progress_maximum(),

View File

@ -32,7 +32,8 @@ use super::{
execution::{Command, ExecutionStepper},
history::{History, HistoryError},
measure_length::MeasureLengthExecutionStepper,
permutator::AutorouteExecutionPermutator,
multilayer_autoroute::MultilayerAutorouteExecutionStepper,
permutator::PlanarAutorouteExecutionPermutator,
place_via::PlaceViaExecutionStepper,
remove_bands::RemoveBandsExecutionStepper,
Autorouter, AutorouterError,
@ -166,8 +167,13 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
#[debug_requires(self.ongoing_command.is_none())]
fn dispatch_command(&mut self, command: &Command) -> Result<ExecutionStepper<M>, InvokerError> {
Ok(match command {
Command::Autoroute(selection, options) => {
ExecutionStepper::Autoroute(self.autorouter.autoroute(selection, *options)?)
Command::Autoroute(selection, options) => ExecutionStepper::PlanarAutoroute(
self.autorouter.planar_autoroute(selection, *options)?,
),
Command::MultilayerAutoroute(selection, options) => {
ExecutionStepper::MultilayerAutoroute(
self.autorouter.multilayer_autoroute(selection, *options)?,
)
}
Command::TopoAutoroute {
selection,

View File

@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT
pub mod autoroute;
pub mod anterouter;
mod autorouter;
pub mod compare_detours;
pub mod conncomps;
@ -10,9 +10,12 @@ pub mod execution;
pub mod history;
pub mod invoker;
pub mod measure_length;
pub mod multilayer_autoroute;
pub mod permutator;
pub mod permuter;
pub mod place_via;
pub mod planar_autoroute;
pub mod planner;
pub mod pointroute;
pub mod presorter;
pub mod ratline;

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::ops::ControlFlow;
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
anterouter::{Anterouter, AnterouterPlan},
invoker::GetDebugOverlayData,
permutator::PlanarAutorouteExecutionPermutator,
planar_autoroute::PlanarAutorouteContinueStatus,
ratline::RatlineIndex,
Autorouter, AutorouterError, AutorouterOptions,
},
board::edit::BoardEdit,
drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape,
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{Abort, EstimateProgress, Step},
};
pub struct MultilayerAutorouteExecutionStepper {
planar: PlanarAutorouteExecutionPermutator,
}
impl MultilayerAutorouteExecutionStepper {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
plan: AnterouterPlan,
options: AutorouterOptions,
) -> Result<Self, AutorouterError> {
let mut assigner = Anterouter::new(plan);
assigner.anteroute(autorouter);
Ok(Self {
planar: PlanarAutorouteExecutionPermutator::new(autorouter, ratlines, options)?,
})
}
}
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteContinueStatus>
for MultilayerAutorouteExecutionStepper
{
type Error = AutorouterError;
fn step(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, PlanarAutorouteContinueStatus>, AutorouterError>
{
self.planar.step(autorouter)
}
}
impl<M: AccessMesadata> Abort<Autorouter<M>> for MultilayerAutorouteExecutionStepper {
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
self.planar.abort(autorouter)
}
}
impl EstimateProgress for MultilayerAutorouteExecutionStepper {
type Value = f64;
fn estimate_progress_value(&self) -> f64 {
self.planar.estimate_progress_value()
}
fn estimate_progress_maximum(&self) -> f64 {
self.planar.estimate_progress_maximum()
}
}
impl GetDebugOverlayData for MultilayerAutorouteExecutionStepper {
fn maybe_thetastar(&self) -> Option<&ThetastarStepper<Navmesh, f64>> {
self.planar.maybe_thetastar()
}
fn maybe_navcord(&self) -> Option<&Navcord> {
self.planar.maybe_navcord()
}
fn ghosts(&self) -> &[PrimitiveShape] {
self.planar.ghosts()
}
fn obstacles(&self) -> &[PrimitiveIndex] {
self.planar.obstacles()
}
}

View File

@ -8,9 +8,9 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
invoker::GetDebugOverlayData,
permuter::{PermuteRatlines, RatlinesPermuter},
planar_autoroute::{PlanarAutorouteContinueStatus, PlanarAutorouteExecutionStepper},
presorter::{PresortParams, PresortRatlines, SccIntersectionsAndLengthPresorter},
ratline::RatlineIndex,
Autorouter, AutorouterError, AutorouterOptions,
@ -22,13 +22,13 @@ use crate::{
stepper::{Abort, EstimateProgress, Permutate, Step},
};
pub struct AutorouteExecutionPermutator {
stepper: AutorouteExecutionStepper,
pub struct PlanarAutorouteExecutionPermutator {
stepper: PlanarAutorouteExecutionStepper,
permuter: RatlinesPermuter,
options: AutorouterOptions,
}
impl AutorouteExecutionPermutator {
impl PlanarAutorouteExecutionPermutator {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
@ -49,7 +49,7 @@ impl AutorouteExecutionPermutator {
let permuter = RatlinesPermuter::new(autorouter, ratlines, presorter, &options);
Ok(Self {
stepper: AutorouteExecutionStepper::new(
stepper: PlanarAutorouteExecutionStepper::new(
autorouter,
initially_sorted_ratlines,
options,
@ -61,15 +61,16 @@ impl AutorouteExecutionPermutator {
}
}
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinueStatus>
for AutorouteExecutionPermutator
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteContinueStatus>
for PlanarAutorouteExecutionPermutator
{
type Error = AutorouterError;
fn step(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
) -> Result<ControlFlow<Option<BoardEdit>, PlanarAutorouteContinueStatus>, AutorouterError>
{
match self.stepper.step(autorouter) {
Ok(ok) => Ok(ok),
Err(err) => {
@ -97,14 +98,14 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
}
}
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionPermutator {
impl<M: AccessMesadata> Abort<Autorouter<M>> for PlanarAutorouteExecutionPermutator {
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
//self.permutations_iter.all(|_| true); // Why did I add this code here???
self.stepper.abort(autorouter);
}
}
impl EstimateProgress for AutorouteExecutionPermutator {
impl EstimateProgress for PlanarAutorouteExecutionPermutator {
type Value = f64;
fn estimate_progress_value(&self) -> f64 {
@ -118,7 +119,7 @@ impl EstimateProgress for AutorouteExecutionPermutator {
}
}
impl GetDebugOverlayData for AutorouteExecutionPermutator {
impl GetDebugOverlayData for PlanarAutorouteExecutionPermutator {
fn maybe_thetastar(&self) -> Option<&ThetastarStepper<Navmesh, f64>> {
self.stepper.maybe_thetastar()
}

View File

@ -10,8 +10,9 @@ use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
autoroute::AutorouteExecutionStepper, presorter::SccIntersectionsAndLengthPresorter,
ratline::RatlineIndex, scc::Scc, Autorouter, AutorouterOptions,
planar_autoroute::PlanarAutorouteExecutionStepper,
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, scc::Scc, Autorouter,
AutorouterOptions,
},
drawing::graph::MakePrimitiveRef,
geometry::{GenericNode, GetLayer},
@ -23,7 +24,7 @@ pub trait PermuteRatlines {
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &AutorouteExecutionStepper,
stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>>;
}
@ -77,7 +78,7 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
_stepper: &AutorouteExecutionStepper,
_stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
let scc_permutation = self.sccs_permutations_iter.next()?;
let mut ratlines = vec![];
@ -130,20 +131,20 @@ impl PermuteRatlines for RatlineCutsRatlinePermuter {
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &AutorouteExecutionStepper,
stepper: &PlanarAutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
let curr_ratline = stepper.ratlines()[*stepper.curr_ratline_index()];
let endpoint_dots = curr_ratline.ref_(autorouter).endpoint_dots();
let terminating_dots = curr_ratline.ref_(autorouter).terminating_dots();
let bands_cut_by_ratline: Vec<_> = autorouter
.board()
.layout()
.bands_between_nodes(
endpoint_dots
terminating_dots
.0
.primitive_ref(autorouter.board().layout().drawing())
.layer(),
GenericNode::Primitive(endpoint_dots.0.into()),
GenericNode::Primitive(endpoint_dots.1.into()),
GenericNode::Primitive(terminating_dots.0.into()),
GenericNode::Primitive(terminating_dots.1.into()),
)
.collect();

View File

@ -30,7 +30,7 @@ use super::{
};
/// Represents the current status of the autoroute operation.
pub enum AutorouteContinueStatus {
pub enum PlanarAutorouteContinueStatus {
/// The autoroute is currently running and in progress.
Running,
/// A specific segment has been successfully routed.
@ -41,7 +41,7 @@ pub enum AutorouteContinueStatus {
/// Manages the autorouting process across multiple ratlines.
#[derive(Getters)]
pub struct AutorouteExecutionStepper {
pub struct PlanarAutorouteExecutionStepper {
/// The ratlines which we are routing.
ratlines: Vec<RatlineIndex>,
/// Keeps track of the current ratline being routed, if one is active.
@ -56,7 +56,7 @@ pub struct AutorouteExecutionStepper {
options: AutorouterOptions,
}
impl AutorouteExecutionStepper {
impl PlanarAutorouteExecutionStepper {
/// Initializes a new [`AutorouteExecutionStepper`] instance.
///
/// This method sets up the routing process by accepting the execution properties.
@ -71,7 +71,7 @@ impl AutorouteExecutionStepper {
return Err(AutorouterError::NothingToRoute);
};
let (origin, destination) = ratlines[0].ref_(autorouter).endpoint_dots();
let (origin, destination) = ratlines[0].ref_(autorouter).terminating_dots();
let mut router = Router::new(autorouter.board.layout_mut(), options.router_options);
Ok(Self {
@ -107,7 +107,7 @@ impl AutorouteExecutionStepper {
autorouter.board.apply_edit(&board_edit.reverse());
let (origin, destination) = self.ratlines[index].ref_(autorouter).endpoint_dots();
let (origin, destination) = self.ratlines[index].ref_(autorouter).terminating_dots();
let mut router = Router::new(autorouter.board.layout_mut(), self.options.router_options);
self.route = Some(router.route(
@ -129,15 +129,16 @@ impl AutorouteExecutionStepper {
}
}
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinueStatus>
for AutorouteExecutionStepper
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarAutorouteContinueStatus>
for PlanarAutorouteExecutionStepper
{
type Error = AutorouterError;
fn step(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
) -> Result<ControlFlow<Option<BoardEdit>, PlanarAutorouteContinueStatus>, AutorouterError>
{
// TODO: Use a proper state machine here for better readability?
if self.curr_ratline_index >= self.ratlines.len() {
@ -157,50 +158,56 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
return Ok(ControlFlow::Break(None));
};
let (source, target) = self.ratlines[self.curr_ratline_index]
let (origin, destination) = self.ratlines[self.curr_ratline_index]
.ref_(autorouter)
.endpoint_dots();
.terminating_dots();
let ret = if let Some(band_termseg) = autorouter.board.band_between_nodes(source, target) {
AutorouteContinueStatus::Skipped(band_termseg[false])
} else {
let band_termseg = {
let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options);
let ret =
if let Some(band_termseg) = autorouter.board.band_between_nodes(origin, destination) {
PlanarAutorouteContinueStatus::Skipped(band_termseg[false])
} else {
let band_termseg = {
let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options);
let ControlFlow::Break(band_termseg) = route.step(&mut router)? else {
return Ok(ControlFlow::Continue(AutorouteContinueStatus::Running));
let ControlFlow::Break(band_termseg) = route.step(&mut router)? else {
return Ok(ControlFlow::Continue(
PlanarAutorouteContinueStatus::Running,
));
};
band_termseg
};
band_termseg
let band = autorouter
.board
.layout()
.drawing()
.find_loose_band_uid(band_termseg.into())
.expect("a completely routed band should've Seg's as ends");
autorouter.ratsnest.assign_band_termseg_to_ratline(
self.ratlines[self.curr_ratline_index],
band_termseg,
);
let mut board_data_edit = BoardDataEdit::new();
autorouter.board.try_set_band_between_nodes(
&mut board_data_edit,
origin,
destination,
band,
);
self.board_data_edits.push(board_data_edit);
PlanarAutorouteContinueStatus::Routed(band_termseg)
};
let band = autorouter
.board
.layout()
.drawing()
.find_loose_band_uid(band_termseg.into())
.expect("a completely routed band should've Seg's as ends");
autorouter.ratsnest.assign_band_termseg_to_ratline(
self.ratlines[self.curr_ratline_index],
band_termseg,
);
let mut board_data_edit = BoardDataEdit::new();
autorouter
.board
.try_set_band_between_nodes(&mut board_data_edit, source, target, band);
self.board_data_edits.push(board_data_edit);
AutorouteContinueStatus::Routed(band_termseg)
};
self.curr_ratline_index += 1;
if let Some(new_ratline) = self.ratlines.get(self.curr_ratline_index) {
let (source, target) = new_ratline.ref_(autorouter).endpoint_dots();
let (source, target) = new_ratline.ref_(autorouter).terminating_dots();
let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options);
@ -219,14 +226,14 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
}
}
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionStepper {
impl<M: AccessMesadata> Abort<Autorouter<M>> for PlanarAutorouteExecutionStepper {
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
self.backtrace_to_index(autorouter, 0);
self.curr_ratline_index = self.ratlines.len();
}
}
impl<M: AccessMesadata> Permutate<Autorouter<M>> for AutorouteExecutionStepper {
impl<M: AccessMesadata> Permutate<Autorouter<M>> for PlanarAutorouteExecutionStepper {
type Index = RatlineIndex;
type Output = Result<(), AutorouterError>;
@ -249,7 +256,7 @@ impl<M: AccessMesadata> Permutate<Autorouter<M>> for AutorouteExecutionStepper {
}
}
impl EstimateProgress for AutorouteExecutionStepper {
impl EstimateProgress for PlanarAutorouteExecutionStepper {
type Value = f64;
fn estimate_progress_value(&self) -> f64 {
@ -264,7 +271,7 @@ impl EstimateProgress for AutorouteExecutionStepper {
}
}
impl GetDebugOverlayData for AutorouteExecutionStepper {
impl GetDebugOverlayData for PlanarAutorouteExecutionStepper {
fn maybe_thetastar(&self) -> Option<&ThetastarStepper<Navmesh, f64>> {
self.route.as_ref().map(|route| route.thetastar())
}

107
src/autorouter/planner.rs Normal file
View File

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::collections::BTreeMap;
use derive_getters::Getters;
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
anterouter::{AnterouterPlan, TerminatingScheme},
ratline::RatlineIndex,
Autorouter,
},
drawing::{
dot::FixedDotIndex,
graph::{MakePrimitiveRef, PrimitiveIndex},
},
geometry::{GenericNode, GetLayer},
graph::MakeRef,
};
#[derive(Getters)]
pub struct Planner {
plan: AnterouterPlan,
}
impl Planner {
pub fn new(autorouter: &Autorouter<impl AccessMesadata>, ratlines: &[RatlineIndex]) -> Self {
let mut plan = AnterouterPlan {
layer_map: ratlines
.iter()
.enumerate()
.map(|(i, ratline)| (*ratline, i % 2))
.collect(),
ratline_endpoint_dot_to_terminating_scheme: BTreeMap::new(),
};
for ratline in ratlines {
let layer = plan.layer_map[ratline];
if let Some(terminating_scheme) = Self::determine_terminating_scheme(
autorouter,
ratline.ref_(autorouter).endpoint_dots().0,
layer,
) {
plan.ratline_endpoint_dot_to_terminating_scheme.insert(
ratline.ref_(autorouter).endpoint_dots().0,
terminating_scheme,
);
}
if let Some(terminating_scheme) = Self::determine_terminating_scheme(
autorouter,
ratline.ref_(autorouter).endpoint_dots().1,
layer,
) {
plan.ratline_endpoint_dot_to_terminating_scheme.insert(
ratline.ref_(autorouter).endpoint_dots().1,
terminating_scheme,
);
}
}
Self { plan }
}
fn determine_terminating_scheme(
autorouter: &Autorouter<impl AccessMesadata>,
ratline_endpoint_dot: FixedDotIndex,
layer: usize,
) -> Option<TerminatingScheme> {
if layer
== ratline_endpoint_dot
.primitive_ref(autorouter.board().layout().drawing())
.layer()
{
return None;
}
let pinname = autorouter
.board()
.node_pinname(&GenericNode::Primitive(ratline_endpoint_dot.into()))
.unwrap();
Some(
autorouter
.board()
.pinname_nodes(pinname)
.find_map(|node| {
if let GenericNode::Primitive(PrimitiveIndex::FixedDot(dot)) = node {
(layer
== dot
.primitive_ref(autorouter.board().layout().drawing())
.layer())
.then_some(dot)
} else {
None
}
})
.map_or(TerminatingScheme::Anteroute([-1.0, -1.0]), |dot| {
TerminatingScheme::ExistingFixedDot(dot)
}),
)
}
}

View File

@ -17,7 +17,7 @@ use crate::{
triangulation::GetTrianvertexNodeIndex,
};
use super::{ratsnest::RatvertexIndex, Autorouter};
use super::{ratsnest::RatvertexNodeIndex, Autorouter};
pub type RatlineIndex = EdgeIndex<usize>;
@ -69,8 +69,8 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.unwrap()
.node_index()
{
RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => poly.ref_(self.autorouter.board.layout()).apex(),
RatvertexNodeIndex::FixedDot(dot) => dot,
RatvertexNodeIndex::Poly(poly) => poly.ref_(self.autorouter.board.layout()).apex(),
};
let target_dot = match self
@ -81,13 +81,41 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
.unwrap()
.node_index()
{
RatvertexIndex::FixedDot(dot) => dot,
RatvertexIndex::Poly(poly) => poly.ref_(self.autorouter.board.layout()).apex(),
RatvertexNodeIndex::FixedDot(dot) => dot,
RatvertexNodeIndex::Poly(poly) => poly.ref_(self.autorouter.board.layout()).apex(),
};
(source_dot, target_dot)
}
pub fn terminating_dots(&self) -> (FixedDotIndex, FixedDotIndex) {
let (source, target) = self
.autorouter
.ratsnest
.graph()
.edge_endpoints(self.index)
.unwrap();
let source_dot = self
.autorouter
.ratsnest
.graph()
.node_weight(source)
.unwrap()
.maybe_terminating_dot
.unwrap_or(self.endpoint_dots().0);
let target_dot = self
.autorouter
.ratsnest
.graph()
.node_weight(target)
.unwrap()
.maybe_terminating_dot
.unwrap_or(self.endpoint_dots().1);
(source_dot, target_dot)
}
pub fn layer(&self) -> usize {
self.endpoint_dots()
.0
@ -172,7 +200,7 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
Line::new(source_pos, target_pos)
}
fn endpoint_indices(&self) -> (NodeIndex<usize>, NodeIndex<usize>) {
pub fn endpoint_indices(&self) -> (NodeIndex<usize>, NodeIndex<usize>) {
self.autorouter
.ratsnest
.graph()

View File

@ -9,12 +9,11 @@ use std::{
use enum_dispatch::enum_dispatch;
use geo::Point;
use petgraph::{data::Element, prelude::StableUnGraph};
use petgraph::{data::Element, graph::NodeIndex, prelude::StableUnGraph};
use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2};
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::conncomps::ConncompsWithPrincipalLayer,
board::Board,
drawing::{
band::BandTermsegIndex,
@ -28,32 +27,36 @@ use crate::{
triangulation::{GetTrianvertexNodeIndex, Triangulation},
};
use super::ratline::{RatlineIndex, RatlineWeight};
use super::{
conncomps::ConncompsWithPrincipalLayer,
ratline::{RatlineIndex, RatlineWeight},
};
#[enum_dispatch(GetIndex)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RatvertexIndex {
pub enum RatvertexNodeIndex {
FixedDot(FixedDotIndex),
Poly(GenericIndex<PolyWeight>),
}
impl From<RatvertexIndex> for crate::layout::NodeIndex {
fn from(vertex: RatvertexIndex) -> crate::layout::NodeIndex {
impl From<RatvertexNodeIndex> for crate::layout::NodeIndex {
fn from(vertex: RatvertexNodeIndex) -> crate::layout::NodeIndex {
match vertex {
RatvertexIndex::FixedDot(dot) => crate::layout::NodeIndex::Primitive(dot.into()),
RatvertexIndex::Poly(poly) => crate::layout::NodeIndex::Compound(poly.into()),
RatvertexNodeIndex::FixedDot(dot) => crate::layout::NodeIndex::Primitive(dot.into()),
RatvertexNodeIndex::Poly(poly) => crate::layout::NodeIndex::Compound(poly.into()),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RatvertexWeight {
vertex: RatvertexIndex,
vertex: RatvertexNodeIndex,
pub pos: Point,
pub maybe_terminating_dot: Option<FixedDotIndex>,
}
impl GetTrianvertexNodeIndex<RatvertexIndex> for RatvertexWeight {
fn node_index(&self) -> RatvertexIndex {
impl GetTrianvertexNodeIndex<RatvertexNodeIndex> for RatvertexWeight {
fn node_index(&self) -> RatvertexNodeIndex {
self.vertex
}
}
@ -80,22 +83,22 @@ impl RatvertexToHandleMap {
}
}
impl Index<RatvertexIndex> for RatvertexToHandleMap {
impl Index<RatvertexNodeIndex> for RatvertexToHandleMap {
type Output = Option<FixedVertexHandle>;
fn index(&self, ratvertex: RatvertexIndex) -> &Self::Output {
fn index(&self, ratvertex: RatvertexNodeIndex) -> &Self::Output {
match ratvertex {
RatvertexIndex::FixedDot(dot) => &self.fixed_dot_to_handle[dot.index()],
RatvertexIndex::Poly(bend) => &self.poly_to_handle[bend.index()],
RatvertexNodeIndex::FixedDot(dot) => &self.fixed_dot_to_handle[dot.index()],
RatvertexNodeIndex::Poly(bend) => &self.poly_to_handle[bend.index()],
}
}
}
impl IndexMut<RatvertexIndex> for RatvertexToHandleMap {
fn index_mut(&mut self, ratvertex: RatvertexIndex) -> &mut Self::Output {
impl IndexMut<RatvertexNodeIndex> for RatvertexToHandleMap {
fn index_mut(&mut self, ratvertex: RatvertexNodeIndex) -> &mut Self::Output {
match ratvertex {
RatvertexIndex::FixedDot(dot) => &mut self.fixed_dot_to_handle[dot.index()],
RatvertexIndex::Poly(bend) => &mut self.poly_to_handle[bend.index()],
RatvertexNodeIndex::FixedDot(dot) => &mut self.fixed_dot_to_handle[dot.index()],
RatvertexNodeIndex::Poly(bend) => &mut self.poly_to_handle[bend.index()],
}
}
}
@ -160,12 +163,12 @@ impl Ratsnest {
board: &Board<impl AccessMesadata>,
triangulations: &mut BTreeMap<
usize,
Triangulation<RatvertexIndex, RatvertexToHandleMap, RatvertexWeight, RatlineWeight>,
Triangulation<RatvertexNodeIndex, RatvertexToHandleMap, RatvertexWeight, RatlineWeight>,
>,
layer: usize,
) -> Result<(), InsertionError> {
let mut handle_ratvertex_weight =
|maybe_net: Option<usize>, vertex: RatvertexIndex, pos: Point| {
|maybe_net: Option<usize>, vertex: RatvertexNodeIndex, pos: Point| {
let Some(net) = maybe_net else {
return Ok(());
};
@ -184,7 +187,11 @@ impl Ratsnest {
return Ok(());
}
triangulation.add_vertex(RatvertexWeight { vertex, pos })?;
triangulation.add_vertex(RatvertexWeight {
vertex,
pos,
maybe_terminating_dot: None,
})?;
Ok(())
};
@ -195,7 +202,7 @@ impl Ratsnest {
if board.layout().drawing().compounds(dot).next().is_none() {
handle_ratvertex_weight(
board.layout().drawing().primitive(dot).maybe_net(),
RatvertexIndex::FixedDot(dot),
RatvertexNodeIndex::FixedDot(dot),
node.primitive_ref(board.layout().drawing())
.shape()
.center(),
@ -211,7 +218,7 @@ impl Ratsnest {
.drawing()
.compound_weight(poly.into())
.maybe_net(),
RatvertexIndex::Poly(poly),
RatvertexNodeIndex::Poly(poly),
poly.ref_(board.layout()).shape().center(),
)?;
}
@ -219,6 +226,17 @@ impl Ratsnest {
Ok(())
}
pub fn assign_terminating_dot_to_ratvertex(
&mut self,
node_index: NodeIndex<usize>,
terminating_dot: FixedDotIndex,
) {
self.graph
.node_weight_mut(node_index)
.unwrap()
.maybe_terminating_dot = Some(terminating_dot)
}
pub fn assign_band_termseg_to_ratline(
&mut self,
ratline: RatlineIndex,

View File

@ -144,7 +144,7 @@ impl<R: AccessRules> Layout<R> {
&mut self,
recorder: &mut LayoutEdit,
weight: ViaWeight,
) -> Result<GenericIndex<ViaWeight>, Infringement> {
) -> Result<(GenericIndex<ViaWeight>, Vec<FixedDotIndex>), Infringement> {
let compound = self.drawing.add_compound(recorder, weight.into());
let mut dots = vec![];
@ -179,7 +179,7 @@ impl<R: AccessRules> Layout<R> {
}
}
Ok(GenericIndex::<ViaWeight>::new(compound.index()))
Ok((GenericIndex::<ViaWeight>::new(compound.index()), dots))
}
pub fn add_fixed_dot(

View File

@ -252,7 +252,7 @@ impl Navmesh {
}
// The existence of a constraint edge does not (!) guarantee that this
// edge exactly will be present in the triangulation. It appears that
// exact edge will be present in the triangulation. It appears that
// Spade splits a constraint edge in two if an endpoint of another
// constraint lies on it.
//

View File

@ -34,11 +34,11 @@ impl RouteStepper {
pub fn new(
router: &mut Router<impl AccessRules>,
recorder: LayoutEdit,
from: FixedDotIndex,
to: FixedDotIndex,
origin: FixedDotIndex,
destination: FixedDotIndex,
width: f64,
) -> Result<Self, NavmeshError> {
let navmesh = Navmesh::new(router.layout(), from, to, *router.options())?;
let navmesh = Navmesh::new(router.layout(), origin, destination, *router.options())?;
Ok(Self::new_from_navmesh(router, recorder, navmesh, width))
}

View File

@ -42,7 +42,7 @@ pub struct RouterOptions {
pub struct RouterThetastarStrategy<'a, R> {
pub layout: &'a mut Layout<R>,
pub navcord: &'a mut Navcord,
pub target: FixedDotIndex,
pub destination: FixedDotIndex,
pub probe_ghosts: Vec<PrimitiveShape>,
pub probe_obstacles: Vec<PrimitiveIndex>,
}
@ -52,7 +52,7 @@ impl<'a, R> RouterThetastarStrategy<'a, R> {
Self {
layout,
navcord,
target,
destination: target,
probe_ghosts: vec![],
probe_obstacles: vec![],
}
@ -79,7 +79,7 @@ impl<R: AccessRules> ThetastarStrategy<Navmesh, f64, BandTermsegIndex>
// without this, since A* will terminate now anyway.
self.navcord.maybe_final_termseg = Some(
self.layout
.finish(navmesh, self.navcord, self.target)
.finish(navmesh, self.navcord, self.destination)
.map_err(|_| ())?,
);
self.navcord.path.push(navnode);
@ -190,7 +190,7 @@ impl<R: AccessRules> ThetastarStrategy<Navmesh, f64, BandTermsegIndex>
let end_point = self
.layout
.drawing()
.primitive(self.target)
.primitive(self.destination)
.shape()
.center();
@ -213,11 +213,11 @@ impl<'a, R: AccessRules> Router<'a, R> {
pub fn route(
&mut self,
recorder: LayoutEdit,
from: FixedDotIndex,
to: FixedDotIndex,
origin: FixedDotIndex,
destination: FixedDotIndex,
width: f64,
) -> Result<RouteStepper, NavmeshError> {
RouteStepper::new(self, recorder, from, to, width)
RouteStepper::new(self, recorder, origin, destination, width)
}
pub fn layout_mut(&mut self) -> &mut Layout<R> {

View File

@ -139,7 +139,7 @@ pub fn assert_navnode_count(
.iter()
.find_map(|ratline| {
let (candidate_origin, candidate_destination) =
ratline.ref_(autorouter).endpoint_dots();
ratline.ref_(autorouter).terminating_dots();
let candidate_origin_pin = autorouter
.board()
.node_pinname(&GenericNode::Primitive(candidate_origin.into()))