From e7b120e8ed7f7402e65582c081985d2e3708dcc2 Mon Sep 17 00:00:00 2001 From: Ellen Emilia Anna Zscheile Date: Thu, 30 Jan 2025 16:39:24 +0100 Subject: [PATCH] feat(layout): implement extraction of band-between-nodes information --- src/board/mod.rs | 8 +---- src/drawing/loose.rs | 13 ++++++++ src/layout/layout.rs | 74 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/board/mod.rs b/src/board/mod.rs index 3cb6bdc..f352ad4 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -62,13 +62,7 @@ impl<'a> ResolvedSelector<'a> { let (layer, loose) = match node { NodeIndex::Primitive(primitive) => ( primitive.primitive(board.layout().drawing()).layer(), - match primitive { - 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, - }, + primitive.try_into().ok(), ), NodeIndex::Compound(compound) => { match board.layout().drawing().compound_weight(compound) { diff --git a/src/drawing/loose.rs b/src/drawing/loose.rs index 0003891..4bb2235 100644 --- a/src/drawing/loose.rs +++ b/src/drawing/loose.rs @@ -57,6 +57,19 @@ impl From for PrimitiveIndex { } } +impl TryFrom for LooseIndex { + type Error = (); + fn try_from(primitive: PrimitiveIndex) -> Result { + 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)] pub enum Loose<'a, CW: Copy, R: AccessRules> { Dot(LooseDot<'a, CW, R>), diff --git a/src/layout/layout.rs b/src/layout/layout.rs index 67efdec..644dc04 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -10,7 +10,7 @@ use rstar::AABB; use crate::{ drawing::{ - band::BandTermsegIndex, + band::{BandTermsegIndex, BandUid}, bend::{BendIndex, BendWeight, LooseBendWeight}, dot::{ DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, GeneralDotWeight, LooseDotIndex, @@ -18,15 +18,21 @@ use crate::{ }, gear::GearIndex, graph::{GetMaybeNet, IsInLayer, MakePrimitive, PrimitiveIndex, PrimitiveWeight}, + loose::LooseIndex, primitive::MakePrimitiveShape, rules::AccessRules, seg::{ FixedSegIndex, FixedSegWeight, LoneLooseSegIndex, LoneLooseSegWeight, SegIndex, 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}, layout::{ poly::{MakePolygon, Poly, PolyWeight}, @@ -367,6 +373,68 @@ impl Layout { } } + /// 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 { + 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 { self.drawing.rules() }