fj_core/algorithms/approx/
face.rs

1//! Face approximation
2//!
3//! See [`FaceApprox`].
4
5use 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        // Run some validation code on the approximation.
42        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        // Curved faces whose curvature is not fully defined by their edges
81        // are not supported yet. For that reason, we can fully ignore `face`'s
82        // `surface` field and just pass the edges to `Self::for_edges`.
83        //
84        // An example of a curved face that is supported, is the cylinder. Its
85        // curvature is fully defined be the edges (circles) that border it. The
86        // circle approximations are sufficient to triangulate the surface.
87        //
88        // An example of a curved face that is currently not supported, and thus
89        // doesn't need to be handled here, is a sphere. A spherical face would
90        // would need to provide its own approximation, as the edges that bound
91        // it have nothing to do with its curvature.
92
93        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/// An approximation of a [`Face`]
119#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
120pub struct FaceApprox {
121    /// Approximation of the exterior cycle
122    pub exterior: CycleApprox,
123
124    /// Approximations of the interior cycles
125    pub interiors: BTreeSet<CycleApprox>,
126
127    /// The color of the approximated face
128    pub color: Option<Color>,
129
130    /// The handedness of the approximated face's front-side coordinate system
131    pub coord_handedness: Handedness,
132}
133
134impl FaceApprox {
135    /// Compute all points that make up the approximation
136    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}