mirror of
https://github.com/hannobraun/Fornjot
synced 2025-11-06 15:10:04 +00:00
Merge pull request #2363 from hannobraun/validation
Clean up validation code that counts object references
This commit is contained in:
commit
3d633f6821
@ -1,7 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{any::type_name_of_val, collections::HashMap, fmt};
|
||||
|
||||
use crate::storage::Handle;
|
||||
use crate::topology::{Cycle, Face, HalfEdge, Region, Shell};
|
||||
use crate::{
|
||||
storage::Handle,
|
||||
topology::{Cycle, Face, HalfEdge, Region, Shell},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ReferenceCounter<T, U>(HashMap<Handle<T>, Vec<Handle<U>>>);
|
||||
@ -11,24 +13,17 @@ impl<T, U> ReferenceCounter<T, U> {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
pub fn add_reference(
|
||||
&mut self,
|
||||
referenced: Handle<T>,
|
||||
reference: Handle<U>,
|
||||
) {
|
||||
self.0
|
||||
.entry(referenced)
|
||||
.and_modify(|references| references.push(reference.clone()))
|
||||
.or_insert(vec![reference]);
|
||||
pub fn count_reference(&mut self, to: Handle<T>, from: Handle<U>) {
|
||||
self.0.entry(to).or_default().push(from);
|
||||
}
|
||||
|
||||
pub fn get_multiples(&self) -> Vec<MultipleReferences<T, U>> {
|
||||
pub fn find_multiples(&self) -> Vec<MultipleReferences<T, U>> {
|
||||
self.0
|
||||
.iter()
|
||||
.filter(|(_, references)| references.len() > 1)
|
||||
.map(|(referenced, references)| MultipleReferences {
|
||||
referenced: referenced.clone(),
|
||||
references: references.to_vec(),
|
||||
.filter(|(_, referenced_by)| referenced_by.len() > 1)
|
||||
.map(|(object, referenced_by)| MultipleReferences {
|
||||
object: object.clone(),
|
||||
referenced_by: referenced_by.to_vec(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -39,68 +34,62 @@ impl<T, U> ReferenceCounter<T, U> {
|
||||
macro_rules! validate_references {
|
||||
($errors:ident, $error_ty:ty;$($counter:ident, $err:ident;)*) => {
|
||||
$(
|
||||
$counter.get_multiples().iter().for_each(|multiple| {
|
||||
let reference_error = ReferenceCountError::$err { references: multiple.clone() };
|
||||
$counter.find_multiples().iter().for_each(|multiple| {
|
||||
let reference_error = ObjectNotExclusivelyOwned::$err { references: multiple.clone() };
|
||||
$errors.push(Into::<$error_ty>::into(reference_error).into());
|
||||
});
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
/// Validation errors for when an object is referenced by multiple other objects. Each object
|
||||
/// should only be referenced by a single other object
|
||||
/// Object that should be exclusively owned by another, is not
|
||||
///
|
||||
/// Some objects are expected to be "owned" by a single other object. This means
|
||||
/// that only one reference to these objects must exist within the topological
|
||||
/// object graph.
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum ReferenceCountError {
|
||||
pub enum ObjectNotExclusivelyOwned {
|
||||
/// [`Region`] referenced by more than one [`Face`]
|
||||
#[error(
|
||||
"[`Region`] referenced by more than one [`Face`]\n{references:#?}"
|
||||
)]
|
||||
#[error(transparent)]
|
||||
Region {
|
||||
references: MultipleReferences<Region, Face>,
|
||||
},
|
||||
/// [`Face`] referenced by more than one [`Shell`]
|
||||
#[error("[`Face`] referenced by more than one [`Shell`]\n{references:#?}")]
|
||||
#[error(transparent)]
|
||||
Face {
|
||||
references: MultipleReferences<Face, Shell>,
|
||||
},
|
||||
/// [`HalfEdge`] referenced by more than one [`Cycle`]
|
||||
#[error(
|
||||
"[`HalfEdge`] referenced by more than one [`Cycle`]\n{references:#?}"
|
||||
)]
|
||||
#[error(transparent)]
|
||||
HalfEdge {
|
||||
references: MultipleReferences<HalfEdge, Cycle>,
|
||||
},
|
||||
/// [`Cycle`] referenced by more than one [`Region`]
|
||||
#[error(
|
||||
"[`Cycle`] referenced by more than one [`Region`]\n{references:#?}"
|
||||
)]
|
||||
#[error(transparent)]
|
||||
Cycle {
|
||||
references: MultipleReferences<Cycle, Region>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub struct MultipleReferences<T, U> {
|
||||
referenced: Handle<T>,
|
||||
references: Vec<Handle<U>>,
|
||||
object: Handle<T>,
|
||||
referenced_by: Vec<Handle<U>>,
|
||||
}
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
impl<T: Debug, U: Debug> Debug for MultipleReferences<T, U> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl<T, U> fmt::Display for MultipleReferences<T, U>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:?} referenced by {:?}",
|
||||
self.referenced, self.references
|
||||
"`{}` ({:?}) referenced by multiple `{}` objects ({:?})",
|
||||
type_name_of_val(&self.object),
|
||||
self.object,
|
||||
type_name_of_val(&self.referenced_by),
|
||||
self.referenced_by
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Clone for MultipleReferences<T, U> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
referenced: self.referenced.clone(),
|
||||
references: self.references.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
references::{ReferenceCountError, ReferenceCounter},
|
||||
references::{ObjectNotExclusivelyOwned, ReferenceCounter},
|
||||
Validate, ValidationConfig, ValidationError,
|
||||
};
|
||||
|
||||
@ -39,7 +39,8 @@ impl Validate for Sketch {
|
||||
pub enum SketchValidationError {
|
||||
/// Object within sketch referenced by more than one other object
|
||||
#[error("Object within sketch referenced by more than one other Object")]
|
||||
MultipleReferences(#[from] ReferenceCountError),
|
||||
ObjectNotExclusivelyOwned(#[from] ObjectNotExclusivelyOwned),
|
||||
|
||||
/// Region within sketch has exterior cycle with clockwise winding
|
||||
#[error(
|
||||
"Exterior cycle within sketch region has clockwise winding\n
|
||||
@ -49,6 +50,7 @@ pub enum SketchValidationError {
|
||||
/// Cycle with clockwise winding
|
||||
cycle: Handle<Cycle>,
|
||||
},
|
||||
|
||||
/// Region within sketch has interior cycle with counter-clockwise winding
|
||||
#[error(
|
||||
"Interior cycle within sketch region has counter-clockwise winding\n
|
||||
@ -71,9 +73,9 @@ impl SketchValidationError {
|
||||
|
||||
sketch.regions().iter().for_each(|r| {
|
||||
r.all_cycles().for_each(|c| {
|
||||
referenced_cycles.add_reference(c.clone(), r.clone());
|
||||
referenced_cycles.count_reference(c.clone(), r.clone());
|
||||
c.half_edges().into_iter().for_each(|e| {
|
||||
referenced_edges.add_reference(e.clone(), c.clone());
|
||||
referenced_edges.count_reference(e.clone(), c.clone());
|
||||
})
|
||||
})
|
||||
});
|
||||
@ -134,8 +136,8 @@ mod tests {
|
||||
},
|
||||
topology::{Cycle, HalfEdge, Region, Sketch, Vertex},
|
||||
validate::{
|
||||
references::ReferenceCountError, SketchValidationError, Validate,
|
||||
ValidationError,
|
||||
references::ObjectNotExclusivelyOwned, SketchValidationError,
|
||||
Validate, ValidationError,
|
||||
},
|
||||
Core,
|
||||
};
|
||||
@ -168,9 +170,11 @@ mod tests {
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(SketchValidationError::MultipleReferences(
|
||||
ReferenceCountError::Cycle { references: _ }
|
||||
))
|
||||
ValidationError::Sketch(
|
||||
SketchValidationError::ObjectNotExclusivelyOwned(
|
||||
ObjectNotExclusivelyOwned::Cycle { references: _ }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -206,9 +210,11 @@ mod tests {
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(SketchValidationError::MultipleReferences(
|
||||
ReferenceCountError::HalfEdge { references: _ }
|
||||
))
|
||||
ValidationError::Sketch(
|
||||
SketchValidationError::ObjectNotExclusivelyOwned(
|
||||
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -9,7 +9,7 @@ use crate::{
|
||||
use fj_math::Point;
|
||||
|
||||
use super::{
|
||||
references::{ReferenceCountError, ReferenceCounter},
|
||||
references::{ObjectNotExclusivelyOwned, ReferenceCounter},
|
||||
Validate, ValidationConfig, ValidationError,
|
||||
};
|
||||
|
||||
@ -70,7 +70,7 @@ pub enum SolidValidationError {
|
||||
|
||||
/// Object within solid referenced by more than one other object
|
||||
#[error("Object within solid referenced by more than one other Object")]
|
||||
MultipleReferences(#[from] ReferenceCountError),
|
||||
MultipleReferences(#[from] ObjectNotExclusivelyOwned),
|
||||
}
|
||||
|
||||
impl SolidValidationError {
|
||||
@ -154,13 +154,14 @@ impl SolidValidationError {
|
||||
|
||||
solid.shells().iter().for_each(|s| {
|
||||
s.faces().into_iter().for_each(|f| {
|
||||
referenced_faces.add_reference(f.clone(), s.clone());
|
||||
referenced_regions.add_reference(f.region().clone(), f.clone());
|
||||
referenced_faces.count_reference(f.clone(), s.clone());
|
||||
referenced_regions
|
||||
.count_reference(f.region().clone(), f.clone());
|
||||
f.region().all_cycles().for_each(|c| {
|
||||
referenced_cycles
|
||||
.add_reference(c.clone(), f.region().clone());
|
||||
.count_reference(c.clone(), f.region().clone());
|
||||
c.half_edges().into_iter().for_each(|e| {
|
||||
referenced_edges.add_reference(e.clone(), c.clone());
|
||||
referenced_edges.count_reference(e.clone(), c.clone());
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -187,8 +188,8 @@ mod tests {
|
||||
},
|
||||
topology::{Cycle, Face, HalfEdge, Region, Shell, Solid, Surface},
|
||||
validate::{
|
||||
references::ReferenceCountError, SolidValidationError, Validate,
|
||||
ValidationError,
|
||||
references::ObjectNotExclusivelyOwned, SolidValidationError,
|
||||
Validate, ValidationError,
|
||||
},
|
||||
Core,
|
||||
};
|
||||
@ -238,7 +239,7 @@ mod tests {
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Face { references: _ }
|
||||
ObjectNotExclusivelyOwned::Face { references: _ }
|
||||
))
|
||||
);
|
||||
|
||||
@ -284,7 +285,7 @@ mod tests {
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Region { references: _ }
|
||||
ObjectNotExclusivelyOwned::Region { references: _ }
|
||||
))
|
||||
);
|
||||
|
||||
@ -334,7 +335,7 @@ mod tests {
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Cycle { references: _ }
|
||||
ObjectNotExclusivelyOwned::Cycle { references: _ }
|
||||
))
|
||||
);
|
||||
|
||||
@ -376,7 +377,7 @@ mod tests {
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::HalfEdge { references: _ }
|
||||
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user