fj_core/validate/
references.rs

1use std::collections::HashMap;
2use std::hash::Hash;
3
4use crate::objects::{Cycle, Face, HalfEdge, Region, Shell};
5use crate::storage::Handle;
6
7#[derive(Default)]
8pub struct ReferenceCounter<T, U>(HashMap<Handle<T>, Vec<Handle<U>>>);
9
10impl<T: Eq + PartialEq + Hash, U> ReferenceCounter<T, U> {
11    pub fn new() -> Self {
12        Self(HashMap::new())
13    }
14
15    pub fn add_reference(
16        &mut self,
17        referenced: Handle<T>,
18        reference: Handle<U>,
19    ) {
20        self.0
21            .entry(referenced)
22            .and_modify(|references| references.push(reference.clone()))
23            .or_insert(vec![reference]);
24    }
25
26    pub fn get_multiples(&self) -> Vec<MultipleReferences<T, U>> {
27        self.0
28            .iter()
29            .filter(|(_, references)| references.len() > 1)
30            .map(|(referenced, references)| MultipleReferences {
31                referenced: referenced.clone(),
32                references: references.to_vec(),
33            })
34            .collect()
35    }
36}
37
38/// Find errors and convert to [`crate::validate::ValidationError`]
39#[macro_export]
40macro_rules! validate_references {
41    ($errors:ident, $error_ty:ty;$($counter:ident, $err:ident;)*) => {
42        $(
43            $counter.get_multiples().iter().for_each(|multiple| {
44                let reference_error = ReferenceCountError::$err { references: multiple.clone() };
45                $errors.push(Into::<$error_ty>::into(reference_error).into());
46            });
47        )*
48    };
49}
50
51/// Validation errors for when an object is referenced by multiple other objects. Each object
52/// should only be referenced by a single other object  
53#[derive(Clone, Debug, thiserror::Error)]
54pub enum ReferenceCountError {
55    /// [`crate::objects::Region`] referenced by more than one [`crate::objects::Face`]
56    #[error(
57        "[`Region`] referenced by more than one [`Face`]\n{references:#?}"
58    )]
59    Region {
60        references: MultipleReferences<Region, Face>,
61    },
62    /// [`crate::objects::Face`] referenced by more than one [`crate::objects::Shell`]
63    #[error("[`Face`] referenced by more than one [`Shell`]\n{references:#?}")]
64    Face {
65        references: MultipleReferences<Face, Shell>,
66    },
67    /// [`crate::objects::HalfEdge`] referenced by more than one [`crate::objects::Cycle`]
68    #[error(
69        "[`HalfEdge`] referenced by more than one [`Cycle`]\n{references:#?}"
70    )]
71    HalfEdge {
72        references: MultipleReferences<HalfEdge, Cycle>,
73    },
74    /// [`crate::objects::Cycle`] referenced by more than one [`crate::objects::Region`]
75    #[error(
76        "[`Cycle`] referenced by more than one [`Region`]\n{references:#?}"
77    )]
78    Cycle {
79        references: MultipleReferences<Cycle, Region>,
80    },
81}
82
83pub struct MultipleReferences<T, U> {
84    referenced: Handle<T>,
85    references: Vec<Handle<U>>,
86}
87
88use std::fmt::Debug;
89
90impl<T: Debug, U: Debug> Debug for MultipleReferences<T, U> {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        write!(
93            f,
94            "{:?} referenced by {:?}",
95            self.referenced, self.references
96        )
97    }
98}
99
100impl<T, U> Clone for MultipleReferences<T, U> {
101    fn clone(&self) -> Self {
102        Self {
103            referenced: self.referenced.clone(),
104            references: self.references.to_vec(),
105        }
106    }
107}