fj_kernel/algorithms/approx/
face.rs

1//! Face approximation
2//!
3//! See [`FaceApprox`].
4
5use std::{collections::BTreeSet, ops::Deref};
6
7use fj_interop::mesh::Color;
8
9use crate::{
10    objects::{Face, FaceSet, Handedness},
11    validate::ValidationConfig,
12};
13
14use super::{
15    cycle::CycleApprox, edge::EdgeCache, Approx, ApproxPoint, Tolerance,
16};
17
18impl Approx for &FaceSet {
19    type Approximation = BTreeSet<FaceApprox>;
20    type Cache = EdgeCache;
21
22    fn approx_with_cache(
23        self,
24        tolerance: impl Into<Tolerance>,
25        cache: &mut Self::Cache,
26    ) -> Self::Approximation {
27        let tolerance = tolerance.into();
28
29        let approx = self
30            .into_iter()
31            .map(|face| face.approx_with_cache(tolerance, cache))
32            .collect();
33
34        let min_distance = ValidationConfig::default().distinct_min_distance;
35        let mut all_points: BTreeSet<ApproxPoint<2>> = BTreeSet::new();
36
37        // Run some validation code on the approximation.
38        for approx in &approx {
39            let approx: &FaceApprox = approx;
40
41            for a in &approx.points() {
42                for b in &all_points {
43                    let distance = (b.global_form - a.global_form).magnitude();
44
45                    if b.global_form != a.global_form && distance < min_distance
46                    {
47                        panic!(
48                            "Invalid approximation: \
49                            Distinct points are too close \
50                            (a: {:?}, b: {:?}, distance: {distance})",
51                            a.global_form, b.global_form,
52                        );
53                    }
54                }
55
56                all_points.insert(a.clone());
57            }
58        }
59
60        approx
61    }
62}
63
64impl Approx for &Face {
65    type Approximation = FaceApprox;
66    type Cache = EdgeCache;
67
68    fn approx_with_cache(
69        self,
70        tolerance: impl Into<Tolerance>,
71        cache: &mut Self::Cache,
72    ) -> Self::Approximation {
73        let tolerance = tolerance.into();
74
75        // Curved faces whose curvature is not fully defined by their edges
76        // are not supported yet. For that reason, we can fully ignore `face`'s
77        // `surface` field and just pass the edges to `Self::for_edges`.
78        //
79        // An example of a curved face that is supported, is the cylinder. Its
80        // curvature is fully defined be the edges (circles) that border it. The
81        // circle approximations are sufficient to triangulate the surface.
82        //
83        // An example of a curved face that is currently not supported, and thus
84        // doesn't need to be handled here, is a sphere. A spherical face would
85        // would need to provide its own approximation, as the edges that bound
86        // it have nothing to do with its curvature.
87
88        let exterior = (self.exterior().deref(), self.surface().deref())
89            .approx_with_cache(tolerance, cache);
90
91        let mut interiors = BTreeSet::new();
92        for cycle in self.interiors() {
93            let cycle = (cycle.deref(), self.surface().deref())
94                .approx_with_cache(tolerance, cache);
95            interiors.insert(cycle);
96        }
97
98        FaceApprox {
99            exterior,
100            interiors,
101            color: self.color(),
102            coord_handedness: self.coord_handedness(),
103        }
104    }
105}
106
107/// An approximation of a [`Face`]
108#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
109pub struct FaceApprox {
110    /// Approximation of the exterior cycle
111    pub exterior: CycleApprox,
112
113    /// Approximations of the interior cycles
114    pub interiors: BTreeSet<CycleApprox>,
115
116    /// The color of the approximated face
117    pub color: Option<Color>,
118
119    /// The handedness of the approximated face's front-side coordinate system
120    pub coord_handedness: Handedness,
121}
122
123impl FaceApprox {
124    /// Compute all points that make up the approximation
125    pub fn points(&self) -> BTreeSet<ApproxPoint<2>> {
126        let mut points = BTreeSet::new();
127
128        points.extend(self.exterior.points());
129
130        for cycle_approx in &self.interiors {
131            points.extend(cycle_approx.points());
132        }
133
134        points
135    }
136}