fj_core/validation/checks/
half_edge_connection.rs1use fj_math::{Point, Scalar};
2
3use crate::{
4 geometry::Geometry,
5 objects::{Cycle, HalfEdge},
6 storage::Handle,
7 validation::{validation_check::ValidationCheck, ValidationConfig},
8};
9
10#[derive(Clone, Debug, thiserror::Error)]
20#[error(
21 "Adjacent `HalfEdge`s in `Cycle` are not connected\n\
22 - End position of first `HalfEdge`: {end_pos_of_first_half_edge:?}\n\
23 - Start position of second `HalfEdge`: {start_pos_of_second_half_edge:?}\n\
24 - Distance between vertices: {distance_between_positions}\n\
25 - The unconnected `HalfEdge`s: {unconnected_half_edges:#?}"
26)]
27pub struct AdjacentHalfEdgesNotConnected {
28 pub end_pos_of_first_half_edge: Point<2>,
30
31 pub start_pos_of_second_half_edge: Point<2>,
33
34 pub distance_between_positions: Scalar,
36
37 pub unconnected_half_edges: [Handle<HalfEdge>; 2],
39}
40
41impl ValidationCheck<Cycle> for AdjacentHalfEdgesNotConnected {
42 fn check(
43 object: &Cycle,
44 geometry: &Geometry,
45 config: &ValidationConfig,
46 ) -> impl Iterator<Item = Self> {
47 object.half_edges().pairs().filter_map(|(first, second)| {
48 let end_pos_of_first_half_edge = {
49 let [_, end] = first.boundary().inner;
50 geometry
51 .of_half_edge(first)
52 .path
53 .point_from_path_coords(end)
54 };
55 let start_pos_of_second_half_edge = second.start_position();
56
57 let distance_between_positions = (end_pos_of_first_half_edge
58 - start_pos_of_second_half_edge)
59 .magnitude();
60
61 if distance_between_positions > config.identical_max_distance {
62 return Some(AdjacentHalfEdgesNotConnected {
63 end_pos_of_first_half_edge,
64 start_pos_of_second_half_edge,
65 distance_between_positions,
66 unconnected_half_edges: [first.clone(), second.clone()],
67 });
68 }
69
70 None
71 })
72 }
73}
74
75#[cfg(test)]
76mod tests {
77
78 use crate::{
79 objects::{Cycle, HalfEdge},
80 operations::{
81 build::{BuildCycle, BuildHalfEdge},
82 update::UpdateCycle,
83 },
84 validation::ValidationCheck,
85 Core,
86 };
87
88 use super::AdjacentHalfEdgesNotConnected;
89
90 #[test]
91 fn adjacent_half_edges_not_connected() -> anyhow::Result<()> {
92 let mut core = Core::new();
93
94 let valid = Cycle::polygon([[0., 0.], [1., 0.], [1., 1.]], &mut core);
95 AdjacentHalfEdgesNotConnected::check_and_return_first_error(
96 &valid,
97 &core.layers.geometry,
98 )?;
99
100 let invalid = valid.update_half_edge(
101 valid.half_edges().first(),
102 |_, core| {
103 [HalfEdge::line_segment([[0., 0.], [2., 0.]], None, core)]
104 },
105 &mut core,
106 );
107 AdjacentHalfEdgesNotConnected::check_and_expect_one_error(
108 &invalid,
109 &core.layers.geometry,
110 );
111
112 Ok(())
113 }
114}