mirror of https://codeberg.org/topola/topola.git
feat(autorouter/autorouter): Implement germ of multilayer autorouting
This commit is contained in:
parent
471c12f657
commit
9a5b2095d5
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
//
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
Loading…
Reference in New Issue