fj_operations/
difference_2d.rs

1use std::ops::Deref;
2
3use fj_interop::{debug::DebugInfo, ext::ArrayExt, mesh::Color};
4use fj_kernel::{
5    algorithms::reverse::Reverse,
6    objects::{Face, Sketch},
7    operations::Insert,
8    services::Services,
9};
10use fj_math::Aabb;
11
12use super::Shape;
13
14impl Shape for fj::Difference2d {
15    type Brep = Sketch;
16
17    fn compute_brep(
18        &self,
19        services: &mut Services,
20        debug_info: &mut DebugInfo,
21    ) -> Self::Brep {
22        // This method assumes that `b` is fully contained within `a`:
23        // https://github.com/hannobraun/Fornjot/issues/92
24
25        let mut faces = Vec::new();
26
27        let mut exteriors = Vec::new();
28        let mut interiors = Vec::new();
29
30        let [a, b] = self
31            .shapes()
32            .each_ref_ext()
33            .map(|shape| shape.compute_brep(services, debug_info));
34
35        if let Some(face) = a.faces().into_iter().next() {
36            // If there's at least one face to subtract from, we can proceed.
37
38            let surface = face.surface();
39
40            for face in a.faces() {
41                assert_eq!(
42                    surface,
43                    face.surface(),
44                    "Trying to subtract faces with different surfaces.",
45                );
46
47                exteriors.push(face.exterior().clone());
48                for cycle in face.interiors() {
49                    interiors.push(cycle.clone().reverse(services));
50                }
51            }
52
53            for face in b.faces() {
54                assert_eq!(
55                    surface,
56                    face.surface(),
57                    "Trying to subtract faces with different surfaces.",
58                );
59
60                interiors.push(face.exterior().clone().reverse(services));
61            }
62
63            // Faces only support one exterior, while the code here comes from
64            // the time when a face could have multiple exteriors. This was only
65            // a special case, i.e. faces that connected to themselves, and I
66            // have my doubts that this code was ever correct in the first
67            // place.
68            //
69            // Anyway, the following should make sure that at least any problems
70            // this code causes become obvious. I don't know if this can ever
71            // trigger, but better safe than sorry.
72            let exterior = exteriors
73                .pop()
74                .expect("Can't construct face without an exterior");
75            assert!(
76                exteriors.is_empty(),
77                "Can't construct face with multiple exteriors"
78            );
79
80            let face = Face::new(
81                surface.clone(),
82                exterior,
83                interiors,
84                Some(Color(self.color())),
85            );
86            faces.push(face.insert(services));
87        }
88
89        let difference = Sketch::new(faces).insert(services);
90        difference.deref().clone()
91    }
92
93    fn bounding_volume(&self) -> Aabb<3> {
94        // This is a conservative estimate of the bounding box: It's never going
95        // to be bigger than the bounding box of the original shape that another
96        // is being subtracted from.
97        self.shapes()[0].bounding_volume()
98    }
99}