Merge pull request #1330 from hannobraun/validate

Complete transition to new validation infrastructure
This commit is contained in:
Hanno Braun 2022-11-09 12:25:53 +01:00 committed by GitHub
commit 4ad557dd30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 31 additions and 232 deletions

View File

@ -104,7 +104,7 @@ use crate::{
storage::{Handle, Store}, storage::{Handle, Store},
validate::{ validate::{
CycleValidationError, FaceValidationError, HalfEdgeValidationError, CycleValidationError, FaceValidationError, HalfEdgeValidationError,
SurfaceVertexValidationError, Validate2, VertexValidationError, SurfaceVertexValidationError, Validate, VertexValidationError,
}, },
}; };

View File

@ -2,9 +2,9 @@ use std::convert::Infallible;
use crate::objects::{Curve, GlobalCurve}; use crate::objects::{Curve, GlobalCurve};
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Curve { impl Validate for Curve {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(
@ -15,7 +15,7 @@ impl Validate2 for Curve {
} }
} }
impl Validate2 for GlobalCurve { impl Validate for GlobalCurve {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(

View File

@ -5,9 +5,9 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Cycle { impl Validate for Cycle {
type Error = CycleValidationError; type Error = CycleValidationError;
fn validate_with_config( fn validate_with_config(
@ -69,7 +69,7 @@ mod tests {
builder::{CycleBuilder, HalfEdgeBuilder, VertexBuilder}, builder::{CycleBuilder, HalfEdgeBuilder, VertexBuilder},
objects::{Cycle, Objects}, objects::{Cycle, Objects},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate,
}; };
#[test] #[test]

View File

@ -11,9 +11,9 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for HalfEdge { impl Validate for HalfEdge {
type Error = HalfEdgeValidationError; type Error = HalfEdgeValidationError;
fn validate_with_config( fn validate_with_config(
@ -33,7 +33,7 @@ impl Validate2 for HalfEdge {
} }
} }
impl Validate2 for GlobalEdge { impl Validate for GlobalEdge {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(
@ -204,7 +204,7 @@ mod tests {
builder::{HalfEdgeBuilder, VertexBuilder}, builder::{HalfEdgeBuilder, VertexBuilder},
objects::{GlobalCurve, HalfEdge, Objects}, objects::{GlobalCurve, HalfEdge, Objects},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate,
}; };
#[test] #[test]

View File

@ -5,9 +5,9 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Face { impl Validate for Face {
type Error = FaceValidationError; type Error = FaceValidationError;
fn validate_with_config( fn validate_with_config(
@ -108,7 +108,7 @@ mod tests {
builder::CycleBuilder, builder::CycleBuilder,
objects::{Cycle, Face, Objects}, objects::{Cycle, Face, Objects},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate,
}; };
#[test] #[test]

View File

@ -1,18 +1,4 @@
//! Infrastructure for validating shapes //! Infrastructure for validating objects
//!
//! 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.
mod curve; mod curve;
mod cycle; mod cycle;
@ -22,87 +8,23 @@ mod shell;
mod sketch; mod sketch;
mod solid; mod solid;
mod surface; mod surface;
mod uniqueness;
mod vertex; mod vertex;
pub use self::{ pub use self::{
cycle::CycleValidationError, cycle::CycleValidationError,
edge::HalfEdgeValidationError, edge::HalfEdgeValidationError,
face::FaceValidationError, face::FaceValidationError,
uniqueness::UniquenessIssues,
vertex::{SurfaceVertexValidationError, VertexValidationError}, vertex::{SurfaceVertexValidationError, VertexValidationError},
}; };
use std::{collections::HashSet, convert::Infallible, ops::Deref}; use std::convert::Infallible;
use fj_math::Scalar; use fj_math::Scalar;
use crate::iter::ObjectIters;
/// Validate an object /// Validate an object
pub trait Validate: Sized {
/// Validate the object using default configuration
/// ///
/// The following calls are equivalent: /// This trait is used automatically when inserting an object into a store.
/// ``` rust pub trait Validate: Sized {
/// # 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 /// The error that validation of the implementing type can result in
type Error: Into<ValidationError>; 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 /// An error that can occur during a validation
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum ValidationError { pub enum ValidationError {
/// Uniqueness validation failed
#[error("Uniqueness validation failed")]
Uniqueness(#[from] UniquenessIssues),
/// `Cycle` validation error /// `Cycle` validation error
#[error(transparent)] #[error(transparent)]
Cycle(#[from] CycleValidationError), Cycle(#[from] CycleValidationError),
@ -205,50 +102,3 @@ impl From<Infallible> for ValidationError {
match infallible {} 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(())
}
}

View File

@ -2,9 +2,9 @@ use std::convert::Infallible;
use crate::objects::Shell; use crate::objects::Shell;
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Shell { impl Validate for Shell {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(

View File

@ -2,9 +2,9 @@ use std::convert::Infallible;
use crate::objects::Sketch; use crate::objects::Sketch;
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Sketch { impl Validate for Sketch {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(

View File

@ -2,9 +2,9 @@ use std::convert::Infallible;
use crate::objects::Solid; use crate::objects::Solid;
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Solid { impl Validate for Solid {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(

View File

@ -2,9 +2,9 @@ use std::convert::Infallible;
use crate::objects::Surface; use crate::objects::Surface;
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Surface { impl Validate for Surface {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(

View File

@ -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(())
}
}

View File

@ -7,9 +7,9 @@ use crate::{
storage::Handle, storage::Handle,
}; };
use super::{Validate2, ValidationConfig}; use super::{Validate, ValidationConfig};
impl Validate2 for Vertex { impl Validate for Vertex {
type Error = VertexValidationError; type Error = VertexValidationError;
fn validate_with_config( fn validate_with_config(
@ -22,7 +22,7 @@ impl Validate2 for Vertex {
} }
} }
impl Validate2 for SurfaceVertex { impl Validate for SurfaceVertex {
type Error = SurfaceVertexValidationError; type Error = SurfaceVertexValidationError;
fn validate_with_config( fn validate_with_config(
@ -34,7 +34,7 @@ impl Validate2 for SurfaceVertex {
} }
} }
impl Validate2 for GlobalVertex { impl Validate for GlobalVertex {
type Error = Infallible; type Error = Infallible;
fn validate_with_config( fn validate_with_config(
@ -182,7 +182,7 @@ mod tests {
builder::{CurveBuilder, SurfaceVertexBuilder}, builder::{CurveBuilder, SurfaceVertexBuilder},
objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex}, objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate,
}; };
#[test] #[test]