fj_core/operations/split/
face.rs1use fj_interop::ext::ArrayExt;
2use fj_math::Point;
3use itertools::Itertools;
4
5use crate::{
6 objects::{Cycle, Face, HalfEdge, Shell},
7 operations::{
8 build::{BuildCycle, BuildHalfEdge},
9 derive::DeriveFrom,
10 geometry::UpdateHalfEdgeGeometry,
11 insert::Insert,
12 split::SplitEdge,
13 update::{
14 UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateRegion, UpdateShell,
15 },
16 },
17 storage::Handle,
18 Core,
19};
20
21pub trait SplitFace: Sized {
23 #[must_use]
40 fn split_face(
41 &self,
42 face: &Handle<Face>,
43 line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
44 core: &mut Core,
45 ) -> (Self, [Handle<Face>; 2]);
46}
47
48impl SplitFace for Shell {
49 fn split_face(
50 &self,
51 face: &Handle<Face>,
52 line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
53 core: &mut Core,
54 ) -> (Self, [Handle<Face>; 2]) {
55 {
66 let [(a, _), (b, _)] = line.each_ref_ext();
67
68 let exterior = face.region().exterior();
69
70 assert!(exterior.half_edges().contains(a));
71 assert!(exterior.half_edges().contains(b));
72 }
73
74 let mut self_ = self.clone();
75
76 let [[a, b], [c, d]] = line.map(|(half_edge, point)| {
77 let (shell, [[a, b], _]) = self_.split_edge(half_edge, point, core);
78 self_ = shell;
79 [a, b]
80 });
81
82 let mut updated_face_after_split_edges = None;
86 for f in self_.faces() {
87 let half_edges = f.region().exterior().half_edges();
88
89 if half_edges.contains(&a)
90 && half_edges.contains(&b)
91 && half_edges.contains(&c)
92 && half_edges.contains(&d)
93 {
94 assert!(
95 updated_face_after_split_edges.is_none(),
96 "There should never be two faces that share half-edges"
97 );
98 updated_face_after_split_edges = Some(f);
99 }
100 }
101 let updated_face_after_split_edges = updated_face_after_split_edges
102 .expect("Updated shell must contain updated face");
103
104 let dividing_half_edge_a_to_d = {
106 let half_edge = HalfEdge::line_segment(
107 [b.start_position(), d.start_position()],
108 None,
109 core,
110 );
111 half_edge
112 .update_start_vertex(|_, _| b.start_vertex().clone(), core)
113 .insert(core)
114 .set_path(
115 core.layers.geometry.of_half_edge(&half_edge).path,
116 &mut core.layers.geometry,
117 )
118 };
119 let dividing_half_edge_c_to_b = HalfEdge::from_sibling(
120 &dividing_half_edge_a_to_d,
121 d.start_vertex().clone(),
122 core,
123 );
124
125 let mut half_edges_of_face_starting_at_b =
126 updated_face_after_split_edges
127 .region()
128 .exterior()
129 .half_edges()
130 .iter()
131 .cloned()
132 .cycle()
133 .skip_while(|half_edge| half_edge != &b);
134
135 let half_edges_b_to_c_inclusive = half_edges_of_face_starting_at_b
136 .take_while_ref(|half_edge| half_edge != &d);
137 let split_face_a = updated_face_after_split_edges
138 .update_region(
139 |region, core| {
140 region.update_exterior(
141 |_, core| {
142 Cycle::empty()
143 .add_half_edges(
144 half_edges_b_to_c_inclusive,
145 core,
146 )
147 .add_half_edges(
148 [dividing_half_edge_c_to_b],
149 core,
150 )
151 },
152 core,
153 )
154 },
155 core,
156 )
157 .insert(core)
158 .derive_from(updated_face_after_split_edges, core);
159
160 let half_edges_of_face_starting_at_d = half_edges_of_face_starting_at_b;
162
163 let half_edges_d_to_a_inclusive = half_edges_of_face_starting_at_d
164 .take_while(|half_edge| half_edge != &b);
165 let split_face_b = updated_face_after_split_edges
166 .update_region(
167 |region, core| {
168 region.update_exterior(
169 |_, core| {
170 Cycle::empty()
171 .add_half_edges(
172 half_edges_d_to_a_inclusive,
173 core,
174 )
175 .add_half_edges(
176 [dividing_half_edge_a_to_d],
177 core,
178 )
179 },
180 core,
181 )
182 },
183 core,
184 )
185 .insert(core)
186 .derive_from(updated_face_after_split_edges, core);
187
188 let faces = [split_face_a, split_face_b];
189 let self_ = self_.update_face(
190 updated_face_after_split_edges,
191 |_, _| faces.clone(),
192 core,
193 );
194
195 (self_, faces)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use fj_interop::Color;
202
203 use crate::{
204 objects::Shell,
205 operations::{
206 build::BuildShell,
207 presentation::{GetColor, SetColor},
208 split::SplitFace,
209 },
210 Core,
211 };
212
213 #[test]
214 fn split_face_should_keep_color() {
215 let mut core = Core::new();
216
217 let tetrahedron = Shell::tetrahedron(
218 [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
219 &mut core,
220 );
221 let triangle = tetrahedron.abc;
222
223 let color = Color::default();
224 triangle.face.region().set_color(color, &mut core);
225
226 let split_line = [
227 (&triangle.half_edges[0], [0.5]),
228 (&triangle.half_edges[1], [0.5]),
229 ];
230 let (_shell, [face_a, face_b]) =
231 tetrahedron
232 .shell
233 .split_face(&triangle.face, split_line, &mut core);
234
235 assert_eq!(face_a.region().get_color(&mut core), Some(color));
236 assert_eq!(face_b.region().get_color(&mut core), Some(color));
237 }
238}