fj_kernel/validate/
edge.rs1use fj_math::{Point, Scalar};
2
3use crate::objects::{GlobalEdge, HalfEdge};
4
5use super::{Validate, ValidationConfig, ValidationError};
6
7impl Validate for HalfEdge {
8 fn validate_with_config(
9 &self,
10 config: &ValidationConfig,
11 errors: &mut Vec<ValidationError>,
12 ) {
13 HalfEdgeValidationError::check_vertex_coincidence(self, config, errors);
14 }
15}
16
17impl Validate for GlobalEdge {
18 fn validate_with_config(
19 &self,
20 _: &ValidationConfig,
21 _: &mut Vec<ValidationError>,
22 ) {
23 }
24}
25
26#[derive(Clone, Debug, thiserror::Error)]
28pub enum HalfEdgeValidationError {
29 #[error(
31 "Vertices of `HalfEdge` on curve are coincident\n\
32 - Position of back vertex: {back_position:?}\n\
33 - Position of front vertex: {front_position:?}\n\
34 - `HalfEdge`: {half_edge:#?}"
35 )]
36 VerticesAreCoincident {
37 back_position: Point<1>,
39
40 front_position: Point<1>,
42
43 distance: Scalar,
45
46 half_edge: HalfEdge,
48 },
49}
50
51impl HalfEdgeValidationError {
52 fn check_vertex_coincidence(
53 half_edge: &HalfEdge,
54 config: &ValidationConfig,
55 errors: &mut Vec<ValidationError>,
56 ) {
57 let [back_position, front_position] = half_edge.boundary();
58 let distance = (back_position - front_position).magnitude();
59
60 if distance < config.distinct_min_distance {
61 errors.push(
62 Self::VerticesAreCoincident {
63 back_position,
64 front_position,
65 distance,
66 half_edge: half_edge.clone(),
67 }
68 .into(),
69 );
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use fj_math::Point;
77
78 use crate::{
79 assert_contains_err,
80 objects::HalfEdge,
81 operations::BuildHalfEdge,
82 services::Services,
83 validate::{HalfEdgeValidationError, Validate, ValidationError},
84 };
85
86 #[test]
87 fn half_edge_vertices_are_coincident() -> anyhow::Result<()> {
88 let mut services = Services::new();
89
90 let valid =
91 HalfEdge::line_segment([[0., 0.], [1., 0.]], None, &mut services);
92 let invalid = {
93 let boundary = [Point::from([0.]); 2];
94
95 HalfEdge::new(
96 valid.curve(),
97 boundary,
98 valid.start_vertex().clone(),
99 valid.global_form().clone(),
100 )
101 };
102
103 valid.validate_and_return_first_error()?;
104 assert_contains_err!(
105 invalid,
106 ValidationError::HalfEdge(
107 HalfEdgeValidationError::VerticesAreCoincident { .. }
108 )
109 );
110
111 Ok(())
112 }
113}