1use std::iter::repeat;
2
3use crate::{
4 geometry::Geometry,
5 objects::{Solid, Vertex},
6 storage::Handle,
7 validate_references,
8};
9use fj_math::Point;
10
11use super::{
12 references::{ReferenceCountError, ReferenceCounter},
13 Validate, ValidationConfig, ValidationError,
14};
15
16impl Validate for Solid {
17 fn validate(
18 &self,
19 config: &ValidationConfig,
20 errors: &mut Vec<ValidationError>,
21 geometry: &Geometry,
22 ) {
23 SolidValidationError::check_vertices(self, geometry, config, errors);
24 SolidValidationError::check_object_references(self, config, errors);
25 }
26}
27
28#[derive(Clone, Debug, thiserror::Error)]
30pub enum SolidValidationError {
31 #[error(
33 "Solid contains Vertices that are coincident but not identical\n
34 Vertex 1: {vertex_a:#?} ({position_a:?})
35 Vertex 2: {vertex_b:#?} ({position_b:?})"
36 )]
37 DistinctVerticesCoincide {
38 vertex_a: Handle<Vertex>,
40
41 vertex_b: Handle<Vertex>,
43
44 position_a: Point<3>,
46
47 position_b: Point<3>,
49 },
50
51 #[error(
53 "Solid contains Vertices that are identical but do not coincide\n
54 Vertex 1: {vertex_a:#?} ({position_a:?})
55 Vertex 2: {vertex_b:#?} ({position_b:?})"
56 )]
57 IdenticalVerticesNotCoincident {
58 vertex_a: Handle<Vertex>,
60
61 vertex_b: Handle<Vertex>,
63
64 position_a: Point<3>,
66
67 position_b: Point<3>,
69 },
70
71 #[error("Object within solid referenced by more than one other Object")]
73 MultipleReferences(#[from] ReferenceCountError),
74}
75
76impl SolidValidationError {
77 fn check_vertices(
78 solid: &Solid,
79 geometry: &Geometry,
80 config: &ValidationConfig,
81 errors: &mut Vec<ValidationError>,
82 ) {
83 let vertices: Vec<(Point<3>, Handle<Vertex>)> = solid
84 .shells()
85 .iter()
86 .flat_map(|s| s.faces())
87 .flat_map(|face| {
88 face.region()
89 .all_cycles()
90 .flat_map(|cycle| cycle.half_edges().iter().cloned())
91 .zip(repeat(geometry.of_surface(face.surface())))
92 })
93 .map(|(h, s)| {
94 (
95 s.point_from_surface_coords(h.start_position()),
96 h.start_vertex().clone(),
97 )
98 })
99 .collect();
100
101 for (position_a, vertex_a) in &vertices {
105 for (position_b, vertex_b) in &vertices {
106 let vertices_are_identical = vertex_a.id() == vertex_b.id();
107 let vertices_are_not_identical = !vertices_are_identical;
108
109 let too_far_to_be_identical = position_a
110 .distance_to(position_b)
111 > config.identical_max_distance;
112 let too_close_to_be_distinct = position_a
113 .distance_to(position_b)
114 < config.distinct_min_distance;
115
116 if vertices_are_identical && too_far_to_be_identical {
117 errors.push(
118 Self::IdenticalVerticesNotCoincident {
119 vertex_a: vertex_a.clone(),
120 vertex_b: vertex_b.clone(),
121 position_a: *position_a,
122 position_b: *position_b,
123 }
124 .into(),
125 )
126 }
127
128 if vertices_are_not_identical && too_close_to_be_distinct {
129 errors.push(
130 Self::DistinctVerticesCoincide {
131 vertex_a: vertex_a.clone(),
132 vertex_b: vertex_b.clone(),
133 position_a: *position_a,
134 position_b: *position_b,
135 }
136 .into(),
137 )
138 }
139 }
140 }
141 }
142
143 fn check_object_references(
144 solid: &Solid,
145 _config: &ValidationConfig,
146 errors: &mut Vec<ValidationError>,
147 ) {
148 let mut referenced_regions = ReferenceCounter::new();
149 let mut referenced_faces = ReferenceCounter::new();
150 let mut referenced_edges = ReferenceCounter::new();
151 let mut referenced_cycles = ReferenceCounter::new();
152
153 solid.shells().iter().for_each(|s| {
154 s.faces().into_iter().for_each(|f| {
155 referenced_faces.add_reference(f.clone(), s.clone());
156 referenced_regions.add_reference(f.region().clone(), f.clone());
157 f.region().all_cycles().for_each(|c| {
158 referenced_cycles
159 .add_reference(c.clone(), f.region().clone());
160 c.half_edges().into_iter().for_each(|e| {
161 referenced_edges.add_reference(e.clone(), c.clone());
162 })
163 })
164 })
165 });
166
167 validate_references!(
168 errors, SolidValidationError;
169 referenced_regions, Region;
170 referenced_faces, Face;
171 referenced_edges, HalfEdge;
172 referenced_cycles, Cycle;
173 );
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use crate::{
180 assert_contains_err,
181 geometry::GlobalPath,
182 objects::{Cycle, Face, HalfEdge, Region, Shell, Solid, Surface},
183 operations::{
184 build::{BuildFace, BuildHalfEdge, BuildSurface},
185 insert::Insert,
186 },
187 validate::{
188 references::ReferenceCountError, SolidValidationError, Validate,
189 ValidationError,
190 },
191 Core,
192 };
193
194 #[test]
195 fn should_find_face_multiple_references() -> anyhow::Result<()> {
196 let mut core = Core::new();
197
198 let shared_face = Face::new(
199 Surface::surface_from_uv(
200 GlobalPath::circle_from_radius(1.),
201 [0., 1., 1.],
202 &mut core,
203 ),
204 Region::new(
205 Cycle::new(vec![HalfEdge::circle([0., 0.], 1., &mut core)])
206 .insert(&mut core),
207 vec![],
208 )
209 .insert(&mut core),
210 )
211 .insert(&mut core);
212
213 let invalid_solid = Solid::new(vec![
214 Shell::new(vec![shared_face.clone()]).insert(&mut core),
215 Shell::new(vec![
216 shared_face,
217 Face::triangle(
218 [[0., 0., 0.], [1., 0., 0.], [1., 1., 0.]],
219 &mut core,
220 )
221 .insert(&mut core)
222 .face,
223 ])
224 .insert(&mut core),
225 ])
226 .insert(&mut core);
227
228 assert_contains_err!(
229 core,
230 invalid_solid,
231 ValidationError::Solid(SolidValidationError::MultipleReferences(
232 ReferenceCountError::Face { references: _ }
233 ))
234 );
235
236 let valid_solid = Solid::new(vec![]).insert(&mut core);
237 valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
238
239 let _ = core.layers.validation.take_errors();
241
242 Ok(())
243 }
244
245 #[test]
246 fn should_find_region_multiple_references() -> anyhow::Result<()> {
247 let mut core = Core::new();
248
249 let shared_region = Region::new(
250 Cycle::new(vec![HalfEdge::circle([0., 0.], 1., &mut core)])
251 .insert(&mut core),
252 vec![],
253 )
254 .insert(&mut core);
255
256 let invalid_solid = Solid::new(vec![Shell::new(vec![
257 Face::new(
258 Surface::surface_from_uv(
259 GlobalPath::circle_from_radius(1.),
260 [0., 1., 1.],
261 &mut core,
262 ),
263 shared_region.clone(),
264 )
265 .insert(&mut core),
266 Face::new(
267 Surface::surface_from_uv(
268 GlobalPath::circle_from_radius(1.),
269 [0., 0., 1.],
270 &mut core,
271 ),
272 shared_region.clone(),
273 )
274 .insert(&mut core),
275 ])
276 .insert(&mut core)])
277 .insert(&mut core);
278
279 assert_contains_err!(
280 core,
281 invalid_solid,
282 ValidationError::Solid(SolidValidationError::MultipleReferences(
283 ReferenceCountError::Region { references: _ }
284 ))
285 );
286
287 let valid_solid = Solid::new(vec![]).insert(&mut core);
288 valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
289
290 let _ = core.layers.validation.take_errors();
292
293 Ok(())
294 }
295
296 #[test]
297 fn should_find_cycle_multiple_references() -> anyhow::Result<()> {
298 let mut core = Core::new();
299
300 let shared_cycle =
301 Cycle::new(vec![HalfEdge::circle([0., 0.], 1., &mut core)])
302 .insert(&mut core);
303
304 let invalid_solid = Solid::new(vec![Shell::new(vec![
305 Face::new(
306 Surface::surface_from_uv(
307 GlobalPath::circle_from_radius(1.),
308 [0., 1., 1.],
309 &mut core,
310 ),
311 Region::new(shared_cycle.clone(), vec![]).insert(&mut core),
312 )
313 .insert(&mut core),
314 Face::new(
315 Surface::surface_from_uv(
316 GlobalPath::circle_from_radius(1.),
317 [0., 0., 1.],
318 &mut core,
319 ),
320 Region::new(shared_cycle, vec![]).insert(&mut core),
321 )
322 .insert(&mut core),
323 ])
324 .insert(&mut core)])
325 .insert(&mut core);
326
327 assert_contains_err!(
328 core,
329 invalid_solid,
330 ValidationError::Solid(SolidValidationError::MultipleReferences(
331 ReferenceCountError::Cycle { references: _ }
332 ))
333 );
334
335 let valid_solid = Solid::new(vec![]).insert(&mut core);
336 valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
337
338 let _ = core.layers.validation.take_errors();
340
341 Ok(())
342 }
343
344 #[test]
345 fn should_find_half_edge_multiple_references() -> anyhow::Result<()> {
346 let mut core = Core::new();
347
348 let shared_edge = HalfEdge::circle([0., 0.], 1., &mut core);
349
350 let invalid_solid = Solid::new(vec![Shell::new(vec![Face::new(
351 Surface::surface_from_uv(
352 GlobalPath::circle_from_radius(1.),
353 [0., 0., 1.],
354 &mut core,
355 ),
356 Region::new(
357 Cycle::new(vec![shared_edge.clone()]).insert(&mut core),
358 vec![Cycle::new(vec![shared_edge.clone()]).insert(&mut core)],
359 )
360 .insert(&mut core),
361 )
362 .insert(&mut core)])
363 .insert(&mut core)])
364 .insert(&mut core);
365
366 assert_contains_err!(
367 core,
368 invalid_solid,
369 ValidationError::Solid(SolidValidationError::MultipleReferences(
370 ReferenceCountError::HalfEdge { references: _ }
371 ))
372 );
373
374 let valid_solid = Solid::new(vec![]).insert(&mut core);
375 valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
376
377 let _ = core.layers.validation.take_errors();
379
380 Ok(())
381 }
382}