mirror of https://codeberg.org/topola/topola.git
feat: Implement TopoNavmesh DualOuter-DualOuter extraction
This also fixes a sign bug in LineIntersection calculation
This commit is contained in:
parent
cfde2eac20
commit
d3dc826be4
|
|
@ -176,6 +176,7 @@ impl eframe::App for App {
|
||||||
ctx,
|
ctx,
|
||||||
&mut self.translator,
|
&mut self.translator,
|
||||||
self.content_channel.0.clone(),
|
self.content_channel.0.clone(),
|
||||||
|
&mut self.error_dialog,
|
||||||
&mut self.viewport,
|
&mut self.viewport,
|
||||||
self.maybe_workspace.as_mut(),
|
self.maybe_workspace.as_mut(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use topola::{
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::Actions,
|
actions::Actions,
|
||||||
app::{execute, handle_file},
|
app::{execute, handle_file},
|
||||||
|
error_dialog::ErrorDialog,
|
||||||
translator::Translator,
|
translator::Translator,
|
||||||
viewport::Viewport,
|
viewport::Viewport,
|
||||||
workspace::Workspace,
|
workspace::Workspace,
|
||||||
|
|
@ -63,6 +64,7 @@ impl MenuBar {
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
tr: &mut Translator,
|
tr: &mut Translator,
|
||||||
content_sender: Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
|
content_sender: Sender<Result<SpecctraDesign, SpecctraLoadingError>>,
|
||||||
|
error_dialog: &mut ErrorDialog,
|
||||||
viewport: &mut Viewport,
|
viewport: &mut Viewport,
|
||||||
maybe_workspace: Option<&mut Workspace>,
|
maybe_workspace: Option<&mut Workspace>,
|
||||||
) -> Result<(), InvokerError> {
|
) -> Result<(), InvokerError> {
|
||||||
|
|
@ -287,6 +289,7 @@ impl MenuBar {
|
||||||
) {
|
) {
|
||||||
} else if workspace_activities_enabled {
|
} else if workspace_activities_enabled {
|
||||||
fn schedule<F: FnOnce(Selection) -> Command>(
|
fn schedule<F: FnOnce(Selection) -> Command>(
|
||||||
|
error_dialog: &mut ErrorDialog,
|
||||||
workspace: &mut Workspace,
|
workspace: &mut Workspace,
|
||||||
op: F,
|
op: F,
|
||||||
) {
|
) {
|
||||||
|
|
@ -306,11 +309,13 @@ impl MenuBar {
|
||||||
.0
|
.0
|
||||||
.retain(|i| i.layer == active_layer);
|
.retain(|i| i.layer == active_layer);
|
||||||
}
|
}
|
||||||
workspace.interactor.schedule(op(selection));
|
if let Err(err) = workspace.interactor.schedule(op(selection)) {
|
||||||
|
error_dialog.push_error("tr-module-invoker", format!("{}", err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let opts = self.autorouter_options;
|
let opts = self.autorouter_options;
|
||||||
if actions.edit.remove_bands.consume_key_triggered(ctx, ui) {
|
if actions.edit.remove_bands.consume_key_triggered(ctx, ui) {
|
||||||
schedule(workspace, |selection| {
|
schedule(error_dialog, workspace, |selection| {
|
||||||
Command::RemoveBands(selection.band_selection)
|
Command::RemoveBands(selection.band_selection)
|
||||||
})
|
})
|
||||||
} else if actions.route.topo_autoroute.consume_key_triggered(ctx, ui) {
|
} else if actions.route.topo_autoroute.consume_key_triggered(ctx, ui) {
|
||||||
|
|
@ -325,15 +330,17 @@ impl MenuBar {
|
||||||
.layer_layername(active_layer)
|
.layer_layername(active_layer)
|
||||||
.expect("unknown active layer")
|
.expect("unknown active layer")
|
||||||
.to_string();
|
.to_string();
|
||||||
schedule(workspace, |selection| Command::TopoAutoroute {
|
schedule(error_dialog, workspace, |selection| {
|
||||||
selection: selection.pin_selection,
|
Command::TopoAutoroute {
|
||||||
allowed_edges: BTreeSet::new(),
|
selection: selection.pin_selection,
|
||||||
active_layer,
|
allowed_edges: BTreeSet::new(),
|
||||||
routed_band_width: opts.router_options.routed_band_width,
|
active_layer,
|
||||||
|
routed_band_width: opts.router_options.routed_band_width,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if actions.route.autoroute.consume_key_triggered(ctx, ui) {
|
} else if actions.route.autoroute.consume_key_triggered(ctx, ui) {
|
||||||
schedule(workspace, |selection| {
|
schedule(error_dialog, workspace, |selection| {
|
||||||
Command::Autoroute(selection.pin_selection, opts)
|
Command::Autoroute(selection.pin_selection, opts)
|
||||||
});
|
});
|
||||||
} else if actions
|
} else if actions
|
||||||
|
|
@ -341,7 +348,7 @@ impl MenuBar {
|
||||||
.compare_detours
|
.compare_detours
|
||||||
.consume_key_triggered(ctx, ui)
|
.consume_key_triggered(ctx, ui)
|
||||||
{
|
{
|
||||||
schedule(workspace, |selection| {
|
schedule(error_dialog, workspace, |selection| {
|
||||||
Command::CompareDetours(selection.pin_selection, opts)
|
Command::CompareDetours(selection.pin_selection, opts)
|
||||||
});
|
});
|
||||||
} else if actions
|
} else if actions
|
||||||
|
|
@ -349,7 +356,7 @@ impl MenuBar {
|
||||||
.measure_length
|
.measure_length
|
||||||
.consume_key_triggered(ctx, ui)
|
.consume_key_triggered(ctx, ui)
|
||||||
{
|
{
|
||||||
schedule(workspace, |selection| {
|
schedule(error_dialog, workspace, |selection| {
|
||||||
Command::MeasureLength(selection.band_selection)
|
Command::MeasureLength(selection.band_selection)
|
||||||
});
|
});
|
||||||
} else if actions
|
} else if actions
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ pub enum AutorouterError {
|
||||||
Navmesh(#[from] NavmeshError),
|
Navmesh(#[from] NavmeshError),
|
||||||
#[error("routing failed: {0}")]
|
#[error("routing failed: {0}")]
|
||||||
Thetastar(#[from] ThetastarError),
|
Thetastar(#[from] ThetastarError),
|
||||||
#[error(transparent)]
|
#[error("TopoNavmesh generation failed: {0}")]
|
||||||
Spade(#[from] spade::InsertionError),
|
TopoNavmeshGeneration(#[from] ng::NavmeshCalculationError),
|
||||||
#[error("could not place via")]
|
#[error("could not place via")]
|
||||||
CouldNotPlaceVia(#[from] Infringement),
|
CouldNotPlaceVia(#[from] Infringement),
|
||||||
#[error("could not remove band")]
|
#[error("could not remove band")]
|
||||||
|
|
|
||||||
|
|
@ -176,9 +176,9 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
|
||||||
&mut self,
|
&mut self,
|
||||||
command: Command,
|
command: Command,
|
||||||
) -> Result<ExecutionStepper<M>, InvokerError> {
|
) -> Result<ExecutionStepper<M>, InvokerError> {
|
||||||
let execute = self.dispatch_command(&command);
|
let execute = self.dispatch_command(&command)?;
|
||||||
self.ongoing_command = Some(command);
|
self.ongoing_command = Some(command);
|
||||||
execute
|
Ok(execute)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_requires(self.ongoing_command.is_none())]
|
#[debug_requires(self.ongoing_command.is_none())]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use core::iter;
|
||||||
use contracts_try::debug_ensures;
|
use contracts_try::debug_ensures;
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use geo::Point;
|
use geo::{Coord, Line, Point};
|
||||||
use planar_incr_embed::RelaxedPath;
|
use planar_incr_embed::RelaxedPath;
|
||||||
use rstar::AABB;
|
use rstar::AABB;
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ use crate::{
|
||||||
poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight},
|
poly::{add_poly_with_nodes_intern, MakePolygon, PolyWeight},
|
||||||
via::{Via, ViaWeight},
|
via::{Via, ViaWeight},
|
||||||
},
|
},
|
||||||
math::{LineIntersection, NormalLine, RotationSense},
|
math::{intersect_linestring_and_beam, LineIntersection, NormalLine, RotationSense},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a weight for various compounds
|
/// Represents a weight for various compounds
|
||||||
|
|
@ -379,20 +379,12 @@ impl<R: AccessRules> Layout<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: computation of bands between outer node and direction towards "outside"
|
fn bands_between_positions_internal(
|
||||||
|
|
||||||
/// Finds all bands on `layer` between `left` and `right`
|
|
||||||
/// (usually assuming `left` and `right` are neighbors in a Delaunay triangulation)
|
|
||||||
/// and returns them ordered from `left` to `right`.
|
|
||||||
pub fn bands_between_nodes(
|
|
||||||
&self,
|
&self,
|
||||||
layer: usize,
|
layer: usize,
|
||||||
left: NodeIndex,
|
left_pos: Point,
|
||||||
right: NodeIndex,
|
right_pos: Point,
|
||||||
) -> impl Iterator<Item = (BandUid, LooseIndex)> {
|
) -> impl Iterator<Item = (f64, BandUid, LooseIndex)> + '_ {
|
||||||
assert_ne!(left, right);
|
|
||||||
let left_pos = self.node_shape(left).center();
|
|
||||||
let right_pos = self.node_shape(right).center();
|
|
||||||
let ltr_line = geo::Line {
|
let ltr_line = geo::Line {
|
||||||
start: left_pos.into(),
|
start: left_pos.into(),
|
||||||
end: right_pos.into(),
|
end: right_pos.into(),
|
||||||
|
|
@ -406,11 +398,10 @@ impl<R: AccessRules> Layout<R> {
|
||||||
orig_hline.make_normal_unit();
|
orig_hline.make_normal_unit();
|
||||||
let orig_hline = orig_hline;
|
let orig_hline = orig_hline;
|
||||||
let location_denom = orig_hline.segment_interval(<r_line);
|
let location_denom = orig_hline.segment_interval(<r_line);
|
||||||
let location_start = location_denom.start();
|
let location_start = *location_denom.start();
|
||||||
let location_denom = location_denom.end() - location_denom.start();
|
let location_denom = *location_denom.end() - *location_denom.start();
|
||||||
|
|
||||||
let mut bands: Vec<_> = self
|
self.drawing
|
||||||
.drawing
|
|
||||||
.rtree()
|
.rtree()
|
||||||
.locate_in_envelope_intersecting(&{
|
.locate_in_envelope_intersecting(&{
|
||||||
let aabb_init = AABB::from_corners(
|
let aabb_init = AABB::from_corners(
|
||||||
|
|
@ -432,7 +423,7 @@ impl<R: AccessRules> Layout<R> {
|
||||||
let shape = prim.primitive(&self.drawing).shape();
|
let shape = prim.primitive(&self.drawing).shape();
|
||||||
(loose, shape)
|
(loose, shape)
|
||||||
})
|
})
|
||||||
.filter_map(|(loose, shape)| {
|
.filter_map(move |(loose, shape)| {
|
||||||
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
||||||
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
||||||
PrimitiveShape::Seg(seg) => {
|
PrimitiveShape::Seg(seg) => {
|
||||||
|
|
@ -461,6 +452,22 @@ impl<R: AccessRules> Layout<R> {
|
||||||
.contains(&location)
|
.contains(&location)
|
||||||
.then_some((location, band_uid, loose))
|
.then_some((location, band_uid, loose))
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds all bands on `layer` between `left` and `right`
|
||||||
|
/// (usually assuming `left` and `right` are neighbors in a Delaunay triangulation)
|
||||||
|
/// and returns them ordered from `left` to `right`.
|
||||||
|
pub fn bands_between_nodes(
|
||||||
|
&self,
|
||||||
|
layer: usize,
|
||||||
|
left: NodeIndex,
|
||||||
|
right: NodeIndex,
|
||||||
|
) -> impl Iterator<Item = (BandUid, LooseIndex)> {
|
||||||
|
assert_ne!(left, right);
|
||||||
|
let left_pos = self.node_shape(left).center();
|
||||||
|
let right_pos = self.node_shape(right).center();
|
||||||
|
let mut bands: Vec<_> = self
|
||||||
|
.bands_between_positions_internal(layer, left_pos, right_pos)
|
||||||
.filter(|(_, band_uid, _)| {
|
.filter(|(_, band_uid, _)| {
|
||||||
// filter entries which are connected to either lhs or rhs (and possibly both)
|
// filter entries which are connected to either lhs or rhs (and possibly both)
|
||||||
let (bts1, bts2) = band_uid.into();
|
let (bts1, bts2) = band_uid.into();
|
||||||
|
|
@ -482,6 +489,49 @@ impl<R: AccessRules> Layout<R> {
|
||||||
.map(|(_, band_uid, loose)| (band_uid, loose))
|
.map(|(_, band_uid, loose)| (band_uid, loose))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds all bands on `layer` between direction `left` and node `right`
|
||||||
|
/// and returns them ordered from `left` to `right`.
|
||||||
|
pub fn bands_between_node_and_boundary(
|
||||||
|
&self,
|
||||||
|
layer: usize,
|
||||||
|
left: Coord,
|
||||||
|
right: NodeIndex,
|
||||||
|
) -> Option<impl Iterator<Item = (BandUid, LooseIndex)>> {
|
||||||
|
// First, decode the `left` direction into a point on the boundary
|
||||||
|
let right_pos = self.node_shape(right).center();
|
||||||
|
let left_pos = intersect_linestring_and_beam(
|
||||||
|
self.drawing.boundary().exterior(),
|
||||||
|
&Line {
|
||||||
|
start: right_pos.0,
|
||||||
|
end: right_pos.0 + left,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut bands: Vec<_> = self
|
||||||
|
.bands_between_positions_internal(layer, left_pos, right_pos)
|
||||||
|
.filter(|(_, band_uid, _)| {
|
||||||
|
// filter entries which are connected to rhs
|
||||||
|
let (bts1, bts2) = band_uid.into();
|
||||||
|
let (bts1, bts2) = (bts1.petgraph_index(), bts2.petgraph_index());
|
||||||
|
let geometry = self.drawing.geometry();
|
||||||
|
[(bts1, right), (bts2, right)]
|
||||||
|
.iter()
|
||||||
|
.all(|&(x, y)| !geometry.is_joined_with(x, y))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
bands.sort_by(|a, b| f64::total_cmp(&a.0, &b.0));
|
||||||
|
|
||||||
|
// TODO: handle "loops" of bands, or multiple primitives from the band crossing the segment
|
||||||
|
// both in the case of "edge" of a primitive/loose, and in case the band actually goes into a segment
|
||||||
|
// and then again out of it.
|
||||||
|
|
||||||
|
Some(
|
||||||
|
bands
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, band_uid, loose)| (band_uid, loose)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn does_compound_have_core(&self, primary: NodeIndex, core: DotIndex) -> bool {
|
fn does_compound_have_core(&self, primary: NodeIndex, core: DotIndex) -> bool {
|
||||||
let core: PrimitiveIndex = core.into();
|
let core: PrimitiveIndex = core.into();
|
||||||
match primary {
|
match primary {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use geo::algorithm::line_measures::{Distance, Euclidean};
|
use geo::algorithm::line_measures::{Distance, Euclidean};
|
||||||
use geo::{geometry::Point, point, Line};
|
use geo::{point, Line, LineString, Point};
|
||||||
pub use specctra_core::math::{Circle, PointWithRotation};
|
pub use specctra_core::math::{Circle, PointWithRotation};
|
||||||
|
|
||||||
mod cyclic_search;
|
mod cyclic_search;
|
||||||
|
|
@ -102,8 +102,8 @@ impl NormalLine {
|
||||||
let apt = geo::point! { x: a.x, y: a.y };
|
let apt = geo::point! { x: a.x, y: a.y };
|
||||||
let bpt = geo::point! { x: b.x, y: b.y };
|
let bpt = geo::point! { x: b.x, y: b.y };
|
||||||
let det = perp_dot_product(apt, bpt);
|
let det = perp_dot_product(apt, bpt);
|
||||||
let rpx = -b.y * a.offset + a.y * b.offset;
|
let rpx = b.y * a.offset - a.y * b.offset;
|
||||||
let rpy = b.x * a.offset - a.x * b.offset;
|
let rpy = -b.x * a.offset + a.x * b.offset;
|
||||||
|
|
||||||
if det.abs() > ALMOST_ZERO {
|
if det.abs() > ALMOST_ZERO {
|
||||||
LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det)
|
LineIntersection::Point(geo::point! { x: rpx, y: rpy } / det)
|
||||||
|
|
@ -302,6 +302,16 @@ pub fn intersect_line_and_beam(line1: &Line, beam2: &Line) -> Option<Point> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(p)` when `p` lies in the intersection of a linestring and a beam
|
||||||
|
pub fn intersect_linestring_and_beam(linestring: &LineString, beam: &Line) -> Option<Point> {
|
||||||
|
for line in linestring.lines() {
|
||||||
|
if let Some(pt) = intersect_line_and_beam(&line, beam) {
|
||||||
|
return Some(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` the point `p` is between the supporting lines of vectors
|
/// Returns `true` the point `p` is between the supporting lines of vectors
|
||||||
/// `from` and `to`.
|
/// `from` and `to`.
|
||||||
pub fn between_vectors(p: Point, from: Point, to: Point) -> bool {
|
pub fn between_vectors(p: Point, from: Point, to: Point) -> bool {
|
||||||
|
|
@ -446,4 +456,22 @@ mod tests {
|
||||||
None
|
None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn intersect_line_and_beam02() {
|
||||||
|
let pt = intersect_line_and_beam(
|
||||||
|
&Line {
|
||||||
|
start: geo::coord! { x: 140., y: -110. },
|
||||||
|
end: geo::coord! { x: 160., y: -110. },
|
||||||
|
},
|
||||||
|
&Line {
|
||||||
|
start: geo::coord! { x: 148., y: -106. },
|
||||||
|
end: geo::coord! { x: 148., y: -109. },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
approx::assert_abs_diff_eq!(pt.x(), 148.);
|
||||||
|
approx::assert_abs_diff_eq!(pt.y(), -110.);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use pie::{
|
||||||
};
|
};
|
||||||
pub use planar_incr_embed as pie;
|
pub use planar_incr_embed as pie;
|
||||||
|
|
||||||
use geo::geometry::{LineString, Point};
|
use geo::{Coord, LineString, Point};
|
||||||
use rstar::AABB;
|
use rstar::AABB;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
|
|
@ -274,10 +274,27 @@ impl EvalException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
|
pub enum NavmeshCalculationError {
|
||||||
|
#[error("Layer contains too few nodes to generate meaningful navmesh")]
|
||||||
|
NotEnoughNodes,
|
||||||
|
|
||||||
|
#[error("Unable to find boundary from node {node:?}, direction {direction:?}")]
|
||||||
|
UnableToFindBoundary {
|
||||||
|
node: FixedDotIndex,
|
||||||
|
direction: Coord,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Insertion(#[from] spade::InsertionError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NOTE: this only works if the layer has ≥ 3 nodes
|
||||||
|
// TODO: handle the case with 2 nodes on the layer specifically.
|
||||||
pub fn calculate_navmesh<R: AccessRules>(
|
pub fn calculate_navmesh<R: AccessRules>(
|
||||||
board: &Board<R>,
|
board: &Board<R>,
|
||||||
active_layer: usize,
|
active_layer: usize,
|
||||||
) -> Result<PieNavmesh, spade::InsertionError> {
|
) -> Result<PieNavmesh, NavmeshCalculationError> {
|
||||||
use pie::NavmeshIndex::*;
|
use pie::NavmeshIndex::*;
|
||||||
use spade::Triangulation;
|
use spade::Triangulation;
|
||||||
|
|
||||||
|
|
@ -302,37 +319,142 @@ pub fn calculate_navmesh<R: AccessRules>(
|
||||||
.collect(),
|
.collect(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if triangulation.num_inner_faces() == 0 {
|
||||||
|
log::warn!("calculate_navmesh: not enough nodes");
|
||||||
|
return Err(NavmeshCalculationError::NotEnoughNodes);
|
||||||
|
}
|
||||||
|
|
||||||
let mut navmesh = navmesh::NavmeshSer::<PieNavmeshBase>::from_triangulation(&triangulation);
|
let mut navmesh = navmesh::NavmeshSer::<PieNavmeshBase>::from_triangulation(&triangulation);
|
||||||
|
|
||||||
let barrier2: Arc<[RelaxedPath<_, _>]> =
|
let barrier2: Arc<[RelaxedPath<_, _>]> =
|
||||||
Arc::from(vec![RelaxedPath::Weak(()), RelaxedPath::Weak(())]);
|
Arc::from(vec![RelaxedPath::Weak(()), RelaxedPath::Weak(())]);
|
||||||
|
|
||||||
// populate DualInner-Dual* routed traces
|
let barrier0: Arc<[RelaxedPath<_, _>]> = Arc::from(vec![]);
|
||||||
for value in navmesh.edges.values_mut() {
|
|
||||||
if let (Some(lhs), Some(rhs)) = (value.0.lhs, value.0.rhs) {
|
|
||||||
value.1 = barrier2.clone();
|
|
||||||
let wrap = |dot| GenericNode::Primitive(PrimitiveIndex::FixedDot(dot));
|
|
||||||
let bands = board
|
|
||||||
.layout()
|
|
||||||
.bands_between_nodes_with_alignment(active_layer, wrap(lhs), wrap(rhs))
|
|
||||||
.map(|i| match i {
|
|
||||||
RelaxedPath::Weak(()) => RelaxedPath::Weak(()),
|
|
||||||
RelaxedPath::Normal(band_uid) => {
|
|
||||||
RelaxedPath::Normal(*board.bands_by_id().get_by_right(&band_uid).unwrap())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if bands != *barrier2 {
|
log::debug!("boundary = {:?}", board.layout().drawing().boundary());
|
||||||
log::debug!("navmesh generated with {:?} = {:?}", value, &bands);
|
|
||||||
value.1 = Arc::from(bands);
|
// populate Dual*-Dual* routed traces
|
||||||
|
for (key, value) in &mut navmesh.edges {
|
||||||
|
let wrap = |dot| GenericNode::Primitive(PrimitiveIndex::FixedDot(dot));
|
||||||
|
match (value.0.lhs, value.0.rhs) {
|
||||||
|
(Some(lhs), Some(rhs)) => {
|
||||||
|
value.1 = barrier2.clone();
|
||||||
|
let bands = board
|
||||||
|
.layout()
|
||||||
|
.bands_between_nodes_with_alignment(active_layer, wrap(lhs), wrap(rhs))
|
||||||
|
.map(|i| match i {
|
||||||
|
RelaxedPath::Weak(()) => RelaxedPath::Weak(()),
|
||||||
|
RelaxedPath::Normal(band_uid) => RelaxedPath::Normal(
|
||||||
|
*board.bands_by_id().get_by_right(&band_uid).unwrap(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if bands != *barrier2 {
|
||||||
|
log::debug!("navmesh generated with {:?} = {:?}", value, &bands);
|
||||||
|
value.1 = Arc::from(bands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(rhs)) => {
|
||||||
|
value.1 = barrier0.clone();
|
||||||
|
let direction = {
|
||||||
|
let (prev_key, next_key) = key.into();
|
||||||
|
let prev_dir = navmesh.nodes[prev_key]
|
||||||
|
.open_direction
|
||||||
|
.expect("expected DualOuter entry");
|
||||||
|
let next_dir = navmesh.nodes[next_key]
|
||||||
|
.open_direction
|
||||||
|
.expect("expected DualOuter entry");
|
||||||
|
Coord {
|
||||||
|
x: (prev_dir.x + next_dir.x) / 2.0,
|
||||||
|
y: (prev_dir.y + next_dir.y) / 2.0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let bands = match board.layout().bands_between_node_and_boundary(
|
||||||
|
active_layer,
|
||||||
|
direction,
|
||||||
|
wrap(rhs),
|
||||||
|
) {
|
||||||
|
None => {
|
||||||
|
log::warn!("calculate_navmesh: unable to find boundary from node {:?}, direction {:?}", rhs, direction);
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
return Err(NavmeshCalculationError::UnableToFindBoundary {
|
||||||
|
node: rhs,
|
||||||
|
direction,
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
Some(x) => {
|
||||||
|
log::debug!("calculate_navmesh: successfully found boundary from node {:?}, direction {:?}", rhs, direction);
|
||||||
|
x.map(|(band_uid, _)| {
|
||||||
|
RelaxedPath::Normal(
|
||||||
|
*board.bands_by_id().get_by_right(&band_uid).unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if bands != *barrier0 {
|
||||||
|
log::debug!("navmesh generated with {:?} = {:?}", value, &bands);
|
||||||
|
value.1 = Arc::from(bands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(lhs), None) => {
|
||||||
|
value.1 = barrier0.clone();
|
||||||
|
let direction = {
|
||||||
|
let (prev_key, next_key) = key.into();
|
||||||
|
let prev_dir = navmesh.nodes[prev_key]
|
||||||
|
.open_direction
|
||||||
|
.expect("expected DualOuter entry");
|
||||||
|
let next_dir = navmesh.nodes[next_key]
|
||||||
|
.open_direction
|
||||||
|
.expect("expected DualOuter entry");
|
||||||
|
Coord {
|
||||||
|
x: (prev_dir.x + next_dir.x) / 2.0,
|
||||||
|
y: (prev_dir.y + next_dir.y) / 2.0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut bands = match board.layout().bands_between_node_and_boundary(
|
||||||
|
active_layer,
|
||||||
|
direction,
|
||||||
|
wrap(lhs),
|
||||||
|
) {
|
||||||
|
None => {
|
||||||
|
log::warn!("calculate_navmesh: unable to find boundary from node {:?}, direction {:?}", lhs, direction);
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
return Err(NavmeshCalculationError::UnableToFindBoundary {
|
||||||
|
node: rhs,
|
||||||
|
direction,
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
Some(x) => {
|
||||||
|
log::debug!("calculate_navmesh: successfully found boundary from node {:?}, direction {:?}", lhs, direction);
|
||||||
|
x.map(|(band_uid, _)| {
|
||||||
|
RelaxedPath::Normal(
|
||||||
|
*board.bands_by_id().get_by_right(&band_uid).unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
bands.reverse();
|
||||||
|
|
||||||
|
if bands != *barrier0 {
|
||||||
|
log::debug!("navmesh generated with {:?} = {:?}", value, &bands);
|
||||||
|
value.1 = Arc::from(bands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, None) => {
|
||||||
|
// nothing to do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: insert fixed and outer routed traces/bands into the navmesh
|
|
||||||
// see also: https://codeberg.org/topola/topola/issues/166
|
// TODO: insert fixed routed traces/bands into the navmesh
|
||||||
// due to not handling outer routed traces/bends,
|
|
||||||
// the above code might produce an inconsistent navmesh
|
|
||||||
|
|
||||||
// populate Primal-Dual* routed traces
|
// populate Primal-Dual* routed traces
|
||||||
let dual_ends: BTreeMap<_, _> = navmesh
|
let dual_ends: BTreeMap<_, _> = navmesh
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue