mirror of https://codeberg.org/topola/topola.git
refactor(layout/layout): Put bands_between_* methods into separate file collect_bands.rs
This commit is contained in:
parent
d3dc826be4
commit
59473c36c8
|
|
@ -56,6 +56,7 @@ allowed_scopes = [
|
||||||
"interactor/interaction",
|
"interactor/interaction",
|
||||||
"interactor/interactor",
|
"interactor/interactor",
|
||||||
"interactor/route_plan",
|
"interactor/route_plan",
|
||||||
|
"layout/collect_bands",
|
||||||
"layout/layout",
|
"layout/layout",
|
||||||
"layout/poly",
|
"layout/poly",
|
||||||
"layout/via",
|
"layout/via",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
|
use geo::{Coord, Line, Point};
|
||||||
|
use planar_incr_embed::RelaxedPath;
|
||||||
|
use rstar::AABB;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
drawing::{
|
||||||
|
band::BandUid,
|
||||||
|
dot::DotIndex,
|
||||||
|
graph::{MakePrimitive, PrimitiveIndex},
|
||||||
|
loose::LooseIndex,
|
||||||
|
primitive::MakePrimitiveShape,
|
||||||
|
rules::AccessRules,
|
||||||
|
Collect,
|
||||||
|
},
|
||||||
|
geometry::{
|
||||||
|
compound::ManageCompounds,
|
||||||
|
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
|
||||||
|
shape::AccessShape,
|
||||||
|
GenericNode,
|
||||||
|
},
|
||||||
|
graph::GetPetgraphIndex,
|
||||||
|
layout::{Layout, NodeIndex},
|
||||||
|
math::{intersect_linestring_and_beam, LineIntersection, NormalLine},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<R: AccessRules> Layout<R> {
|
||||||
|
fn bands_between_positions(
|
||||||
|
&self,
|
||||||
|
layer: usize,
|
||||||
|
left_pos: Point,
|
||||||
|
right_pos: Point,
|
||||||
|
) -> impl Iterator<Item = (f64, BandUid, LooseIndex)> + '_ {
|
||||||
|
let ltr_line = geo::Line {
|
||||||
|
start: left_pos.into(),
|
||||||
|
end: right_pos.into(),
|
||||||
|
};
|
||||||
|
let fake_seg = SegShape {
|
||||||
|
from: left_pos,
|
||||||
|
to: right_pos,
|
||||||
|
width: f64::EPSILON * 16.0,
|
||||||
|
};
|
||||||
|
let mut orig_hline = NormalLine::from(ltr_line);
|
||||||
|
orig_hline.make_normal_unit();
|
||||||
|
let orig_hline = orig_hline;
|
||||||
|
let location_denom = orig_hline.segment_interval(<r_line);
|
||||||
|
let location_start = *location_denom.start();
|
||||||
|
let location_denom = *location_denom.end() - *location_denom.start();
|
||||||
|
|
||||||
|
self.drawing
|
||||||
|
.rtree()
|
||||||
|
.locate_in_envelope_intersecting(&{
|
||||||
|
let aabb_init = AABB::from_corners(
|
||||||
|
[left_pos.x(), left_pos.y()],
|
||||||
|
[right_pos.x(), right_pos.y()],
|
||||||
|
);
|
||||||
|
AABB::from_corners(
|
||||||
|
[aabb_init.lower()[0], aabb_init.lower()[1], layer as f64],
|
||||||
|
[aabb_init.upper()[0], aabb_init.upper()[1], layer as f64],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// TODO: handle non-loose entries (bends, segs)
|
||||||
|
.filter_map(|geom| match geom.data {
|
||||||
|
NodeIndex::Primitive(prim) => LooseIndex::try_from(prim).ok(),
|
||||||
|
NodeIndex::Compound(_) => None,
|
||||||
|
})
|
||||||
|
.map(|loose| {
|
||||||
|
let prim: PrimitiveIndex = loose.into();
|
||||||
|
let shape = prim.primitive(&self.drawing).shape();
|
||||||
|
(loose, shape)
|
||||||
|
})
|
||||||
|
.filter_map(move |(loose, shape)| {
|
||||||
|
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
||||||
|
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
||||||
|
PrimitiveShape::Seg(seg) => {
|
||||||
|
let seg_hline = NormalLine::from(seg.middle_line());
|
||||||
|
match orig_hline.intersects(&seg_hline) {
|
||||||
|
LineIntersection::Empty => return None,
|
||||||
|
LineIntersection::Overlapping => shape.center(),
|
||||||
|
LineIntersection::Point(pt) => pt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if !fake_seg.intersects(&shape) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
shape.center()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let location = (loose_hline.offset - location_start) / location_denom;
|
||||||
|
log::trace!(
|
||||||
|
"intersection ({:?}) with {:?} is at {:?}",
|
||||||
|
band_uid,
|
||||||
|
shape,
|
||||||
|
location
|
||||||
|
);
|
||||||
|
(0.0..=1.0)
|
||||||
|
.contains(&location)
|
||||||
|
.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(layer, left_pos, right_pos)
|
||||||
|
.filter(|(_, band_uid, _)| {
|
||||||
|
// filter entries which are connected to either lhs or rhs (and possibly both)
|
||||||
|
let (bts1, bts2) = band_uid.into();
|
||||||
|
let (bts1, bts2) = (bts1.petgraph_index(), bts2.petgraph_index());
|
||||||
|
let geometry = self.drawing.geometry();
|
||||||
|
[(bts1, left), (bts1, right), (bts2, left), (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.
|
||||||
|
|
||||||
|
bands
|
||||||
|
.into_iter()
|
||||||
|
.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(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 {
|
||||||
|
let core: PrimitiveIndex = core.into();
|
||||||
|
match primary {
|
||||||
|
GenericNode::Primitive(pi) => pi == core,
|
||||||
|
GenericNode::Compound(compound) => self
|
||||||
|
.drawing
|
||||||
|
.geometry()
|
||||||
|
.compound_members(compound)
|
||||||
|
.any(|(_, pi)| pi == core),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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_with_alignment(
|
||||||
|
&self,
|
||||||
|
layer: usize,
|
||||||
|
left: NodeIndex,
|
||||||
|
right: NodeIndex,
|
||||||
|
) -> impl Iterator<Item = RelaxedPath<BandUid, ()>> + '_ {
|
||||||
|
let mut alignment_idx: u8 = 0;
|
||||||
|
|
||||||
|
// resolve end-points possibly to compounds such that
|
||||||
|
// `does_compound_have_core` produces correct results.
|
||||||
|
let maybe_to_compound = |x: NodeIndex| match x {
|
||||||
|
GenericNode::Primitive(pi) => self
|
||||||
|
.drawing
|
||||||
|
.geometry()
|
||||||
|
.compounds(pi)
|
||||||
|
.next()
|
||||||
|
.map(|(_, idx)| idx),
|
||||||
|
GenericNode::Compound(compound) => Some(compound),
|
||||||
|
};
|
||||||
|
let resolve_node =
|
||||||
|
|x: NodeIndex| maybe_to_compound(x).map(GenericNode::Compound).unwrap_or(x);
|
||||||
|
let (left, right) = (resolve_node(left), resolve_node(right));
|
||||||
|
|
||||||
|
self.bands_between_nodes(layer, left, right)
|
||||||
|
.map(move |(band_uid, loose)| {
|
||||||
|
// first, tag entry with core
|
||||||
|
let maybe_core = match loose {
|
||||||
|
LooseIndex::Bend(lbi) => Some(self.drawing.geometry().core(lbi.into())),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
Some((band_uid, maybe_core))
|
||||||
|
})
|
||||||
|
.chain(iter::once(None))
|
||||||
|
.flat_map(move |item| {
|
||||||
|
// insert alignment pseudo-paths if necessary
|
||||||
|
let alignment_incr = match item {
|
||||||
|
Some((_, maybe_core)) => match (alignment_idx, maybe_core) {
|
||||||
|
(0, Some(core)) if self.does_compound_have_core(left, core) => 0,
|
||||||
|
(_, Some(core)) if self.does_compound_have_core(left, core) => {
|
||||||
|
panic!("invalid band ordering")
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, Some(core)) if self.does_compound_have_core(right, core) => 2u8
|
||||||
|
.checked_sub(alignment_idx)
|
||||||
|
.expect("invalid band ordering"),
|
||||||
|
|
||||||
|
(0, _) => 1,
|
||||||
|
(1, _) => 0,
|
||||||
|
_ => panic!("invalid band ordering"),
|
||||||
|
},
|
||||||
|
None => 2u8
|
||||||
|
.checked_sub(alignment_idx)
|
||||||
|
.expect("invalid band ordering"),
|
||||||
|
};
|
||||||
|
|
||||||
|
alignment_idx += alignment_incr;
|
||||||
|
|
||||||
|
iter::repeat_n(RelaxedPath::Weak(()), alignment_incr.into()).chain(
|
||||||
|
item.map(|(band_uid, _)| RelaxedPath::Normal(band_uid))
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,18 +2,15 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
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::{Coord, Line, Point};
|
use geo::Point;
|
||||||
use planar_incr_embed::RelaxedPath;
|
|
||||||
use rstar::AABB;
|
use rstar::AABB;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing::{
|
drawing::{
|
||||||
band::{BandTermsegIndex, BandUid},
|
band::BandTermsegIndex,
|
||||||
bend::{BendIndex, BendWeight, LooseBendWeight},
|
bend::{BendIndex, BendWeight, LooseBendWeight},
|
||||||
dot::{
|
dot::{
|
||||||
DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight, LooseDotIndex,
|
DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight, LooseDotIndex,
|
||||||
|
|
@ -21,19 +18,17 @@ use crate::{
|
||||||
},
|
},
|
||||||
gear::GearIndex,
|
gear::GearIndex,
|
||||||
graph::{GetMaybeNet, IsInLayer, MakePrimitive, PrimitiveIndex, PrimitiveWeight},
|
graph::{GetMaybeNet, IsInLayer, MakePrimitive, PrimitiveIndex, PrimitiveWeight},
|
||||||
loose::LooseIndex,
|
|
||||||
primitive::MakePrimitiveShape,
|
primitive::MakePrimitiveShape,
|
||||||
rules::AccessRules,
|
rules::AccessRules,
|
||||||
seg::{
|
seg::{
|
||||||
FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SegIndex,
|
FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SegIndex,
|
||||||
SegWeight, SeqLooseSegIndex, SeqLooseSegWeight,
|
SegWeight, SeqLooseSegIndex, SeqLooseSegWeight,
|
||||||
},
|
},
|
||||||
Cane, Collect, Drawing, DrawingEdit, DrawingException, Infringement,
|
Cane, Drawing, DrawingEdit, DrawingException, Infringement,
|
||||||
},
|
},
|
||||||
geometry::{
|
geometry::{
|
||||||
compound::ManageCompounds,
|
compound::ManageCompounds,
|
||||||
edit::ApplyGeometryEdit,
|
edit::ApplyGeometryEdit,
|
||||||
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
|
|
||||||
shape::{AccessShape, Shape},
|
shape::{AccessShape, Shape},
|
||||||
GenericNode, GetLayer, GetSetPos,
|
GenericNode, GetLayer, GetSetPos,
|
||||||
},
|
},
|
||||||
|
|
@ -42,7 +37,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::{intersect_linestring_and_beam, LineIntersection, NormalLine, RotationSense},
|
math::RotationSense,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a weight for various compounds
|
/// Represents a weight for various compounds
|
||||||
|
|
@ -379,238 +374,6 @@ impl<R: AccessRules> Layout<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bands_between_positions_internal(
|
|
||||||
&self,
|
|
||||||
layer: usize,
|
|
||||||
left_pos: Point,
|
|
||||||
right_pos: Point,
|
|
||||||
) -> impl Iterator<Item = (f64, BandUid, LooseIndex)> + '_ {
|
|
||||||
let ltr_line = geo::Line {
|
|
||||||
start: left_pos.into(),
|
|
||||||
end: right_pos.into(),
|
|
||||||
};
|
|
||||||
let fake_seg = SegShape {
|
|
||||||
from: left_pos,
|
|
||||||
to: right_pos,
|
|
||||||
width: f64::EPSILON * 16.0,
|
|
||||||
};
|
|
||||||
let mut orig_hline = NormalLine::from(ltr_line);
|
|
||||||
orig_hline.make_normal_unit();
|
|
||||||
let orig_hline = orig_hline;
|
|
||||||
let location_denom = orig_hline.segment_interval(<r_line);
|
|
||||||
let location_start = *location_denom.start();
|
|
||||||
let location_denom = *location_denom.end() - *location_denom.start();
|
|
||||||
|
|
||||||
self.drawing
|
|
||||||
.rtree()
|
|
||||||
.locate_in_envelope_intersecting(&{
|
|
||||||
let aabb_init = AABB::from_corners(
|
|
||||||
[left_pos.x(), left_pos.y()],
|
|
||||||
[right_pos.x(), right_pos.y()],
|
|
||||||
);
|
|
||||||
AABB::from_corners(
|
|
||||||
[aabb_init.lower()[0], aabb_init.lower()[1], layer as f64],
|
|
||||||
[aabb_init.upper()[0], aabb_init.upper()[1], layer as f64],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
// TODO: handle non-loose entries (bends, segs)
|
|
||||||
.filter_map(|geom| match geom.data {
|
|
||||||
NodeIndex::Primitive(prim) => LooseIndex::try_from(prim).ok(),
|
|
||||||
NodeIndex::Compound(_) => None,
|
|
||||||
})
|
|
||||||
.map(|loose| {
|
|
||||||
let prim: PrimitiveIndex = loose.into();
|
|
||||||
let shape = prim.primitive(&self.drawing).shape();
|
|
||||||
(loose, shape)
|
|
||||||
})
|
|
||||||
.filter_map(move |(loose, shape)| {
|
|
||||||
let band_uid = self.drawing.loose_band_uid(loose).ok()?;
|
|
||||||
let loose_hline = orig_hline.orthogonal_through(&match shape {
|
|
||||||
PrimitiveShape::Seg(seg) => {
|
|
||||||
let seg_hline = NormalLine::from(seg.middle_line());
|
|
||||||
match orig_hline.intersects(&seg_hline) {
|
|
||||||
LineIntersection::Empty => return None,
|
|
||||||
LineIntersection::Overlapping => shape.center(),
|
|
||||||
LineIntersection::Point(pt) => pt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if !fake_seg.intersects(&shape) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
shape.center()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let location = (loose_hline.offset - location_start) / location_denom;
|
|
||||||
log::trace!(
|
|
||||||
"intersection ({:?}) with {:?} is at {:?}",
|
|
||||||
band_uid,
|
|
||||||
shape,
|
|
||||||
location
|
|
||||||
);
|
|
||||||
(0.0..=1.0)
|
|
||||||
.contains(&location)
|
|
||||||
.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 entries which are connected to either lhs or rhs (and possibly both)
|
|
||||||
let (bts1, bts2) = band_uid.into();
|
|
||||||
let (bts1, bts2) = (bts1.petgraph_index(), bts2.petgraph_index());
|
|
||||||
let geometry = self.drawing.geometry();
|
|
||||||
[(bts1, left), (bts1, right), (bts2, left), (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.
|
|
||||||
|
|
||||||
bands
|
|
||||||
.into_iter()
|
|
||||||
.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 {
|
|
||||||
let core: PrimitiveIndex = core.into();
|
|
||||||
match primary {
|
|
||||||
GenericNode::Primitive(pi) => pi == core,
|
|
||||||
GenericNode::Compound(compound) => self
|
|
||||||
.drawing
|
|
||||||
.geometry()
|
|
||||||
.compound_members(compound)
|
|
||||||
.any(|(_, pi)| pi == core),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_with_alignment(
|
|
||||||
&self,
|
|
||||||
layer: usize,
|
|
||||||
left: NodeIndex,
|
|
||||||
right: NodeIndex,
|
|
||||||
) -> impl Iterator<Item = RelaxedPath<BandUid, ()>> + '_ {
|
|
||||||
let mut alignment_idx: u8 = 0;
|
|
||||||
|
|
||||||
// resolve end-points possibly to compounds such that
|
|
||||||
// `does_compound_have_core` produces correct results.
|
|
||||||
let maybe_to_compound = |x: NodeIndex| match x {
|
|
||||||
GenericNode::Primitive(pi) => self
|
|
||||||
.drawing
|
|
||||||
.geometry()
|
|
||||||
.compounds(pi)
|
|
||||||
.next()
|
|
||||||
.map(|(_, idx)| idx),
|
|
||||||
GenericNode::Compound(compound) => Some(compound),
|
|
||||||
};
|
|
||||||
let resolve_node =
|
|
||||||
|x: NodeIndex| maybe_to_compound(x).map(GenericNode::Compound).unwrap_or(x);
|
|
||||||
let (left, right) = (resolve_node(left), resolve_node(right));
|
|
||||||
|
|
||||||
self.bands_between_nodes(layer, left, right)
|
|
||||||
.map(move |(band_uid, loose)| {
|
|
||||||
// first, tag entry with core
|
|
||||||
let maybe_core = match loose {
|
|
||||||
LooseIndex::Bend(lbi) => Some(self.drawing.geometry().core(lbi.into())),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
Some((band_uid, maybe_core))
|
|
||||||
})
|
|
||||||
.chain(iter::once(None))
|
|
||||||
.flat_map(move |item| {
|
|
||||||
// insert alignment pseudo-paths if necessary
|
|
||||||
let alignment_incr = match item {
|
|
||||||
Some((_, maybe_core)) => match (alignment_idx, maybe_core) {
|
|
||||||
(0, Some(core)) if self.does_compound_have_core(left, core) => 0,
|
|
||||||
(_, Some(core)) if self.does_compound_have_core(left, core) => {
|
|
||||||
panic!("invalid band ordering")
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, Some(core)) if self.does_compound_have_core(right, core) => 2u8
|
|
||||||
.checked_sub(alignment_idx)
|
|
||||||
.expect("invalid band ordering"),
|
|
||||||
|
|
||||||
(0, _) => 1,
|
|
||||||
(1, _) => 0,
|
|
||||||
_ => panic!("invalid band ordering"),
|
|
||||||
},
|
|
||||||
None => 2u8
|
|
||||||
.checked_sub(alignment_idx)
|
|
||||||
.expect("invalid band ordering"),
|
|
||||||
};
|
|
||||||
|
|
||||||
alignment_idx += alignment_incr;
|
|
||||||
|
|
||||||
iter::repeat_n(RelaxedPath::Weak(()), alignment_incr.into()).chain(
|
|
||||||
item.map(|(band_uid, _)| RelaxedPath::Normal(band_uid))
|
|
||||||
.into_iter(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rules(&self) -> &R {
|
pub fn rules(&self) -> &R {
|
||||||
self.drawing.rules()
|
self.drawing.rules()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
//! Layout module for handling board geometry.
|
//! Layout module for handling board geometry.
|
||||||
|
|
||||||
|
mod collect_bands;
|
||||||
mod layout;
|
mod layout;
|
||||||
pub mod poly;
|
pub mod poly;
|
||||||
pub mod via;
|
pub mod via;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue