feat(layout): implement extraction of band-between-nodes information

This commit is contained in:
Ellen Emilia Anna Zscheile 2025-01-30 16:39:24 +01:00
parent 3c3c1d9b42
commit e7b120e8ed
3 changed files with 85 additions and 10 deletions

View File

@ -62,13 +62,7 @@ impl<'a> ResolvedSelector<'a> {
let (layer, loose) = match node { let (layer, loose) = match node {
NodeIndex::Primitive(primitive) => ( NodeIndex::Primitive(primitive) => (
primitive.primitive(board.layout().drawing()).layer(), primitive.primitive(board.layout().drawing()).layer(),
match primitive { primitive.try_into().ok(),
PrimitiveIndex::LooseDot(dot) => Some(dot.into()),
PrimitiveIndex::LoneLooseSeg(seg) => Some(seg.into()),
PrimitiveIndex::SeqLooseSeg(seg) => Some(seg.into()),
PrimitiveIndex::LooseBend(bend) => Some(bend.into()),
_ => None,
},
), ),
NodeIndex::Compound(compound) => { NodeIndex::Compound(compound) => {
match board.layout().drawing().compound_weight(compound) { match board.layout().drawing().compound_weight(compound) {

View File

@ -57,6 +57,19 @@ impl From<LooseIndex> for PrimitiveIndex {
} }
} }
impl TryFrom<PrimitiveIndex> for LooseIndex {
type Error = ();
fn try_from(primitive: PrimitiveIndex) -> Result<LooseIndex, ()> {
match primitive {
PrimitiveIndex::LooseDot(dot) => Ok(dot.into()),
PrimitiveIndex::LoneLooseSeg(seg) => Ok(seg.into()),
PrimitiveIndex::SeqLooseSeg(seg) => Ok(seg.into()),
PrimitiveIndex::LooseBend(bend) => Ok(bend.into()),
_ => Err(()),
}
}
}
#[enum_dispatch(GetPrevNextLoose, GetDrawing, GetPetgraphIndex)] #[enum_dispatch(GetPrevNextLoose, GetDrawing, GetPetgraphIndex)]
pub enum Loose<'a, CW: Copy, R: AccessRules> { pub enum Loose<'a, CW: Copy, R: AccessRules> {
Dot(LooseDot<'a, CW, R>), Dot(LooseDot<'a, CW, R>),

View File

@ -10,7 +10,7 @@ use rstar::AABB;
use crate::{ use crate::{
drawing::{ drawing::{
band::BandTermsegIndex, band::{BandTermsegIndex, BandUid},
bend::{BendIndex, BendWeight, LooseBendWeight}, bend::{BendIndex, BendWeight, LooseBendWeight},
dot::{ dot::{
DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight, LooseDotIndex, DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight, LooseDotIndex,
@ -18,15 +18,21 @@ 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, Drawing, DrawingEdit, DrawingException, Infringement, Cane, Collect, Drawing, DrawingEdit, DrawingException, Infringement,
},
geometry::{
edit::ApplyGeometryEdit,
primitive::{AccessPrimitiveShape, PrimitiveShape, SegShape},
shape::{AccessShape, Shape},
GenericNode,
}, },
geometry::{edit::ApplyGeometryEdit, shape::Shape, GenericNode},
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
layout::{ layout::{
poly::{MakePolygon, Poly, PolyWeight}, poly::{MakePolygon, Poly, PolyWeight},
@ -367,6 +373,68 @@ impl<R: AccessRules> Layout<R> {
} }
} }
/// 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,
) -> Vec<BandUid> {
assert_ne!(left, right);
let left_pos = self.node_shape(left).center();
let right_pos = self.node_shape(right).center();
let delta = right_pos - left_pos;
let delta_len = delta.y().hypot(delta.x());
let fake_seg = PrimitiveShape::Seg(SegShape {
from: left_pos,
to: right_pos,
// TODO: being able to use a zero-width segshape here would be optimal
// check if that works
width: f64::EPSILON,
});
let mut bands: Vec<_> = 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();
(prim, loose, shape)
})
.filter(|(_, _, shape)| shape.intersects(&fake_seg))
.map(|(_, loose, shape)| {
let band_uid = self.drawing.loose_band_uid(loose);
// TODO: check that the resulting order is correct
let location = crate::math::dot_product(delta, shape.center()) / delta_len;
(location, band_uid)
})
.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)| band_uid).collect()
}
pub fn rules(&self) -> &R { pub fn rules(&self) -> &R {
self.drawing.rules() self.drawing.rules()
} }