// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use core::fmt; use std::{collections::HashMap, hash::Hash, marker::PhantomData}; use crate::{ drawing::graph::{GetLayer, Retag}, graph::{GenericIndex, GetPetgraphIndex}, }; use super::{AccessBendWeight, AccessDotWeight, AccessSegWeight, GetWidth}; pub trait ApplyGeometryEdit< PW: GetWidth + GetLayer + TryInto + TryInto + TryInto + Retag + Copy, DW: AccessDotWeight + GetLayer, SW: AccessSegWeight + GetLayer, BW: AccessBendWeight + GetLayer, CW: Copy, PI: GetPetgraphIndex + TryInto + TryInto + TryInto + Eq + Hash + Copy, DI: GetPetgraphIndex + Into + Eq + Hash + Copy, SI: GetPetgraphIndex + Into + Eq + Hash + Copy, BI: GetPetgraphIndex + Into + Eq + Hash + Copy, > { fn apply(&mut self, edit: &GeometryEdit); } #[derive(Debug, Clone)] pub struct GeometryEdit { pub(super) dots: HashMap, Option)>, pub(super) segs: HashMap, Option<((DI, DI), SW)>)>, pub(super) bends: HashMap, Option<((DI, DI, DI), BW)>)>, pub(super) compounds: HashMap, (Option<(Vec, CW)>, Option<(Vec, CW)>)>, primitive_weight_marker: PhantomData, } impl< PW: GetWidth + GetLayer + TryInto + TryInto + TryInto + Retag + Copy, DW: AccessDotWeight + GetLayer, SW: AccessSegWeight + GetLayer, BW: AccessBendWeight + GetLayer, CW: Copy, PI: GetPetgraphIndex + TryInto + TryInto + TryInto + Eq + Hash + Copy, DI: GetPetgraphIndex + Into + Eq + Hash + Copy, SI: GetPetgraphIndex + Into + Eq + Hash + Copy, BI: GetPetgraphIndex + Into + Eq + Hash + Copy, > GeometryEdit { pub fn new() -> Self { Self { dots: HashMap::new(), segs: HashMap::new(), bends: HashMap::new(), compounds: HashMap::new(), primitive_weight_marker: PhantomData, } } pub fn reverse(&self) -> Self { Self { dots: self .dots .clone() .into_iter() .map(|(k, v)| (k, (v.1, v.0))) .collect(), segs: self .segs .clone() .into_iter() .map(|(k, v)| (k, (v.1, v.0))) .collect(), bends: self .bends .clone() .into_iter() .map(|(k, v)| (k, (v.1, v.0))) .collect(), compounds: self .compounds .clone() .into_iter() .map(|(k, v)| (k, (v.1, v.0))) .collect(), primitive_weight_marker: PhantomData, } } } /// Recordable objects are objects to which a [recorder](`GeometryEdit`) can be attached, /// the specialized methods working on `&mut` [`Recording`] then (are to be expected to) /// also write changes to the passed `recorder`, and can also be replayed or rolled back /// via [`ApplyGeometryEdit`]. pub trait Recordable { type PW: GetWidth + GetLayer + TryInto + TryInto + TryInto + Retag + Copy; type DW: AccessDotWeight + GetLayer; type SW: AccessSegWeight + GetLayer; type BW: AccessBendWeight + GetLayer; type CW: Copy; type PI: GetPetgraphIndex + TryInto + TryInto + TryInto + Copy + Eq + Hash; type DI: GetPetgraphIndex + Into + Copy + Eq + Hash; type SI: GetPetgraphIndex + Into + Copy + Eq + Hash; type BI: GetPetgraphIndex + Into + Copy + Eq + Hash; #[inline(always)] fn recording<'a>( &'a mut self, recorder: &'a mut GeometryEdit< Self::PW, Self::DW, Self::SW, Self::BW, Self::CW, Self::PI, Self::DI, Self::SI, Self::BI, >, ) -> Recording<'a, Self> { Recording { inner: self, recorder, } } } pub type RecordingEdit = GeometryEdit< ::PW, ::DW, ::SW, ::BW, ::CW, ::PI, ::DI, ::SI, ::BI, >; pub struct Recording<'a, T: Recordable + ?Sized> { pub inner: &'a mut T, pub recorder: &'a mut RecordingEdit, } impl<'a, T> fmt::Debug for Recording<'a, T> where T: Recordable + fmt::Debug + ?Sized, ::PW: fmt::Debug, ::DW: fmt::Debug, ::SW: fmt::Debug, ::BW: fmt::Debug, ::CW: fmt::Debug, ::PI: fmt::Debug, ::DI: fmt::Debug, ::SI: fmt::Debug, ::BI: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Recording") .field("inner", &self.inner) .field("recorder", &self.recorder) .finish() } } // this is a hack, but it allows us to not needing as much boilerplate for delegations impl<'a, T: Recordable + ?Sized> core::ops::Deref for Recording<'a, T> { type Target = T; #[inline(always)] fn deref(&self) -> &T { self.inner } } impl<'a, T> ApplyGeometryEdit< ::PW, ::DW, ::SW, ::BW, ::CW, ::PI, ::DI, ::SI, ::BI, > for Recording<'a, T> where T: Recordable + ?Sized + ApplyGeometryEdit< ::PW, ::DW, ::SW, ::BW, ::CW, ::PI, ::DI, ::SI, ::BI, >, { #[inline(always)] fn apply( &mut self, edit: &GeometryEdit< ::PW, ::DW, ::SW, ::BW, ::CW, ::PI, ::DI, ::SI, ::BI, >, ) { self.inner.apply(edit) } }