fj_kernel/validate/
solid.rs

1use std::iter::repeat;
2
3use crate::{
4    objects::{Solid, Vertex},
5    storage::Handle,
6};
7use fj_math::Point;
8
9use super::{Validate, ValidationConfig, ValidationError};
10
11impl Validate for Solid {
12    fn validate_with_config(
13        &self,
14        config: &ValidationConfig,
15        errors: &mut Vec<ValidationError>,
16    ) {
17        SolidValidationError::check_vertices(self, config, errors)
18    }
19}
20
21/// [`Solid`] validation failed
22#[derive(Clone, Debug, thiserror::Error)]
23pub enum SolidValidationError {
24    /// [`Solid`] contains vertices that are coincident, but not identical
25    #[error(
26        "Solid contains Vertices that are coincident but not identical\n
27        Vertex 1: {vertex_a:#?} ({position_a:?})
28        Vertex 2: {vertex_b:#?} ({position_b:?})"
29    )]
30    DistinctVerticesCoincide {
31        /// The first vertex
32        vertex_a: Handle<Vertex>,
33
34        /// The second vertex
35        vertex_b: Handle<Vertex>,
36
37        /// Position of first vertex
38        position_a: Point<3>,
39
40        /// Position of second vertex
41        position_b: Point<3>,
42    },
43
44    /// [`Solid`] contains vertices that are identical, but do not coincide
45    #[error(
46        "Solid contains Vertices that are identical but do not coincide\n
47        Vertex 1: {vertex_a:#?} ({position_a:?})
48        Vertex 2: {vertex_b:#?} ({position_b:?})"
49    )]
50    IdenticalVerticesNotCoincident {
51        /// The first vertex
52        vertex_a: Handle<Vertex>,
53
54        /// The second vertex
55        vertex_b: Handle<Vertex>,
56
57        /// Position of first vertex
58        position_a: Point<3>,
59
60        /// Position of second vertex
61        position_b: Point<3>,
62    },
63}
64
65impl SolidValidationError {
66    fn check_vertices(
67        solid: &Solid,
68        config: &ValidationConfig,
69        errors: &mut Vec<ValidationError>,
70    ) {
71        let vertices: Vec<(Point<3>, Handle<Vertex>)> = solid
72            .shells()
73            .flat_map(|s| s.faces())
74            .flat_map(|face| {
75                face.all_cycles()
76                    .flat_map(|cycle| cycle.half_edges().cloned())
77                    .zip(repeat(face.surface().geometry()))
78            })
79            .map(|(h, s)| {
80                (
81                    s.point_from_surface_coords(h.start_position()),
82                    h.start_vertex().clone(),
83                )
84            })
85            .collect();
86
87        // This is O(N^2) which isn't great, but we can't use a HashMap since we
88        // need to deal with float inaccuracies. Maybe we could use some smarter
89        // data-structure like an octree.
90        for (position_a, vertex_a) in &vertices {
91            for (position_b, vertex_b) in &vertices {
92                let vertices_are_identical = vertex_a.id() == vertex_b.id();
93                let vertices_are_not_identical = !vertices_are_identical;
94
95                let too_far_to_be_identical = position_a
96                    .distance_to(position_b)
97                    > config.identical_max_distance;
98                let too_close_to_be_distinct = position_a
99                    .distance_to(position_b)
100                    < config.distinct_min_distance;
101
102                if vertices_are_identical && too_far_to_be_identical {
103                    errors.push(
104                        Self::IdenticalVerticesNotCoincident {
105                            vertex_a: vertex_a.clone(),
106                            vertex_b: vertex_b.clone(),
107                            position_a: *position_a,
108                            position_b: *position_b,
109                        }
110                        .into(),
111                    )
112                }
113
114                if vertices_are_not_identical && too_close_to_be_distinct {
115                    errors.push(
116                        Self::DistinctVerticesCoincide {
117                            vertex_a: vertex_a.clone(),
118                            vertex_b: vertex_b.clone(),
119                            position_a: *position_a,
120                            position_b: *position_b,
121                        }
122                        .into(),
123                    )
124                }
125            }
126        }
127    }
128}