fj_core/operations/
holes.rs

1//! Add holes to shapes
2
3use fj_math::{Point, Scalar, Vector};
4
5use crate::{
6    objects::{Cycle, Face, HalfEdge, Region, Shell},
7    storage::Handle,
8    Core,
9};
10
11use super::{
12    build::{BuildCycle, BuildHalfEdge, BuildRegion},
13    join::JoinCycle,
14    sweep::{SweepCache, SweepRegion},
15    update::{UpdateCycle, UpdateFace, UpdateRegion, UpdateShell},
16};
17
18/// Add a hole to a [`Shell`]
19pub trait AddHole {
20    /// Add a blind hole at the provided location
21    fn add_blind_hole(
22        &self,
23        location: HoleLocation,
24        radius: impl Into<Scalar>,
25        path: impl Into<Vector<3>>,
26        core: &mut Core,
27    ) -> Self;
28
29    /// Add a through hole between the provided locations
30    fn add_through_hole(
31        &self,
32        locations: [HoleLocation; 2],
33        radius: impl Into<Scalar>,
34        core: &mut Core,
35    ) -> Self;
36}
37
38impl AddHole for Shell {
39    fn add_blind_hole(
40        &self,
41        location: HoleLocation,
42        radius: impl Into<Scalar>,
43        path: impl Into<Vector<3>>,
44        core: &mut Core,
45    ) -> Self {
46        let entry = HalfEdge::circle(location.position, radius, core);
47        let hole = Region::empty(core)
48            .update_exterior(
49                |_, core| Cycle::empty().add_half_edges([entry.clone()], core),
50                core,
51            )
52            .sweep_region(
53                location.face.surface(),
54                None,
55                path,
56                &mut SweepCache::default(),
57                core,
58            )
59            .all_faces()
60            .collect::<Vec<_>>();
61
62        self.update_face(
63            location.face,
64            |face, core| {
65                [face.update_region(
66                    |region, core| {
67                        region.add_interiors(
68                            [Cycle::empty().add_joined_edges(
69                                [(
70                                    entry.clone(),
71                                    core.layers
72                                        .geometry
73                                        .of_half_edge(&entry)
74                                        .path,
75                                    entry.boundary(),
76                                )],
77                                core,
78                            )],
79                            core,
80                        )
81                    },
82                    core,
83                )]
84            },
85            core,
86        )
87        .add_faces(hole, core)
88    }
89
90    fn add_through_hole(
91        &self,
92        [entry_location, exit_location]: [HoleLocation; 2],
93        radius: impl Into<Scalar>,
94        core: &mut Core,
95    ) -> Self {
96        let radius = radius.into();
97
98        let entry = HalfEdge::circle(entry_location.position, radius, core);
99
100        let path = {
101            let point = |location: &HoleLocation| {
102                core.layers
103                    .geometry
104                    .of_surface(location.face.surface())
105                    .point_from_surface_coords(location.position)
106            };
107
108            let entry_point = point(&entry_location);
109            let exit_point = point(&exit_location);
110
111            exit_point - entry_point
112        };
113
114        let swept_region = Region::empty(core)
115            .update_exterior(
116                |_, core| Cycle::empty().add_half_edges([entry.clone()], core),
117                core,
118            )
119            .sweep_region(
120                entry_location.face.surface(),
121                None,
122                path,
123                &mut SweepCache::default(),
124                core,
125            );
126
127        let hole = swept_region.side_faces.into_iter().collect::<Vec<_>>();
128
129        let exit = swept_region
130            .top_face
131            .region()
132            .exterior()
133            .half_edges()
134            .only();
135
136        self.update_face(
137            entry_location.face,
138            |face, core| {
139                [face.update_region(
140                    |region, core| {
141                        region.add_interiors(
142                            [Cycle::empty().add_joined_edges(
143                                [(
144                                    entry.clone(),
145                                    core.layers
146                                        .geometry
147                                        .of_half_edge(&entry)
148                                        .path,
149                                    entry.boundary(),
150                                )],
151                                core,
152                            )],
153                            core,
154                        )
155                    },
156                    core,
157                )]
158            },
159            core,
160        )
161        .update_face(
162            exit_location.face,
163            |face, core| {
164                [face.update_region(
165                    |region, core| {
166                        region.add_interiors(
167                            [Cycle::empty().add_joined_edges(
168                                [(
169                                    exit.clone(),
170                                    core.layers
171                                        .geometry
172                                        .of_half_edge(exit)
173                                        .path,
174                                    exit.boundary(),
175                                )],
176                                core,
177                            )],
178                            core,
179                        )
180                    },
181                    core,
182                )]
183            },
184            core,
185        )
186        .add_faces(hole, core)
187    }
188}
189
190/// Defines the location of a hole
191pub struct HoleLocation<'r> {
192    /// The face that the hole is in
193    pub face: &'r Handle<Face>,
194
195    /// The position of the hole within the face, in surface coordinates
196    pub position: Point<2>,
197}