Skip to content

Commit

Permalink
Merge pull request #1505 from hannobraun/validate
Browse files Browse the repository at this point in the history
Don't stop on first validation error
  • Loading branch information
hannobraun authored Jan 12, 2023
2 parents 4d64a65 + f9e76b1 commit 224a384
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 205 deletions.
6 changes: 2 additions & 4 deletions crates/fj-kernel/src/objects/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,12 @@ macro_rules! object {
}

/// Validate the object
pub fn validate(&self) -> Result<(), ValidationError> {
pub fn validate(&self, errors: &mut Vec<ValidationError>) {
match self {
$(
Self::$ty((_, object)) => object.validate()?,
Self::$ty((_, object)) => object.validate(errors),
)*
}

Ok(())
}
}

Expand Down
7 changes: 5 additions & 2 deletions crates/fj-kernel/src/services/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@ impl State for Validation {
type Event = ValidationFailed;

fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
if let Err(err) = command.object.validate() {
let mut errors = Vec::new();
command.object.validate(&mut errors);

for err in errors {
events.push(ValidationFailed {
object: command.object.into(),
object: command.object.clone().into(),
err,
});
}
Expand Down
16 changes: 5 additions & 11 deletions crates/fj-kernel/src/validate/curve.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
use std::convert::Infallible;

use crate::objects::{Curve, GlobalCurve};

use super::{Validate, ValidationConfig};
use super::{Validate, ValidationConfig, ValidationError};

impl Validate for Curve {
type Error = Infallible;

fn validate_with_config(
&self,
_: &ValidationConfig,
) -> Result<(), Self::Error> {
Ok(())
_: &mut Vec<ValidationError>,
) {
}
}

impl Validate for GlobalCurve {
type Error = Infallible;

fn validate_with_config(
&self,
_: &ValidationConfig,
) -> Result<(), Self::Error> {
Ok(())
_: &mut Vec<ValidationError>,
) {
}
}
33 changes: 17 additions & 16 deletions crates/fj-kernel/src/validate/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@ use crate::{
storage::Handle,
};

use super::{Validate, ValidationConfig};
use super::{Validate, ValidationConfig, ValidationError};

impl Validate for Cycle {
type Error = CycleValidationError;

fn validate_with_config(
&self,
_: &ValidationConfig,
) -> Result<(), Self::Error> {
CycleValidationError::check_half_edge_connections(self)?;
errors: &mut Vec<ValidationError>,
) {
CycleValidationError::check_half_edge_connections(self, errors);

// We don't need to check that all half-edges are defined in the same
// surface. We already check that they are connected by identical
// surface vertices, so that would be redundant.

Ok(())
}
}

Expand All @@ -43,7 +40,10 @@ pub enum CycleValidationError {
}

impl CycleValidationError {
fn check_half_edge_connections(cycle: &Cycle) -> Result<(), Self> {
fn check_half_edge_connections(
cycle: &Cycle,
errors: &mut Vec<ValidationError>,
) {
for (a, b) in cycle.half_edges().circular_tuple_windows() {
let [_, prev] = a.vertices();
let [next, _] = b.vertices();
Expand All @@ -52,14 +52,15 @@ impl CycleValidationError {
let next = next.surface_form();

if prev.id() != next.id() {
return Err(Self::HalfEdgeConnection {
prev: prev.clone(),
next: next.clone(),
});
errors.push(
Self::HalfEdgeConnection {
prev: prev.clone(),
next: next.clone(),
}
.into(),
);
}
}

Ok(())
}
}

Expand Down Expand Up @@ -109,8 +110,8 @@ mod tests {
Cycle::new(half_edges)
};

valid.validate()?;
assert!(invalid.validate().is_err());
valid.validate_and_return_first_error()?;
assert!(invalid.validate_and_return_first_error().is_err());

Ok(())
}
Expand Down
123 changes: 65 additions & 58 deletions crates/fj-kernel/src/validate/edge.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::convert::Infallible;

use fj_interop::ext::ArrayExt;
use fj_math::{Point, Scalar};

Expand All @@ -11,36 +9,31 @@ use crate::{
storage::Handle,
};

use super::{Validate, ValidationConfig};
use super::{Validate, ValidationConfig, ValidationError};

impl Validate for HalfEdge {
type Error = HalfEdgeValidationError;

fn validate_with_config(
&self,
config: &ValidationConfig,
) -> Result<(), Self::Error> {
HalfEdgeValidationError::check_curve_identity(self)?;
HalfEdgeValidationError::check_global_curve_identity(self)?;
HalfEdgeValidationError::check_global_vertex_identity(self)?;
HalfEdgeValidationError::check_vertex_positions(self, config)?;
errors: &mut Vec<ValidationError>,
) {
HalfEdgeValidationError::check_curve_identity(self, errors);
HalfEdgeValidationError::check_global_curve_identity(self, errors);
HalfEdgeValidationError::check_global_vertex_identity(self, errors);
HalfEdgeValidationError::check_vertex_positions(self, config, errors);

// We don't need to check anything about surfaces here. We already check
// curves, which makes sure the vertices are consistent with each other,
// and the validation of those vertices checks the surfaces.

Ok(())
}
}

impl Validate for GlobalEdge {
type Error = Infallible;

fn validate_with_config(
&self,
_: &ValidationConfig,
) -> Result<(), Self::Error> {
Ok(())
_: &mut Vec<ValidationError>,
) {
}
}

Expand Down Expand Up @@ -128,38 +121,49 @@ pub enum HalfEdgeValidationError {
}

impl HalfEdgeValidationError {
fn check_curve_identity(half_edge: &HalfEdge) -> Result<(), Self> {
fn check_curve_identity(
half_edge: &HalfEdge,
errors: &mut Vec<ValidationError>,
) {
let back_curve = half_edge.back().curve();
let front_curve = half_edge.front().curve();

if back_curve.id() != front_curve.id() {
return Err(Self::CurveMismatch {
back_curve: back_curve.clone(),
front_curve: front_curve.clone(),
half_edge: half_edge.clone(),
});
errors.push(
Self::CurveMismatch {
back_curve: back_curve.clone(),
front_curve: front_curve.clone(),
half_edge: half_edge.clone(),
}
.into(),
);
}

Ok(())
}

fn check_global_curve_identity(half_edge: &HalfEdge) -> Result<(), Self> {
fn check_global_curve_identity(
half_edge: &HalfEdge,
errors: &mut Vec<ValidationError>,
) {
let global_curve_from_curve = half_edge.curve().global_form();
let global_curve_from_global_form = half_edge.global_form().curve();

if global_curve_from_curve.id() != global_curve_from_global_form.id() {
return Err(Self::GlobalCurveMismatch {
global_curve_from_curve: global_curve_from_curve.clone(),
global_curve_from_global_form: global_curve_from_global_form
.clone(),
half_edge: half_edge.clone(),
});
errors.push(
Self::GlobalCurveMismatch {
global_curve_from_curve: global_curve_from_curve.clone(),
global_curve_from_global_form:
global_curve_from_global_form.clone(),
half_edge: half_edge.clone(),
}
.into(),
);
}

Ok(())
}

fn check_global_vertex_identity(half_edge: &HalfEdge) -> Result<(), Self> {
fn check_global_vertex_identity(
half_edge: &HalfEdge,
errors: &mut Vec<ValidationError>,
) {
let global_vertices_from_vertices = {
let (global_vertices_from_vertices, _) =
VerticesInNormalizedOrder::new(
Expand All @@ -183,35 +187,38 @@ impl HalfEdgeValidationError {
.map(|global_vertex| global_vertex.id());

if ids_from_vertices != ids_from_global_form {
return Err(Self::GlobalVertexMismatch {
global_vertices_from_vertices,
global_vertices_from_global_form,
half_edge: half_edge.clone(),
});
errors.push(
Self::GlobalVertexMismatch {
global_vertices_from_vertices,
global_vertices_from_global_form,
half_edge: half_edge.clone(),
}
.into(),
);
}

Ok(())
}

fn check_vertex_positions(
half_edge: &HalfEdge,
config: &ValidationConfig,
) -> Result<(), Self> {
errors: &mut Vec<ValidationError>,
) {
let back_position = half_edge.back().position();
let front_position = half_edge.front().position();

let distance = (back_position - front_position).magnitude();

if distance < config.distinct_min_distance {
return Err(Self::VerticesAreCoincident {
back_position,
front_position,
distance,
half_edge: half_edge.clone(),
});
errors.push(
Self::VerticesAreCoincident {
back_position,
front_position,
distance,
half_edge: half_edge.clone(),
}
.into(),
);
}

Ok(())
}
}

Expand Down Expand Up @@ -254,8 +261,8 @@ mod tests {
HalfEdge::new(vertices, valid.global_form().clone())
};

valid.validate()?;
assert!(invalid.validate().is_err());
valid.validate_and_return_first_error()?;
assert!(invalid.validate_and_return_first_error().is_err());

Ok(())
}
Expand All @@ -280,8 +287,8 @@ mod tests {
tmp.build(&mut services.objects)
});

valid.validate()?;
assert!(invalid.validate().is_err());
valid.validate_and_return_first_error()?;
assert!(invalid.validate_and_return_first_error().is_err());

Ok(())
}
Expand Down Expand Up @@ -312,8 +319,8 @@ mod tests {
tmp.build(&mut services.objects)
});

valid.validate()?;
assert!(invalid.validate().is_err());
valid.validate_and_return_first_error()?;
assert!(invalid.validate_and_return_first_error().is_err());

Ok(())
}
Expand Down Expand Up @@ -349,8 +356,8 @@ mod tests {
valid.global_form().clone(),
);

valid.validate()?;
assert!(invalid.validate().is_err());
valid.validate_and_return_first_error()?;
assert!(invalid.validate_and_return_first_error().is_err());

Ok(())
}
Expand Down
Loading

0 comments on commit 224a384

Please sign in to comment.