From 41438eecccc6e0f662b162c435b392c1fd183865 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Tue, 26 Aug 2025 00:23:48 +0200 Subject: [PATCH] feat(drawing/gear): Implement finding next and previous gear in chain for fixed dots --- src/drawing/gear.rs | 25 +++++++++++++- src/drawing/primitive.rs | 31 +++++++++++++++++ src/drawing/query.rs | 72 ++++++++++++++++++++++++++++++---------- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/drawing/gear.rs b/src/drawing/gear.rs index 9ca93e1..f1eaafb 100644 --- a/src/drawing/gear.rs +++ b/src/drawing/gear.rs @@ -53,7 +53,7 @@ impl From for GearIndex { } } -#[enum_dispatch(WalkOutwards, GetOuterGears, GetDrawing, GetPetgraphIndex)] +#[enum_dispatch(GetOuterGears, WalkOutwards, GetDrawing, GetPetgraphIndex)] pub enum GearRef<'a, CW, Cel, R> { FixedDot(FixedDot<'a, CW, Cel, R>), FixedBend(FixedBend<'a, CW, Cel, R>), @@ -83,6 +83,29 @@ pub trait WalkOutwards { fn outwards(&self) -> DrawingOutwardWalker; } +//#[enum_dispatch] +pub trait GetPrevNextInChain { + fn next_in_chain(&self, maybe_prev: Option) -> Option; + + fn prev_in_chain(&self, maybe_next: Option) -> Option { + // Just as in the `GetPrevNextLoose` trait. + let maybe_prev = maybe_next.or_else(|| self.next_in_chain(None)); + self.next_in_chain(maybe_prev) + } +} + +// Because types have trait bounds, we cannot use enum_dispatch and instead we +// implement `GetPrevNextInChain` explicitly. +impl<'a, CW: Clone, Cel: Copy, R: AccessRules> GetPrevNextInChain for GearRef<'a, CW, Cel, R> { + fn next_in_chain(&self, maybe_prev: Option) -> Option { + match self { + GearRef::FixedDot(dot) => dot.next_in_chain(maybe_prev), + GearRef::FixedBend(bend) => bend.next_in_chain(maybe_prev), + GearRef::LooseBend(bend) => bend.next_in_chain(maybe_prev), + } + } +} + /// I found it easier to just duplicate `OutwardWalker` for `Drawing<...>`. pub struct DrawingOutwardWalker { frontier: VecDeque, diff --git a/src/drawing/primitive.rs b/src/drawing/primitive.rs index 4d7f79f..9d4be8c 100644 --- a/src/drawing/primitive.rs +++ b/src/drawing/primitive.rs @@ -9,6 +9,7 @@ use crate::{ drawing::{ bend::{BendIndex, FixedBendWeight, LooseBendIndex, LooseBendWeight}, dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, LooseDotIndex, LooseDotWeight}, + gear::{GearIndex, GetPrevNextInChain}, graph::{GetMaybeNet, PrimitiveIndex, PrimitiveWeight}, rules::{AccessRules, Conditions, GetConditions}, seg::{FixedSegWeight, LoneLooseSegWeight, SegIndex, SeqLooseSegIndex, SeqLooseSegWeight}, @@ -291,6 +292,24 @@ impl GetOuterGears for FixedDot<'_, CW, Cel, R> { } } +impl GetPrevNextInChain for FixedDot<'_, CW, Cel, R> { + fn next_in_chain(&self, maybe_prev: Option) -> Option { + self.drawing + .clearance_intersectors(self.index.into()) + .find_map(|infringement| { + let PrimitiveIndex::FixedDot(intersectee) = infringement.1 else { + return None; + }; + + if let Some(prev) = maybe_prev { + (infringement.1 == prev.into()).then_some(intersectee.into()) + } else { + Some(intersectee.into()) + } + }) + } +} + impl WalkOutwards for FixedDot<'_, CW, Cel, R> { fn outwards(&self) -> DrawingOutwardWalker { DrawingOutwardWalker::new(self.lowest_gears().into_iter()) @@ -457,6 +476,12 @@ impl GetOuterGears for FixedBend<'_, CW, Cel, R> { } } +impl GetPrevNextInChain for FixedBend<'_, CW, Cel, R> { + fn next_in_chain(&self, _maybe_prev: Option) -> Option { + None + } +} + impl WalkOutwards for FixedBend<'_, CW, Cel, R> { fn outwards(&self) -> DrawingOutwardWalker { DrawingOutwardWalker::new(self.lowest_gears().into_iter()) @@ -510,6 +535,12 @@ impl GetOuterGears for LooseBend<'_, CW, Cel, R> { } } +impl GetPrevNextInChain for LooseBend<'_, CW, Cel, R> { + fn next_in_chain(&self, _maybe_prev: Option) -> Option { + None + } +} + impl WalkOutwards for LooseBend<'_, CW, Cel, R> { fn outwards(&self) -> DrawingOutwardWalker { DrawingOutwardWalker::new(self.outers()) diff --git a/src/drawing/query.rs b/src/drawing/query.rs index 5702335..82a6249 100644 --- a/src/drawing/query.rs +++ b/src/drawing/query.rs @@ -112,24 +112,38 @@ impl Drawing { v } - pub(super) fn find_infringement_except( - &self, + pub(super) fn find_infringement_except<'a>( + &'a self, infringer: PrimitiveIndex, - predicate: &impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool, + predicate: &'a impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool, ) -> Option { + self.infringements_except(infringer, predicate).next() + } + + /*pub(super) fn infringements<'a>( + &'a self, + infringer: PrimitiveIndex, + ) -> impl Iterator + 'a { + self.infringements_except(infringer, &|_, _, _| true) + }*/ + + fn infringements_except<'a>( + &'a self, + infringer: PrimitiveIndex, + predicate: &'a impl Fn(&Self, PrimitiveIndex, PrimitiveIndex) -> bool, + ) -> impl Iterator + 'a { self.infringements_among( infringer, self.locate_possible_infringees(infringer) - .filter_map(|infringee_node| { + .filter_map(move |infringee_node| { if let GenericNode::Primitive(primitive_node) = infringee_node { Some(primitive_node) } else { None } }) - .filter(|infringee| predicate(&self, infringer, *infringee)), + .filter(move |infringee| predicate(&self, infringer, *infringee)), ) - .next() } pub(super) fn infringements_among<'a>( @@ -137,21 +151,45 @@ impl Drawing { infringer: PrimitiveIndex, it: impl Iterator + 'a, ) -> impl Iterator + 'a { - let mut inflated_shape = infringer.primitive(self).shape(); // Unused temporary value just for initialization. - let conditions = infringer.primitive(self).conditions(); + self.clearance_intersectors_among(infringer, it) + .filter(move |infringement| { + // Infringement with loose dots resulted in false positives for + // line-of-sight paths. + !matches!(infringer, PrimitiveIndex::LooseDot(..)) + && !matches!(infringement.1, PrimitiveIndex::LooseDot(..)) + }) + .filter(move |infringement| !self.are_connectable(infringer, infringement.1)) + } - it.filter(move |infringee| { - // Infringement with loose dots resulted in false positives for - // line-of-sight paths. - !matches!(infringer, PrimitiveIndex::LooseDot(..)) - && !matches!(infringee, PrimitiveIndex::LooseDot(..)) - }) - .filter(move |infringee| !self.are_connectable(infringer, *infringee)) - .filter_map(move |primitive_node| { + pub(super) fn clearance_intersectors<'a>( + &'a self, + intersector: PrimitiveIndex, + ) -> impl Iterator + 'a { + self.clearance_intersectors_among( + intersector, + self.locate_possible_infringees(intersector) + .filter_map(move |infringee_node| { + if let GenericNode::Primitive(primitive_node) = infringee_node { + Some(primitive_node) + } else { + None + } + }), + ) + } + + pub(super) fn clearance_intersectors_among<'a>( + &'a self, + intersector: PrimitiveIndex, + it: impl Iterator + 'a, + ) -> impl Iterator + 'a { + let conditions = intersector.primitive(self).conditions(); + + it.filter_map(move |primitive_node| { let infringee_conditions = primitive_node.primitive(self).conditions(); let epsilon = 1.0; - inflated_shape = infringer.primitive(self).shape().inflate( + let inflated_shape = intersector.primitive(self).shape().inflate( match (&conditions, infringee_conditions) { (None, _) | (_, None) => 0.0, (Some(lhs), Some(rhs)) => {