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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use fj_interop::ext::ArrayExt;
use fj_math::Point;
use itertools::Itertools;
use crate::{
objects::{Face, HalfEdge, Shell},
operations::{
build::{BuildFace, BuildHalfEdge},
insert::Insert,
split::SplitEdge,
update::{
UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateRegion, UpdateShell,
},
},
services::Services,
storage::Handle,
};
/// Split a face into two
pub trait SplitFace: Sized {
/// Split the face into two
///
/// The line that splits the face is defined by two points, each specified
/// in local coordinates of an edge.
///
/// # Panics
///
/// Panics, if the half-edges are not part of the boundary of the provided
/// face.
///
/// # Implementation Note
///
/// The way the split line is specified is rather inconvenient, and not very
/// flexible. This is an artifact of the current implementation, and more
/// flexible and convenient ways to split the face (like an arbitrary curve)
/// can be provided later.
#[must_use]
fn split_face(
&self,
face: &Handle<Face>,
line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
services: &mut Services,
) -> (Self, [Handle<Face>; 2]);
}
impl SplitFace for Shell {
fn split_face(
&self,
face: &Handle<Face>,
line: [(&Handle<HalfEdge>, impl Into<Point<1>>); 2],
services: &mut Services,
) -> (Self, [Handle<Face>; 2]) {
// The code below might assume that the half-edges that define the line
// are part of the face's exterior. Let's make that explicit here.
//
// This is actually the only time we're using `face` in this method, as
// it's going to get replaced with a new version as soon as we split the
// edges. We could probably do without it, but not taking it would
// probably make validating that both half-edges belong to the same face
// more difficult, as well as make the method signature less intuitive.
//
// Something to think about though!
{
let [(a, _), (b, _)] = line.each_ref_ext();
let exterior = face.region().exterior();
assert!(exterior.half_edges().contains(a));
assert!(exterior.half_edges().contains(b));
}
let mut self_ = self.clone();
let [[a, b], [c, d]] = line.map(|(half_edge, point)| {
let (shell, [[a, b], _]) =
self_.split_edge(half_edge, point, services);
self_ = shell;
[a, b]
});
// The original face doesn't exist in the updated shell, as it's been
// replaced by a new version due to the edge splitting. Let's find the
// face that replaced it.
let mut updated_face_after_split_edges = None;
for f in self_.faces() {
let half_edges = f.region().exterior().half_edges();
if half_edges.contains(&a)
&& half_edges.contains(&b)
&& half_edges.contains(&c)
&& half_edges.contains(&d)
{
assert!(
updated_face_after_split_edges.is_none(),
"There should never be two faces that share half-edges"
);
updated_face_after_split_edges = Some(f);
}
}
let updated_face_after_split_edges = updated_face_after_split_edges
.expect("Updated shell must contain updated face");
// Build the edge that's going to divide the new faces.
let dividing_half_edge_a_to_d = HalfEdge::line_segment(
[b.start_position(), d.start_position()],
None,
services,
)
.update_start_vertex(|_| b.start_vertex().clone())
.insert(services);
let dividing_half_edge_c_to_b = HalfEdge::from_sibling(
&dividing_half_edge_a_to_d,
d.start_vertex().clone(),
)
.insert(services);
let mut half_edges_of_face_starting_at_b =
updated_face_after_split_edges
.region()
.exterior()
.half_edges()
.iter()
.cloned()
.cycle()
.skip_while(|half_edge| half_edge != &b);
let half_edges_b_to_c_inclusive = half_edges_of_face_starting_at_b
.take_while_ref(|half_edge| half_edge != &d);
let split_face_a = Face::unbound(
updated_face_after_split_edges.surface().clone(),
services,
)
.update_region(|region| {
region
.update_exterior(|cycle| {
cycle
.add_half_edges(half_edges_b_to_c_inclusive)
.add_half_edges([dividing_half_edge_c_to_b])
.insert(services)
})
.insert(services)
})
.insert(services);
// The previous operation has moved the iterator along.
let half_edges_of_face_starting_at_d = half_edges_of_face_starting_at_b;
let half_edges_d_to_a_inclusive = half_edges_of_face_starting_at_d
.take_while(|half_edge| half_edge != &b);
let split_face_b = Face::unbound(
updated_face_after_split_edges.surface().clone(),
services,
)
.update_region(|region| {
region
.update_exterior(|cycle| {
cycle
.add_half_edges(half_edges_d_to_a_inclusive)
.add_half_edges([dividing_half_edge_a_to_d])
.insert(services)
})
.insert(services)
})
.insert(services);
let faces = [split_face_a, split_face_b];
let self_ = self_
.replace_face(updated_face_after_split_edges, |_| faces.clone());
(self_, faces)
}
}