use fj_math::{Point, Scalar};
use crate::objects::HalfEdge;
use super::{Validate, ValidationConfig, ValidationError};
impl Validate for HalfEdge {
fn validate_with_config(
&self,
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
) {
EdgeValidationError::check_vertex_coincidence(self, config, errors);
}
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum EdgeValidationError {
#[error(
"Vertices of `Edge` on curve are coincident\n\
- Position of back vertex: {back_position:?}\n\
- Position of front vertex: {front_position:?}\n\
- `Edge`: {half_edge:#?}"
)]
VerticesAreCoincident {
back_position: Point<1>,
front_position: Point<1>,
distance: Scalar,
half_edge: HalfEdge,
},
}
impl EdgeValidationError {
fn check_vertex_coincidence(
edge: &HalfEdge,
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
) {
let [back_position, front_position] = edge.boundary().inner;
let distance = (back_position - front_position).magnitude();
if distance < config.distinct_min_distance {
errors.push(
Self::VerticesAreCoincident {
back_position,
front_position,
distance,
half_edge: edge.clone(),
}
.into(),
);
}
}
}
#[cfg(test)]
mod tests {
use fj_math::Point;
use crate::{
assert_contains_err,
objects::HalfEdge,
operations::build::BuildHalfEdge,
services::Services,
validate::{EdgeValidationError, Validate, ValidationError},
};
#[test]
fn edge_vertices_are_coincident() -> anyhow::Result<()> {
let mut services = Services::new();
let valid =
HalfEdge::line_segment([[0., 0.], [1., 0.]], None, &mut services);
let invalid = {
let boundary = [Point::from([0.]); 2];
HalfEdge::new(
valid.path(),
boundary,
valid.curve().clone(),
valid.start_vertex().clone(),
)
};
valid.validate_and_return_first_error()?;
assert_contains_err!(
invalid,
ValidationError::Edge(
EdgeValidationError::VerticesAreCoincident { .. }
)
);
Ok(())
}
}