1use glam::Vec3;
7
8#[derive(Debug, Clone)]
11pub struct CellSliceResult {
12 pub vertices: Vec<Vec3>,
14 pub interpolation: Vec<(u32, u32, f32)>,
17}
18
19impl CellSliceResult {
20 #[must_use]
22 pub fn empty() -> Self {
23 Self {
24 vertices: Vec::new(),
25 interpolation: Vec::new(),
26 }
27 }
28
29 #[must_use]
31 pub fn has_intersection(&self) -> bool {
32 self.vertices.len() >= 3
33 }
34}
35
36#[must_use]
49pub fn slice_tet(
50 v0: Vec3,
51 v1: Vec3,
52 v2: Vec3,
53 v3: Vec3,
54 plane_origin: Vec3,
55 plane_normal: Vec3,
56) -> CellSliceResult {
57 let verts = [v0, v1, v2, v3];
58
59 let d: [f32; 4] = std::array::from_fn(|i| (verts[i] - plane_origin).dot(plane_normal));
61
62 let mut intersections = Vec::new();
64 let mut interp_data = Vec::new();
65
66 let edges = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)];
68
69 for &(i, j) in &edges {
70 if d[i] * d[j] < 0.0 {
72 let t = d[i] / (d[i] - d[j]);
74 let point = verts[i].lerp(verts[j], t);
75 intersections.push(point);
76 interp_data.push((i as u32, j as u32, t));
77 }
78 }
79
80 if intersections.len() >= 3 {
82 order_polygon_vertices(&mut intersections, &mut interp_data, plane_normal);
83 }
84
85 CellSliceResult {
86 vertices: intersections,
87 interpolation: interp_data,
88 }
89}
90
91#[must_use]
104pub fn slice_hex(vertices: [Vec3; 8], plane_origin: Vec3, plane_normal: Vec3) -> CellSliceResult {
105 let tet_indices = [
108 [0, 1, 3, 4],
109 [1, 2, 3, 6],
110 [1, 4, 5, 6],
111 [3, 4, 6, 7],
112 [1, 3, 4, 6], ];
114
115 let mut all_vertices = Vec::new();
116 let mut all_interp = Vec::new();
117
118 for tet in &tet_indices {
119 let result = slice_tet(
120 vertices[tet[0]],
121 vertices[tet[1]],
122 vertices[tet[2]],
123 vertices[tet[3]],
124 plane_origin,
125 plane_normal,
126 );
127
128 for (local_a, local_b, t) in result.interpolation {
130 let hex_a = tet[local_a as usize] as u32;
131 let hex_b = tet[local_b as usize] as u32;
132 all_interp.push((hex_a, hex_b, t));
133 }
134 all_vertices.extend(result.vertices);
135 }
136
137 merge_slice_vertices(&mut all_vertices, &mut all_interp);
139
140 if all_vertices.len() >= 3 {
142 order_polygon_vertices(&mut all_vertices, &mut all_interp, plane_normal);
143 }
144
145 CellSliceResult {
146 vertices: all_vertices,
147 interpolation: all_interp,
148 }
149}
150
151fn order_polygon_vertices(
155 vertices: &mut Vec<Vec3>,
156 interp: &mut Vec<(u32, u32, f32)>,
157 normal: Vec3,
158) {
159 if vertices.len() < 3 {
160 return;
161 }
162
163 let centroid: Vec3 = vertices.iter().copied().sum::<Vec3>() / vertices.len() as f32;
165
166 let ref_dir = if let Some(first) = vertices.first() {
168 (*first - centroid).normalize()
169 } else {
170 return;
171 };
172
173 let mut indices: Vec<usize> = (0..vertices.len()).collect();
175
176 indices.sort_by(|&a, &b| {
177 let va = (vertices[a] - centroid).normalize();
178 let vb = (vertices[b] - centroid).normalize();
179
180 let cross_a = ref_dir.cross(va);
183 let cross_b = ref_dir.cross(vb);
184
185 let angle_a = cross_a.dot(normal).atan2(ref_dir.dot(va));
186 let angle_b = cross_b.dot(normal).atan2(ref_dir.dot(vb));
187
188 angle_a
189 .partial_cmp(&angle_b)
190 .unwrap_or(std::cmp::Ordering::Equal)
191 });
192
193 let sorted_verts: Vec<Vec3> = indices.iter().map(|&i| vertices[i]).collect();
195 let sorted_interp: Vec<_> = indices.iter().map(|&i| interp[i]).collect();
196
197 *vertices = sorted_verts;
198 *interp = sorted_interp;
199}
200
201fn merge_slice_vertices(vertices: &mut Vec<Vec3>, interp: &mut Vec<(u32, u32, f32)>) {
206 const EPSILON: f32 = 1e-4;
208
209 if vertices.len() <= 1 {
210 return;
211 }
212
213 let mut merged_verts = Vec::new();
214 let mut merged_interp = Vec::new();
215
216 for i in 0..vertices.len() {
217 let v = vertices[i];
218 let mut is_duplicate = false;
219
220 for merged_v in &merged_verts {
221 let diff: Vec3 = v - *merged_v;
222 if diff.length_squared() < EPSILON * EPSILON {
223 is_duplicate = true;
224 break;
225 }
226 }
227
228 if !is_duplicate {
229 merged_verts.push(v);
230 merged_interp.push(interp[i]);
231 }
232 }
233
234 *vertices = merged_verts;
235 *interp = merged_interp;
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_slice_tet_no_intersection() {
244 let result = slice_tet(
246 Vec3::new(0.0, 1.0, 0.0),
247 Vec3::new(1.0, 1.0, 0.0),
248 Vec3::new(0.5, 1.0, 1.0),
249 Vec3::new(0.5, 2.0, 0.5),
250 Vec3::ZERO,
251 Vec3::Y,
252 );
253 assert!(result.vertices.is_empty());
254 assert!(!result.has_intersection());
255 }
256
257 #[test]
258 fn test_slice_tet_triangle() {
259 let result = slice_tet(
262 Vec3::new(0.0, -1.0, 0.0), Vec3::new(1.0, 1.0, 0.0), Vec3::new(-1.0, 1.0, 0.0), Vec3::new(0.0, 1.0, 1.0), Vec3::ZERO,
267 Vec3::Y,
268 );
269 assert_eq!(result.vertices.len(), 3);
270 assert!(result.has_intersection());
271
272 assert_eq!(result.interpolation.len(), 3);
274 for (_, _, t) in &result.interpolation {
275 assert!(*t >= 0.0 && *t <= 1.0);
276 }
277 }
278
279 #[test]
280 fn test_slice_tet_quad() {
281 let result = slice_tet(
284 Vec3::new(0.0, -1.0, 0.0), Vec3::new(1.0, -1.0, 0.0), Vec3::new(0.5, 1.0, 0.0), Vec3::new(0.5, 1.0, 1.0), Vec3::ZERO,
289 Vec3::Y,
290 );
291 assert_eq!(result.vertices.len(), 4);
292 assert!(result.has_intersection());
293 }
294
295 #[test]
296 fn test_slice_tet_interpolation_values() {
297 let v0 = Vec3::new(0.0, -1.0, 0.0);
299 let v1 = Vec3::new(0.0, 1.0, 0.0);
300 let v2 = Vec3::new(1.0, 0.0, 0.0);
301 let v3 = Vec3::new(0.0, 0.0, 1.0);
302
303 let result = slice_tet(v0, v1, v2, v3, Vec3::ZERO, Vec3::Y);
304
305 for (i, (a, b, t)) in result.interpolation.iter().enumerate() {
308 let verts = [v0, v1, v2, v3];
309 let computed = verts[*a as usize].lerp(verts[*b as usize], *t);
310 let diff = (computed - result.vertices[i]).length();
311 assert!(diff < 1e-5, "Interpolation mismatch at index {}", i);
312 }
313 }
314
315 #[test]
316 fn test_slice_hex_no_intersection() {
317 let vertices = [
319 Vec3::new(0.0, 1.0, 0.0),
320 Vec3::new(1.0, 1.0, 0.0),
321 Vec3::new(1.0, 1.0, 1.0),
322 Vec3::new(0.0, 1.0, 1.0),
323 Vec3::new(0.0, 2.0, 0.0),
324 Vec3::new(1.0, 2.0, 0.0),
325 Vec3::new(1.0, 2.0, 1.0),
326 Vec3::new(0.0, 2.0, 1.0),
327 ];
328 let result = slice_hex(vertices, Vec3::ZERO, Vec3::Y);
329 assert!(result.vertices.is_empty());
330 }
331
332 #[test]
333 fn test_slice_hex_quad() {
334 let vertices = [
337 Vec3::new(-0.5, -0.5, -0.5),
338 Vec3::new(0.5, -0.5, -0.5),
339 Vec3::new(0.5, -0.5, 0.5),
340 Vec3::new(-0.5, -0.5, 0.5),
341 Vec3::new(-0.5, 0.5, -0.5),
342 Vec3::new(0.5, 0.5, -0.5),
343 Vec3::new(0.5, 0.5, 0.5),
344 Vec3::new(-0.5, 0.5, 0.5),
345 ];
346 let result = slice_hex(vertices, Vec3::ZERO, Vec3::Y);
347
348 assert!(result.has_intersection());
350 assert!(
351 result.vertices.len() >= 3,
352 "Expected at least 3 vertices, got {}",
353 result.vertices.len()
354 );
355
356 for v in &result.vertices {
358 assert!(v.y.abs() < 1e-5, "Vertex y={} should be 0", v.y);
359 }
360
361 let expected_corners = [
364 Vec3::new(-0.5, 0.0, -0.5),
365 Vec3::new(0.5, 0.0, -0.5),
366 Vec3::new(0.5, 0.0, 0.5),
367 Vec3::new(-0.5, 0.0, 0.5),
368 ];
369
370 for corner in &expected_corners {
371 let has_near = result
372 .vertices
373 .iter()
374 .any(|v| (*v - *corner).length() < 0.1);
375 assert!(has_near, "Expected vertex near corner {:?}", corner);
376 }
377 }
378
379 #[test]
380 fn test_polygon_ordering() {
381 let v0 = Vec3::new(0.0, -1.0, 0.0);
383 let v1 = Vec3::new(1.0, 1.0, 0.0);
384 let v2 = Vec3::new(-1.0, 1.0, 0.0);
385 let v3 = Vec3::new(0.0, 1.0, 1.0);
386
387 let result = slice_tet(v0, v1, v2, v3, Vec3::ZERO, Vec3::Y);
388
389 if result.vertices.len() >= 3 {
391 let e1 = result.vertices[1] - result.vertices[0];
392 let e2 = result.vertices[2] - result.vertices[0];
393 let computed_normal = e1.cross(e2).normalize();
394
395 let dot = computed_normal.dot(Vec3::Y);
397 assert!(
398 dot.abs() > 0.9,
399 "Polygon normal should align with plane normal"
400 );
401 }
402 }
403}