topola/src/geometry/edit.rs

234 lines
6.9 KiB
Rust

// 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<DW> + TryInto<SW> + TryInto<BW> + Retag<PI> + Copy,
DW: AccessDotWeight<PW> + GetLayer,
SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer,
CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
>
{
fn apply(&mut self, edit: &GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>);
}
#[derive(Debug, Clone)]
pub struct GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI> {
pub(super) dots: HashMap<DI, (Option<DW>, Option<DW>)>,
pub(super) segs: HashMap<SI, (Option<((DI, DI), SW)>, Option<((DI, DI), SW)>)>,
pub(super) bends: HashMap<BI, (Option<((DI, DI, DI), BW)>, Option<((DI, DI, DI), BW)>)>,
pub(super) compounds: HashMap<GenericIndex<CW>, (Option<(Vec<PI>, CW)>, Option<(Vec<PI>, CW)>)>,
primitive_weight_marker: PhantomData<PW>,
}
impl<
PW: GetWidth + GetLayer + TryInto<DW> + TryInto<SW> + TryInto<BW> + Retag<PI> + Copy,
DW: AccessDotWeight<PW> + GetLayer,
SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer,
CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy,
> GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>
{
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<Self::DW>
+ TryInto<Self::SW>
+ TryInto<Self::BW>
+ Retag<Self::PI>
+ Copy;
type DW: AccessDotWeight<Self::PW> + GetLayer;
type SW: AccessSegWeight<Self::PW> + GetLayer;
type BW: AccessBendWeight<Self::PW> + GetLayer;
type CW: Copy;
type PI: GetPetgraphIndex
+ TryInto<Self::DI>
+ TryInto<Self::SI>
+ TryInto<Self::BI>
+ Copy
+ Eq
+ Hash;
type DI: GetPetgraphIndex + Into<Self::PI> + Copy + Eq + Hash;
type SI: GetPetgraphIndex + Into<Self::PI> + Copy + Eq + Hash;
type BI: GetPetgraphIndex + Into<Self::PI> + 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<T: Recordable + ?Sized> = GeometryEdit<
<T as Recordable>::PW,
<T as Recordable>::DW,
<T as Recordable>::SW,
<T as Recordable>::BW,
<T as Recordable>::CW,
<T as Recordable>::PI,
<T as Recordable>::DI,
<T as Recordable>::SI,
<T as Recordable>::BI,
>;
pub struct Recording<'a, T: Recordable + ?Sized> {
pub inner: &'a mut T,
pub recorder: &'a mut RecordingEdit<T>,
}
impl<'a, T> fmt::Debug for Recording<'a, T>
where
T: Recordable + fmt::Debug + ?Sized,
<T as Recordable>::PW: fmt::Debug,
<T as Recordable>::DW: fmt::Debug,
<T as Recordable>::SW: fmt::Debug,
<T as Recordable>::BW: fmt::Debug,
<T as Recordable>::CW: fmt::Debug,
<T as Recordable>::PI: fmt::Debug,
<T as Recordable>::DI: fmt::Debug,
<T as Recordable>::SI: fmt::Debug,
<T as Recordable>::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<
<T as Recordable>::PW,
<T as Recordable>::DW,
<T as Recordable>::SW,
<T as Recordable>::BW,
<T as Recordable>::CW,
<T as Recordable>::PI,
<T as Recordable>::DI,
<T as Recordable>::SI,
<T as Recordable>::BI,
> for Recording<'a, T>
where
T: Recordable
+ ?Sized
+ ApplyGeometryEdit<
<T as Recordable>::PW,
<T as Recordable>::DW,
<T as Recordable>::SW,
<T as Recordable>::BW,
<T as Recordable>::CW,
<T as Recordable>::PI,
<T as Recordable>::DI,
<T as Recordable>::SI,
<T as Recordable>::BI,
>,
{
#[inline(always)]
fn apply(
&mut self,
edit: &GeometryEdit<
<T as Recordable>::PW,
<T as Recordable>::DW,
<T as Recordable>::SW,
<T as Recordable>::BW,
<T as Recordable>::CW,
<T as Recordable>::PI,
<T as Recordable>::DI,
<T as Recordable>::SI,
<T as Recordable>::BI,
>,
) {
self.inner.apply(edit)
}
}