1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Face approximation
//!
//! See [`FaceApprox`].

use std::collections::BTreeSet;

use fj_interop::mesh::Color;

use crate::{
    algorithms::validate::ValidationConfig,
    objects::{Face, Faces, Handedness},
};

use super::{cycle::CycleApprox, Approx, ApproxPoint, Tolerance};

impl Approx for &Faces {
    type Approximation = BTreeSet<FaceApprox>;

    fn approx(self, tolerance: Tolerance) -> Self::Approximation {
        let approx = self
            .into_iter()
            .map(|face| face.approx(tolerance))
            .collect();

        let min_distance = ValidationConfig::default().distinct_min_distance;
        let mut all_points: BTreeSet<ApproxPoint<2>> = BTreeSet::new();

        for approx in &approx {
            let approx: &FaceApprox = approx;

            for point in &approx.points() {
                for p in &all_points {
                    let distance =
                        (p.global_form - point.global_form).magnitude();

                    if p.global_form != point.global_form
                        && distance < min_distance
                    {
                        let a = p;
                        let b = point;

                        panic!(
                            "Invalid approximation: \
                            Distinct points are too close \
                            (a: {:?}, b: {:?}, distance: {distance})\n\
                            source of `a`: {:#?}\n\
                            source of `b`: {:#?}\n",
                            a.global_form, b.global_form, a.source, b.source
                        );
                    }
                }

                all_points.insert(point.clone());
            }
        }

        approx
    }
}

impl Approx for &Face {
    type Approximation = FaceApprox;

    fn approx(self, tolerance: Tolerance) -> Self::Approximation {
        // Curved faces whose curvature is not fully defined by their edges
        // are not supported yet. For that reason, we can fully ignore `face`'s
        // `surface` field and just pass the edges to `Self::for_edges`.
        //
        // An example of a curved face that is supported, is the cylinder. Its
        // curvature is fully defined be the edges (circles) that border it. The
        // circle approximations are sufficient to triangulate the surface.
        //
        // An example of a curved face that is currently not supported, and thus
        // doesn't need to be handled here, is a sphere. A spherical face would
        // would need to provide its own approximation, as the edges that bound
        // it have nothing to do with its curvature.

        let exterior = self.exterior().approx(tolerance);

        let mut interiors = BTreeSet::new();
        for cycle in self.interiors() {
            let cycle = cycle.approx(tolerance);
            interiors.insert(cycle);
        }

        FaceApprox {
            exterior,
            interiors,
            color: self.color(),
            coord_handedness: self.coord_handedness(),
        }
    }
}

/// An approximation of a [`Face`]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct FaceApprox {
    /// Approximation of the exterior cycle
    pub exterior: CycleApprox,

    /// Approximations of the interior cycles
    pub interiors: BTreeSet<CycleApprox>,

    /// The color of the approximated face
    pub color: Color,

    /// The handedness of the approximated face's front-side coordinate system
    pub coord_handedness: Handedness,
}

impl FaceApprox {
    /// Compute all points that make up the approximation
    pub fn points(&self) -> BTreeSet<ApproxPoint<2>> {
        let mut points = BTreeSet::new();

        points.extend(self.exterior.points());

        for cycle_approx in &self.interiors {
            points.extend(cycle_approx.points());
        }

        points
    }
}