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},
|
storage::{Handle, Store},
|
||||||
validate::{
|
validate::{
|
||||||
CycleValidationError, FaceValidationError, HalfEdgeValidationError,
|
CycleValidationError, FaceValidationError, HalfEdgeValidationError,
|
||||||
SurfaceVertexValidationError, Validate2, VertexValidationError,
|
SurfaceVertexValidationError, Validate, VertexValidationError,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
///
|
||||||
|
/// This trait is used automatically when inserting an object into a store.
|
||||||
pub trait Validate: Sized {
|
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
|
/// 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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
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]
|
||||||
|
|
Loading…
Reference in New Issue