1use 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
18pub trait AddHole {
20 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 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
190pub struct HoleLocation<'r> {
192 pub face: &'r Handle<Face>,
194
195 pub position: Point<2>,
197}