// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use enum_dispatch::enum_dispatch; use petgraph::stable_graph::NodeIndex; use crate::{ drawing::Drawing, drawing::{ bend::LooseBendIndex, dot::{DotIndex, LooseDotIndex}, graph::{MakePrimitiveRef, PrimitiveIndex}, primitive::{ GetJoints, LoneLooseSegRef, LooseBendRef, LooseDotRef, PrimitiveRef, SeqLooseSegRef, }, rules::AccessRules, seg::{LoneLooseSegIndex, SeqLooseSegIndex}, }, graph::GetPetgraphIndex, }; #[enum_dispatch] pub trait GetPrevNextLoose { fn next_loose(&self, maybe_prev: Option) -> Option; fn prev_loose(&self, maybe_next: Option) -> Option { // `next_loose` and `prev_loose` do exactly the same thing // when `maybe_*` is `Some(_)`, // but otherwise, they start in opposite direction, here by going via: let maybe_prev = maybe_next.or_else(|| { // default_neighbor = self.next_loose(None) // * normally, one would retrieve the next element via `default_neighbor.next_loose(self)` // * `self.next_loose(default_neighbor)` on the other hand inverts the direction we're iterating towards. }); self.next_loose(maybe_prev) } } #[enum_dispatch(GetPetgraphIndex, MakePrimitiveRef)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum LooseIndex { Dot(LooseDotIndex), LoneSeg(LoneLooseSegIndex), SeqSeg(SeqLooseSegIndex), Bend(LooseBendIndex), } impl From for PrimitiveIndex { fn from(loose: LooseIndex) -> Self { match loose { LooseIndex::Dot(dot) => PrimitiveIndex::LooseDot(dot), LooseIndex::LoneSeg(seg) => PrimitiveIndex::LoneLooseSeg(seg), LooseIndex::SeqSeg(seg) => PrimitiveIndex::SeqLooseSeg(seg), LooseIndex::Bend(bend) => PrimitiveIndex::LooseBend(bend), } } } 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, Cel, R> { Dot(LooseDotRef<'a, CW, Cel, R>), LoneSeg(LoneLooseSegRef<'a, CW, Cel, R>), SeqSeg(SeqLooseSegRef<'a, CW, Cel, R>), Bend(LooseBendRef<'a, CW, Cel, R>), } impl<'a, CW, Cel, R> Loose<'a, CW, Cel, R> { pub fn new(index: LooseIndex, drawing: &'a Drawing) -> Self { match index { LooseIndex::Dot(dot) => drawing.primitive(dot).into(), LooseIndex::LoneSeg(seg) => drawing.primitive(seg).into(), LooseIndex::SeqSeg(seg) => drawing.primitive(seg).into(), LooseIndex::Bend(bend) => drawing.primitive(bend).into(), } } } impl GetPrevNextLoose for LooseDotRef<'_, CW, Cel, R> { fn next_loose(&self, maybe_prev: Option) -> Option { let bend = self.bend(); if let Some(prev) = maybe_prev { if bend.petgraph_index() != prev.petgraph_index() { Some(bend.into()) } else { self.seg().map(Into::into) } } else { Some(bend.into()) } } } impl GetPrevNextLoose for LoneLooseSegRef<'_, CW, Cel, R> { fn next_loose(&self, _maybe_prev: Option) -> Option { None } } impl GetPrevNextLoose for SeqLooseSegRef<'_, CW, Cel, R> { fn next_loose(&self, maybe_prev: Option) -> Option { let joints = self.joints(); let Some(prev) = maybe_prev else { return Some(joints.1.into()); }; if joints.0.petgraph_index() != prev.petgraph_index() { match joints.0 { DotIndex::Fixed(..) => None, DotIndex::Loose(dot) => Some(dot.into()), } } else { Some(joints.1.into()) } } } impl GetPrevNextLoose for LooseBendRef<'_, CW, Cel, R> { fn next_loose(&self, maybe_prev: Option) -> Option { let joints = self.joints(); if let Some(prev) = maybe_prev { if joints.0.petgraph_index() != prev.petgraph_index() { Some(joints.0.into()) } else { Some(joints.1.into()) } } else { Some(joints.0.into()) } } }