fj_core/algorithms/approx/
face.rs1use std::{collections::BTreeSet, ops::Deref};
6
7use fj_interop::Color;
8
9use crate::{
10 objects::{Face, Handedness, ObjectSet},
11 operations::presentation::GetColor,
12 validation::ValidationConfig,
13 Core,
14};
15
16use super::{
17 cycle::CycleApprox, edge::HalfEdgeApproxCache, Approx, ApproxPoint,
18 Tolerance,
19};
20
21impl Approx for &ObjectSet<Face> {
22 type Approximation = BTreeSet<FaceApprox>;
23 type Cache = HalfEdgeApproxCache;
24
25 fn approx_with_cache(
26 self,
27 tolerance: impl Into<Tolerance>,
28 cache: &mut Self::Cache,
29 core: &mut Core,
30 ) -> Self::Approximation {
31 let tolerance = tolerance.into();
32
33 let approx = self
34 .into_iter()
35 .map(|face| face.approx_with_cache(tolerance, cache, core))
36 .collect();
37
38 let min_distance = ValidationConfig::default().distinct_min_distance;
39 let mut all_points: BTreeSet<ApproxPoint<2>> = BTreeSet::new();
40
41 for approx in &approx {
43 let approx: &FaceApprox = approx;
44
45 for a in &approx.points() {
46 for b in &all_points {
47 let distance = (b.global_form - a.global_form).magnitude();
48
49 if b.global_form != a.global_form && distance < min_distance
50 {
51 panic!(
52 "Invalid approximation: \
53 Distinct points are too close \
54 (a: {:?}, b: {:?}, distance: {distance})",
55 a.global_form, b.global_form,
56 );
57 }
58 }
59
60 all_points.insert(*a);
61 }
62 }
63
64 approx
65 }
66}
67
68impl Approx for &Face {
69 type Approximation = FaceApprox;
70 type Cache = HalfEdgeApproxCache;
71
72 fn approx_with_cache(
73 self,
74 tolerance: impl Into<Tolerance>,
75 cache: &mut Self::Cache,
76 core: &mut Core,
77 ) -> Self::Approximation {
78 let tolerance = tolerance.into();
79
80 let exterior = (
94 self.region().exterior().deref(),
95 &core.layers.geometry.of_surface(self.surface()),
96 )
97 .approx_with_cache(tolerance, cache, core);
98
99 let mut interiors = BTreeSet::new();
100 for cycle in self.region().interiors() {
101 let cycle = (
102 cycle.deref(),
103 &core.layers.geometry.of_surface(self.surface()),
104 )
105 .approx_with_cache(tolerance, cache, core);
106 interiors.insert(cycle);
107 }
108
109 FaceApprox {
110 exterior,
111 interiors,
112 color: self.region().get_color(core),
113 coord_handedness: self.coord_handedness(&core.layers.geometry),
114 }
115 }
116}
117
118#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
120pub struct FaceApprox {
121 pub exterior: CycleApprox,
123
124 pub interiors: BTreeSet<CycleApprox>,
126
127 pub color: Option<Color>,
129
130 pub coord_handedness: Handedness,
132}
133
134impl FaceApprox {
135 pub fn points(&self) -> BTreeSet<ApproxPoint<2>> {
137 let mut points = BTreeSet::new();
138
139 points.extend(self.exterior.points());
140
141 for cycle_approx in &self.interiors {
142 points.extend(cycle_approx.points());
143 }
144
145 points
146 }
147}