mirror of https://github.com/hannobraun/Fornjot
Merge pull request #1330 from hannobraun/validate
Complete transition to new validation infrastructure
This commit is contained in:
commit
4ad557dd30
|
@ -104,7 +104,7 @@ use crate::{
|
|||
storage::{Handle, Store},
|
||||
validate::{
|
||||
CycleValidationError, FaceValidationError, HalfEdgeValidationError,
|
||||
SurfaceVertexValidationError, Validate2, VertexValidationError,
|
||||
SurfaceVertexValidationError, Validate, VertexValidationError,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::convert::Infallible;
|
|||
|
||||
use crate::objects::{Curve, GlobalCurve};
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Curve {
|
||||
impl Validate for Curve {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -15,7 +15,7 @@ impl Validate2 for Curve {
|
|||
}
|
||||
}
|
||||
|
||||
impl Validate2 for GlobalCurve {
|
||||
impl Validate for GlobalCurve {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
|
|
@ -5,9 +5,9 @@ use crate::{
|
|||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Cycle {
|
||||
impl Validate for Cycle {
|
||||
type Error = CycleValidationError;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -69,7 +69,7 @@ mod tests {
|
|||
builder::{CycleBuilder, HalfEdgeBuilder, VertexBuilder},
|
||||
objects::{Cycle, Objects},
|
||||
partial::HasPartial,
|
||||
validate::Validate2,
|
||||
validate::Validate,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -11,9 +11,9 @@ use crate::{
|
|||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for HalfEdge {
|
||||
impl Validate for HalfEdge {
|
||||
type Error = HalfEdgeValidationError;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -33,7 +33,7 @@ impl Validate2 for HalfEdge {
|
|||
}
|
||||
}
|
||||
|
||||
impl Validate2 for GlobalEdge {
|
||||
impl Validate for GlobalEdge {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -204,7 +204,7 @@ mod tests {
|
|||
builder::{HalfEdgeBuilder, VertexBuilder},
|
||||
objects::{GlobalCurve, HalfEdge, Objects},
|
||||
partial::HasPartial,
|
||||
validate::Validate2,
|
||||
validate::Validate,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5,9 +5,9 @@ use crate::{
|
|||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Face {
|
||||
impl Validate for Face {
|
||||
type Error = FaceValidationError;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -108,7 +108,7 @@ mod tests {
|
|||
builder::CycleBuilder,
|
||||
objects::{Cycle, Face, Objects},
|
||||
partial::HasPartial,
|
||||
validate::Validate2,
|
||||
validate::Validate,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,18 +1,4 @@
|
|||
//! Infrastructure for validating shapes
|
||||
//!
|
||||
//! Validation enforces various constraints about shapes and the objects that
|
||||
//! constitute them. These constraints fall into 4 categories:
|
||||
//!
|
||||
//! - **Coherence:** Local forms of objects must be consistent with their
|
||||
//! canonical forms.
|
||||
//! - **Geometric:** Comprises various object-specific constraints, for example
|
||||
//! edges or faces might not be allowed to intersect.
|
||||
//! - **Structural:** All other objects that an object references must be part
|
||||
//! of the same shape.
|
||||
//! - **Uniqueness:** Objects within a shape must be unique.
|
||||
//!
|
||||
//! Please note that not all of these validation categories are fully
|
||||
//! implemented, as of this writing.
|
||||
//! Infrastructure for validating objects
|
||||
|
||||
mod curve;
|
||||
mod cycle;
|
||||
|
@ -22,87 +8,23 @@ mod shell;
|
|||
mod sketch;
|
||||
mod solid;
|
||||
mod surface;
|
||||
mod uniqueness;
|
||||
mod vertex;
|
||||
|
||||
pub use self::{
|
||||
cycle::CycleValidationError,
|
||||
edge::HalfEdgeValidationError,
|
||||
face::FaceValidationError,
|
||||
uniqueness::UniquenessIssues,
|
||||
vertex::{SurfaceVertexValidationError, VertexValidationError},
|
||||
};
|
||||
|
||||
use std::{collections::HashSet, convert::Infallible, ops::Deref};
|
||||
use std::convert::Infallible;
|
||||
|
||||
use fj_math::Scalar;
|
||||
|
||||
use crate::iter::ObjectIters;
|
||||
|
||||
/// Validate an object
|
||||
///
|
||||
/// This trait is used automatically when inserting an object into a store.
|
||||
pub trait Validate: Sized {
|
||||
/// Validate the object using default configuration
|
||||
///
|
||||
/// The following calls are equivalent:
|
||||
/// ``` rust
|
||||
/// # use fj_kernel::{
|
||||
/// # objects::{GlobalVertex, Objects},
|
||||
/// # validate::{Validate, ValidationConfig},
|
||||
/// # };
|
||||
/// # let objects = Objects::new();
|
||||
/// # let object = objects.global_vertices.insert(
|
||||
/// # GlobalVertex::from_position([0., 0., 0.])
|
||||
/// # );
|
||||
/// object.validate();
|
||||
/// ```
|
||||
/// ``` rust
|
||||
/// # use fj_kernel::{
|
||||
/// # objects::{GlobalVertex, Objects},
|
||||
/// # validate::{Validate, ValidationConfig},
|
||||
/// # };
|
||||
/// # let objects = Objects::new();
|
||||
/// # let object = objects.global_vertices.insert(
|
||||
/// # GlobalVertex::from_position([0., 0., 0.])
|
||||
/// # );
|
||||
/// object.validate_with_config(&ValidationConfig::default());
|
||||
/// ```
|
||||
fn validate(self) -> Result<Validated<Self>, ValidationError> {
|
||||
self.validate_with_config(&ValidationConfig::default())
|
||||
}
|
||||
|
||||
/// Validate the object
|
||||
fn validate_with_config(
|
||||
self,
|
||||
config: &ValidationConfig,
|
||||
) -> Result<Validated<Self>, ValidationError>;
|
||||
}
|
||||
|
||||
impl<T> Validate for T
|
||||
where
|
||||
T: for<'r> ObjectIters<'r>,
|
||||
{
|
||||
fn validate_with_config(
|
||||
self,
|
||||
config: &ValidationConfig,
|
||||
) -> Result<Validated<Self>, ValidationError> {
|
||||
let mut global_vertices = HashSet::new();
|
||||
|
||||
for global_vertex in self.global_vertex_iter() {
|
||||
uniqueness::validate_vertex(
|
||||
global_vertex,
|
||||
&global_vertices,
|
||||
config.distinct_min_distance,
|
||||
)?;
|
||||
|
||||
global_vertices.insert(*global_vertex);
|
||||
}
|
||||
|
||||
Ok(Validated(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate an object
|
||||
pub trait Validate2: Sized {
|
||||
/// The error that validation of the implementing type can result in
|
||||
type Error: Into<ValidationError>;
|
||||
|
||||
|
@ -150,35 +72,10 @@ impl Default for ValidationConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper around an object that indicates the object has been validated
|
||||
///
|
||||
/// Returned by implementations of `Validate`.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct Validated<T>(T);
|
||||
|
||||
impl<T> Validated<T> {
|
||||
/// Consume this instance of `Validated` and return the wrapped object
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Validated<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that can occur during a validation
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ValidationError {
|
||||
/// Uniqueness validation failed
|
||||
#[error("Uniqueness validation failed")]
|
||||
Uniqueness(#[from] UniquenessIssues),
|
||||
|
||||
/// `Cycle` validation error
|
||||
#[error(transparent)]
|
||||
Cycle(#[from] CycleValidationError),
|
||||
|
@ -205,50 +102,3 @@ impl From<Infallible> for ValidationError {
|
|||
match infallible {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use fj_math::{Point, Scalar};
|
||||
|
||||
use crate::{
|
||||
objects::{GlobalVertex, Objects},
|
||||
validate::{Validate, ValidationConfig, ValidationError},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn uniqueness_vertex() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
let mut shape = Vec::new();
|
||||
|
||||
let deviation = Scalar::from_f64(0.25);
|
||||
|
||||
let a = Point::from([0., 0., 0.]);
|
||||
|
||||
let mut b = a;
|
||||
b.x += deviation;
|
||||
|
||||
let config = ValidationConfig {
|
||||
distinct_min_distance: deviation * 2.,
|
||||
..ValidationConfig::default()
|
||||
};
|
||||
|
||||
// Adding a vertex should work.
|
||||
shape.push(
|
||||
objects
|
||||
.global_vertices
|
||||
.insert(GlobalVertex::from_position(a)),
|
||||
);
|
||||
shape.clone().validate_with_config(&config)?;
|
||||
|
||||
// Adding a second vertex that is considered identical should fail.
|
||||
shape.push(
|
||||
objects
|
||||
.global_vertices
|
||||
.insert(GlobalVertex::from_position(b)),
|
||||
);
|
||||
let result = shape.validate_with_config(&config);
|
||||
assert!(matches!(result, Err(ValidationError::Uniqueness(_))));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::convert::Infallible;
|
|||
|
||||
use crate::objects::Shell;
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Shell {
|
||||
impl Validate for Shell {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::convert::Infallible;
|
|||
|
||||
use crate::objects::Sketch;
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Sketch {
|
||||
impl Validate for Sketch {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::convert::Infallible;
|
|||
|
||||
use crate::objects::Solid;
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Solid {
|
||||
impl Validate for Solid {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::convert::Infallible;
|
|||
|
||||
use crate::objects::Surface;
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Surface {
|
||||
impl Validate for Surface {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
use std::{collections::HashSet, fmt};
|
||||
|
||||
use fj_math::Scalar;
|
||||
|
||||
use crate::objects::GlobalVertex;
|
||||
|
||||
pub fn validate_vertex(
|
||||
vertex: &GlobalVertex,
|
||||
vertices: &HashSet<GlobalVertex>,
|
||||
min_distance: Scalar,
|
||||
) -> Result<(), UniquenessIssues> {
|
||||
for existing in vertices {
|
||||
if (existing.position() - vertex.position()).magnitude() < min_distance
|
||||
{
|
||||
return Err(UniquenessIssues {
|
||||
duplicate_vertex: Some(*existing),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Uniqueness issues found during validation
|
||||
///
|
||||
/// Used by [`ValidationError`].
|
||||
///
|
||||
/// # Implementation Note
|
||||
///
|
||||
/// This struct doesn't carry any actual information, currently. Information
|
||||
/// about the specific uniqueness issues found can be added as required. For
|
||||
/// now, this struct exists to ease the error handling code.
|
||||
///
|
||||
/// [`ValidationError`]: super::ValidationError
|
||||
#[derive(Debug, Default, thiserror::Error)]
|
||||
pub struct UniquenessIssues {
|
||||
/// Duplicate vertex found
|
||||
pub duplicate_vertex: Option<GlobalVertex>,
|
||||
}
|
||||
|
||||
impl fmt::Display for UniquenessIssues {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Uniqueness issues found:")?;
|
||||
|
||||
if let Some(duplicate_vertex) = &self.duplicate_vertex {
|
||||
writeln!(f, "- Duplicate vertex ({:?}", duplicate_vertex)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -7,9 +7,9 @@ use crate::{
|
|||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{Validate2, ValidationConfig};
|
||||
use super::{Validate, ValidationConfig};
|
||||
|
||||
impl Validate2 for Vertex {
|
||||
impl Validate for Vertex {
|
||||
type Error = VertexValidationError;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -22,7 +22,7 @@ impl Validate2 for Vertex {
|
|||
}
|
||||
}
|
||||
|
||||
impl Validate2 for SurfaceVertex {
|
||||
impl Validate for SurfaceVertex {
|
||||
type Error = SurfaceVertexValidationError;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -34,7 +34,7 @@ impl Validate2 for SurfaceVertex {
|
|||
}
|
||||
}
|
||||
|
||||
impl Validate2 for GlobalVertex {
|
||||
impl Validate for GlobalVertex {
|
||||
type Error = Infallible;
|
||||
|
||||
fn validate_with_config(
|
||||
|
@ -182,7 +182,7 @@ mod tests {
|
|||
builder::{CurveBuilder, SurfaceVertexBuilder},
|
||||
objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex},
|
||||
partial::HasPartial,
|
||||
validate::Validate2,
|
||||
validate::Validate,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in New Issue