1use std::collections::HashSet;
32
33use crate::collider::Collider;
34use crate::disjoint_sets::DisjointSets;
35use crate::impl_mesh::ManifoldImpl;
36use crate::linalg::{dot, IVec3, Vec2, Vec3, Vec4};
37use crate::sort::get_face_box_morton;
38use crate::types::{Box as BBox, Halfedge, OpType, RayHit, TriRef};
39
40#[derive(Clone, Default)]
48pub struct Intersections {
49 pub p1q2: Vec<[i32; 2]>,
51 pub x12: Vec<i32>,
53 pub v12: Vec<Vec3>,
55}
56
57pub struct Boolean3 {
63 pub xv12: Intersections,
64 pub xv21: Intersections,
65 pub w03: Vec<i32>,
66 pub w30: Vec<i32>,
67 pub expand_p: bool,
68 pub valid: bool,
69}
70
71#[inline]
80fn with_sign(pos: bool, v: f64) -> f64 {
81 if pos { v } else { -v }
82}
83
84fn interpolate(a_l: Vec3, a_r: Vec3, x: f64) -> Vec2 {
88 let dx_l = x - a_l.x;
89 let dx_r = x - a_r.x;
90 debug_assert!(
91 dx_l * dx_r <= 0.0,
92 "Boolean manifold error: not in domain"
93 );
94 let use_l = dx_l.abs() < dx_r.abs();
95 let d_lr = a_r - a_l;
96 let lambda = (if use_l { dx_l } else { dx_r }) / d_lr.x;
97 if !lambda.is_finite() || !d_lr.y.is_finite() || !d_lr.z.is_finite() {
98 return Vec2::new(a_l.y, a_l.z);
99 }
100 Vec2::new(
101 lambda * d_lr.y + if use_l { a_l.y } else { a_r.y },
102 lambda * d_lr.z + if use_l { a_l.z } else { a_r.z },
103 )
104}
105
106fn intersect_edges(a_l: Vec3, a_r: Vec3, b_l: Vec3, b_r: Vec3) -> Vec4 {
109 let dy_l = b_l.y - a_l.y;
110 let dy_r = b_r.y - a_r.y;
111 debug_assert!(
112 dy_l * dy_r <= 0.0,
113 "Boolean manifold error: no intersection"
114 );
115 let use_l = dy_l.abs() < dy_r.abs();
116 let dx = a_r.x - a_l.x;
117 let mut lambda = (if use_l { dy_l } else { dy_r }) / (dy_l - dy_r);
118 if !lambda.is_finite() {
119 lambda = 0.0;
120 }
121 let x = lambda * dx + if use_l { a_l.x } else { a_r.x };
122 let a_dy = a_r.y - a_l.y;
123 let b_dy = b_r.y - b_l.y;
124 let use_a = a_dy.abs() < b_dy.abs();
125 let y = lambda * (if use_a { a_dy } else { b_dy })
126 + if use_l {
127 if use_a { a_l.y } else { b_l.y }
128 } else if use_a {
129 a_r.y
130 } else {
131 b_r.y
132 };
133 let z = lambda * (a_r.z - a_l.z) + if use_l { a_l.z } else { a_r.z };
134 let w = lambda * (b_r.z - b_l.z) + if use_l { b_l.z } else { b_r.z };
135 Vec4::new(x, y, z, w)
136}
137
138#[inline]
141fn shadows(p: f64, q: f64, dir: f64) -> bool {
142 if p == q { dir < 0.0 } else { p < q }
143}
144
145fn shadow01(
152 a0: usize,
153 b1: usize,
154 in_a: &ManifoldImpl,
155 in_b: &ManifoldImpl,
156 expand_p: bool,
157 forward: bool,
158) -> (i32, Vec2) {
159 let b1s = in_b.halfedge[b1].start_vert as usize;
160 let b1e = in_b.halfedge[b1].end_vert as usize;
161 let a0x = in_a.vert_pos[a0].x;
162 let b1sx = in_b.vert_pos[b1s].x;
163 let b1ex = in_b.vert_pos[b1e].x;
164 let a0xp = in_a.vert_normal[a0].x;
165 let b1sxp = in_b.vert_normal[b1s].x;
166 let b1exp = in_b.vert_normal[b1e].x;
167
168 let mut s01 = if forward {
169 shadows(a0x, b1ex, with_sign(expand_p, a0xp) - b1exp) as i32
170 - shadows(a0x, b1sx, with_sign(expand_p, a0xp) - b1sxp) as i32
171 } else {
172 shadows(b1sx, a0x, with_sign(expand_p, b1sxp) - a0xp) as i32
173 - shadows(b1ex, a0x, with_sign(expand_p, b1exp) - a0xp) as i32
174 };
175
176 let mut yz01 = Vec2::new(f64::NAN, f64::NAN);
177
178 if s01 != 0 {
179 yz01 = interpolate(in_b.vert_pos[b1s], in_b.vert_pos[b1e], in_a.vert_pos[a0].x);
180 let b1pair = in_b.halfedge[b1].paired_halfedge as usize;
181 let dir = in_b.face_normal[b1 / 3].y + in_b.face_normal[b1pair / 3].y;
182 if forward {
183 if !shadows(in_a.vert_pos[a0].y, yz01.x, -dir) {
184 s01 = 0;
185 }
186 } else if !shadows(yz01.x, in_a.vert_pos[a0].y, with_sign(expand_p, dir)) {
187 s01 = 0;
188 }
189 }
190 (s01, yz01)
191}
192
193fn kernel11(
198 p1: usize,
199 q1: usize,
200 in_p: &ManifoldImpl,
201 in_q: &ManifoldImpl,
202 expand_p: bool,
203) -> (i32, Vec4) {
204 let mut xyzz11 = Vec4::splat(f64::NAN);
205 let mut s11: i32 = 0;
206
207 let mut k: usize = 0;
208 let mut p_rl = [Vec3::splat(0.0); 2];
209 let mut q_rl = [Vec3::splat(0.0); 2];
210 let mut shadow_state = false;
211
212 let p0 = [
213 in_p.halfedge[p1].start_vert as usize,
214 in_p.halfedge[p1].end_vert as usize,
215 ];
216 for i in 0..2 {
217 let (s01, yz01) = shadow01(p0[i], q1, in_p, in_q, expand_p, true);
218 if yz01.x.is_finite() {
219 s11 += s01 * if i == 0 { -1 } else { 1 };
220 if k < 2 && (k == 0 || (s01 != 0) != shadow_state) {
221 shadow_state = s01 != 0;
222 p_rl[k] = in_p.vert_pos[p0[i]];
223 q_rl[k] = Vec3::new(p_rl[k].x, yz01.x, yz01.y);
224 k += 1;
225 }
226 }
227 }
228
229 let q0 = [
230 in_q.halfedge[q1].start_vert as usize,
231 in_q.halfedge[q1].end_vert as usize,
232 ];
233 for i in 0..2 {
234 let (s10, yz10) = shadow01(q0[i], p1, in_q, in_p, expand_p, false);
235 if yz10.x.is_finite() {
236 s11 += s10 * if i == 0 { -1 } else { 1 };
237 if k < 2 && (k == 0 || (s10 != 0) != shadow_state) {
238 shadow_state = s10 != 0;
239 q_rl[k] = in_q.vert_pos[q0[i]];
240 p_rl[k] = Vec3::new(q_rl[k].x, yz10.x, yz10.y);
241 k += 1;
242 }
243 }
244 }
245
246 if s11 == 0 {
247 xyzz11 = Vec4::splat(f64::NAN);
248 } else {
249 debug_assert_eq!(k, 2, "Boolean manifold error: s11");
250 xyzz11 = intersect_edges(p_rl[0], p_rl[1], q_rl[0], q_rl[1]);
251
252 let p1pair = in_p.halfedge[p1].paired_halfedge as usize;
253 let dir_p = in_p.face_normal[p1 / 3].z + in_p.face_normal[p1pair / 3].z;
254 let q1pair = in_q.halfedge[q1].paired_halfedge as usize;
255 let dir_q = in_q.face_normal[q1 / 3].z + in_q.face_normal[q1pair / 3].z;
256 if !shadows(xyzz11.z, xyzz11.w, with_sign(expand_p, dir_p) - dir_q) {
257 s11 = 0;
258 }
259 }
260
261 (s11, xyzz11)
262}
263
264fn kernel02(
269 a0: usize,
270 b2: usize,
271 in_a: &ManifoldImpl,
272 in_b: &ManifoldImpl,
273 expand_p: bool,
274 forward: bool,
275) -> (i32, f64) {
276 let mut s02: i32 = 0;
277 let mut z02: f64 = 0.0;
278
279 let mut k: usize = 0;
280 let mut yzz_rl = [Vec3::splat(0.0); 2];
281 let mut shadow_state = false;
282
283 for i in 0..3 {
284 let b1 = 3 * b2 + i;
285 let edge_b = in_b.halfedge[b1];
286 let b1f = if edge_b.is_forward() {
287 b1
288 } else {
289 edge_b.paired_halfedge as usize
290 };
291
292 let (s01, yz01) = shadow01(a0, b1f, in_a, in_b, expand_p, forward);
293 if yz01.x.is_finite() {
294 s02 += s01 * if forward == edge_b.is_forward() { -1 } else { 1 };
295 if k < 2 && (k == 0 || (s01 != 0) != shadow_state) {
296 shadow_state = s01 != 0;
297 yzz_rl[k] = Vec3::new(yz01.x, yz01.y, yz01.y);
298 k += 1;
299 }
300 }
301 }
302
303 if s02 == 0 {
304 z02 = f64::NAN;
305 } else {
306 debug_assert_eq!(k, 2, "Boolean manifold error: s02");
307 let vert_pos_a = in_a.vert_pos[a0];
308 z02 = interpolate(yzz_rl[0], yzz_rl[1], vert_pos_a.y).y;
309 if forward {
310 if !shadows(vert_pos_a.z, z02, -in_b.face_normal[b2].z) {
311 s02 = 0;
312 }
313 } else if !shadows(z02, vert_pos_a.z, with_sign(expand_p, in_b.face_normal[b2].z)) {
314 s02 = 0;
315 }
316 }
317 (s02, z02)
318}
319
320fn kernel12(
325 a1: usize,
326 b2: usize,
327 in_a: &ManifoldImpl,
328 in_b: &ManifoldImpl,
329 in_p: &ManifoldImpl,
330 in_q: &ManifoldImpl,
331 expand_p: bool,
332 forward: bool,
333) -> (i32, Vec3) {
334 let mut x12: i32 = 0;
335 let mut v12 = Vec3::splat(f64::NAN);
336
337 let mut k: usize = 0;
338 let mut xzy_lr0 = [Vec3::splat(0.0); 2];
339 let mut xzy_lr1 = [Vec3::splat(0.0); 2];
340 let mut shadow_state = false;
341
342 let edge_a = in_a.halfedge[a1];
343
344 for vert_a in [edge_a.start_vert as usize, edge_a.end_vert as usize] {
345 let (s, z) = kernel02(vert_a, b2, in_a, in_b, expand_p, forward);
346 if z.is_finite() {
347 x12 += s * if (vert_a == edge_a.start_vert as usize) == forward { 1 } else { -1 };
348 if k < 2 && (k == 0 || (s != 0) != shadow_state) {
349 shadow_state = s != 0;
350 let pos = in_a.vert_pos[vert_a];
351 xzy_lr0[k] = Vec3::new(pos.x, pos.z, pos.y);
352 xzy_lr1[k] = xzy_lr0[k];
353 xzy_lr1[k].y = z;
354 k += 1;
355 }
356 }
357 }
358
359 for i in 0..3 {
360 let b1 = 3 * b2 + i;
361 let edge_b = in_b.halfedge[b1];
362 let b1f = if edge_b.is_forward() {
363 b1
364 } else {
365 edge_b.paired_halfedge as usize
366 };
367 let (s, xyzz) = if forward {
368 kernel11(a1, b1f, in_p, in_q, expand_p)
369 } else {
370 kernel11(b1f, a1, in_p, in_q, expand_p)
371 };
372 if xyzz.x.is_finite() {
373 x12 -= s * if edge_b.is_forward() { 1 } else { -1 };
374 if k < 2 && (k == 0 || (s != 0) != shadow_state) {
375 shadow_state = s != 0;
376 xzy_lr0[k] = Vec3::new(xyzz.x, xyzz.z, xyzz.y);
377 xzy_lr1[k] = xzy_lr0[k];
378 xzy_lr1[k].y = xyzz.w;
379 if !forward {
380 let tmp = xzy_lr0[k].y;
381 xzy_lr0[k].y = xzy_lr1[k].y;
382 xzy_lr1[k].y = tmp;
383 }
384 k += 1;
385 }
386 }
387 }
388
389 if x12 == 0 {
390 v12 = Vec3::splat(f64::NAN);
391 } else {
392 debug_assert_eq!(k, 2, "Boolean manifold error: v12");
393 let xzyy = intersect_edges(xzy_lr0[0], xzy_lr0[1], xzy_lr1[0], xzy_lr1[1]);
394 v12.x = xzyy.x;
395 v12.y = xzyy.z;
396 v12.z = xzyy.y;
397 }
398 (x12, v12)
399}
400
401fn intersect12(
406 in_p: &ManifoldImpl,
407 in_q: &ManifoldImpl,
408 expand_p: bool,
409 forward: bool,
410) -> Intersections {
411 let (a, b) = if forward { (in_p, in_q) } else { (in_q, in_p) };
413
414 let (face_box, face_morton) = get_face_box_morton(b);
415 let collider = Collider::new(face_box, face_morton);
416
417 let mut result = Intersections::default();
418
419 let n = a.halfedge.len();
424 let per_edge: Vec<Vec<([i32; 2], i32, Vec3)>> =
425 crate::par::maybe_par_map(n, 10_000, |query_idx| {
426 let mut local: Vec<([i32; 2], i32, Vec3)> = Vec::new();
427 if !a.halfedge[query_idx].is_forward() {
428 return local;
429 }
430 let query = BBox::from_points(
431 a.vert_pos[a.halfedge[query_idx].start_vert as usize],
432 a.vert_pos[a.halfedge[query_idx].end_vert as usize],
433 );
434 collider.collisions_one(&query, query_idx, |query_idx, face_idx| {
435 let (x, v) =
436 kernel12(query_idx, face_idx, a, b, in_p, in_q, expand_p, forward);
437 if v.x.is_finite() {
438 let pair = if forward {
439 [query_idx as i32, face_idx as i32]
440 } else {
441 [face_idx as i32, query_idx as i32]
442 };
443 local.push((pair, x, v));
444 }
445 });
446 local
447 });
448 for local in per_edge {
449 for (pair, x, v) in local {
450 result.p1q2.push(pair);
451 result.x12.push(x);
452 result.v12.push(v);
453 }
454 }
455
456 let mut indices: Vec<usize> = (0..result.p1q2.len()).collect();
458 let sort_idx = if forward { 0 } else { 1 };
459 indices.sort_by(|&a, &b| {
460 let pa = result.p1q2[a];
461 let pb = result.p1q2[b];
462 pa[sort_idx]
463 .cmp(&pb[sort_idx])
464 .then(pa[1 - sort_idx].cmp(&pb[1 - sort_idx]))
465 });
466
467 let old_p1q2 = result.p1q2.clone();
468 let old_x12 = result.x12.clone();
469 let old_v12 = result.v12.clone();
470 for (new_i, &old_i) in indices.iter().enumerate() {
471 result.p1q2[new_i] = old_p1q2[old_i];
472 result.x12[new_i] = old_x12[old_i];
473 result.v12[new_i] = old_v12[old_i];
474 }
475
476 result
477}
478
479fn winding03(
489 in_p: &ManifoldImpl,
490 in_q: &ManifoldImpl,
491 p1q2: &[[i32; 2]],
492 expand_p: bool,
493 forward: bool,
494) -> Vec<i32> {
495 let (a, b) = if forward { (in_p, in_q) } else { (in_q, in_p) };
496 let sort_idx = if forward { 0 } else { 1 };
497
498 let u_a = DisjointSets::new(a.vert_pos.len() as u32);
500 for edge in 0..a.halfedge.len() {
501 let he = &a.halfedge[edge];
502 if !he.is_forward() {
503 continue;
504 }
505 let is_broken = p1q2
507 .binary_search_by(|pair| pair[sort_idx].cmp(&(edge as i32)))
508 .is_ok();
509 if !is_broken {
510 u_a.unite(he.start_vert as u32, he.end_vert as u32);
511 }
512 }
513
514 let mut components = HashSet::new();
516 for v in 0..a.vert_pos.len() {
517 components.insert(u_a.find(v as u32));
518 }
519 let verts: Vec<usize> = components.into_iter().map(|v| v as usize).collect();
520
521 let (face_box, face_morton) = get_face_box_morton(b);
523 let collider = Collider::new(face_box, face_morton);
524
525 let mut w03 = vec![0i32; a.vert_pos.len()];
527
528 let query_boxes: Vec<(usize, BBox)> = verts.iter().map(|&vi| {
532 let pt = a.vert_pos[vi];
533 let qbox = BBox::from_points(
534 Vec3::new(pt.x, pt.y, f64::NEG_INFINITY),
535 Vec3::new(pt.x, pt.y, f64::INFINITY),
536 );
537 (vi, qbox)
538 }).collect();
539
540 let sums: Vec<(usize, i32)> =
544 crate::par::maybe_par_map(query_boxes.len(), 1_000, |qi| {
545 let (vi, ref qbox) = query_boxes[qi];
546 let mut sum = 0i32;
547 collider.collisions_one(qbox, 0, |_qi, face_idx| {
548 let (s02, z02) = kernel02(vi, face_idx, a, b, expand_p, forward);
549 if z02.is_finite() {
550 sum += s02 * if forward { 1 } else { -1 };
551 }
552 });
553 (vi, sum)
554 });
555 for (vi, sum) in sums {
556 w03[vi] += sum;
557 }
558
559 for i in 0..w03.len() {
561 let root = u_a.find(i as u32) as usize;
562 if root != i {
563 w03[i] = w03[root];
564 }
565 }
566
567 w03
568}
569
570impl Boolean3 {
575 pub fn new(in_p: &ManifoldImpl, in_q: &ManifoldImpl, op: OpType) -> Self {
577 let expand_p = op == OpType::Add;
578
579 if in_p.is_empty() || in_q.is_empty() || !in_p.bbox.does_overlap_box(&in_q.bbox) {
580 return Boolean3 {
581 xv12: Intersections::default(),
582 xv21: Intersections::default(),
583 w03: vec![0; in_p.num_vert()],
584 w30: vec![0; in_q.num_vert()],
585 expand_p,
586 valid: true,
587 };
588 }
589
590 let xv12 = intersect12(in_p, in_q, expand_p, true);
592 let xv21 = intersect12(in_p, in_q, expand_p, false);
593
594 if xv12.x12.len() > i32::MAX as usize || xv21.x12.len() > i32::MAX as usize {
595 return Boolean3 {
596 xv12: Intersections::default(),
597 xv21: Intersections::default(),
598 w03: Vec::new(),
599 w30: Vec::new(),
600 expand_p,
601 valid: false,
602 };
603 }
604
605 let w03 = winding03(in_p, in_q, &xv12.p1q2, expand_p, true);
607 let w30 = winding03(in_p, in_q, &xv21.p1q2, expand_p, false);
608
609 Boolean3 {
610 xv12,
611 xv21,
612 w03,
613 w30,
614 expand_p,
615 valid: true,
616 }
617 }
618}
619
620fn extract_tri_vert(mesh: &ManifoldImpl) -> Vec<IVec3> {
625 (0..mesh.num_tri())
626 .map(|tri| {
627 IVec3::new(
628 mesh.halfedge[3 * tri].start_vert,
629 mesh.halfedge[3 * tri + 1].start_vert,
630 mesh.halfedge[3 * tri + 2].start_vert,
631 )
632 })
633 .collect()
634}
635
636fn extract_tri_prop(mesh: &ManifoldImpl) -> Vec<IVec3> {
637 (0..mesh.num_tri())
638 .map(|tri| {
639 IVec3::new(
640 mesh.halfedge[3 * tri].prop_vert,
641 mesh.halfedge[3 * tri + 1].prop_vert,
642 mesh.halfedge[3 * tri + 2].prop_vert,
643 )
644 })
645 .collect()
646}
647
648fn property_row(mesh: &ManifoldImpl, row: usize, width: usize) -> Vec<f64> {
649 if mesh.num_prop == 0 {
650 vec![0.0; width]
651 } else {
652 let mut out = vec![0.0; width];
653 let src = &mesh.properties[row * mesh.num_prop..(row + 1) * mesh.num_prop];
654 out[..src.len()].copy_from_slice(src);
655 out
656 }
657}
658
659pub fn compose_meshes(meshes: &[ManifoldImpl]) -> ManifoldImpl {
663 if meshes.is_empty() {
664 return ManifoldImpl::new();
665 }
666 if meshes.len() == 1 {
667 return meshes[0].clone();
668 }
669
670 let num_prop = meshes.iter().map(|m| m.num_prop).max().unwrap_or(0);
671 let mut vert_pos = Vec::new();
672 let mut properties = Vec::new();
673 let mut tri_vert = Vec::new();
674 let mut tri_prop = Vec::new();
675 let mut vert_offset = 0i32;
676 let mut prop_offset = 0i32;
677
678 for mesh in meshes {
679 vert_pos.extend_from_slice(&mesh.vert_pos);
680
681 let old_tri_vert = extract_tri_vert(mesh);
682 let old_tri_prop = extract_tri_prop(mesh);
683 tri_vert.extend(old_tri_vert.into_iter().map(|t| {
684 IVec3::new(t.x + vert_offset, t.y + vert_offset, t.z + vert_offset)
685 }));
686 tri_prop.extend(old_tri_prop.into_iter().map(|t| {
687 IVec3::new(t.x + prop_offset, t.y + prop_offset, t.z + prop_offset)
688 }));
689
690 if num_prop > 0 {
691 let prop_rows = mesh.num_prop_vert();
692 for row in 0..prop_rows {
693 properties.extend(property_row(mesh, row, num_prop));
694 }
695 prop_offset += prop_rows as i32;
696 } else {
697 prop_offset += mesh.num_prop_vert() as i32;
698 }
699 vert_offset += mesh.num_vert() as i32;
700 }
701
702 let mut all_tri_refs: Vec<TriRef> = Vec::new();
705 let mut merged_transforms = std::collections::BTreeMap::new();
706 let mut tri_offset = 0i32;
707 for mesh in meshes {
708 let mesh_tri_count = mesh.num_tri() as i32;
709 for tri_ref in &mesh.mesh_relation.tri_ref {
710 all_tri_refs.push(TriRef {
711 mesh_id: tri_ref.mesh_id,
712 original_id: tri_ref.original_id,
713 face_id: tri_ref.face_id,
714 coplanar_id: tri_ref.coplanar_id + tri_offset,
715 });
716 }
717 for (id, rel) in &mesh.mesh_relation.mesh_id_transform {
718 merged_transforms.insert(*id, rel.clone());
719 }
720 tri_offset += mesh_tri_count;
721 }
722
723 let mut out = ManifoldImpl::new();
724 out.vert_pos = vert_pos;
725 out.num_prop = num_prop;
726 out.properties = properties;
727 out.create_halfedges(&tri_prop, &tri_vert);
728 out.mesh_relation.tri_ref = all_tri_refs;
731 out.mesh_relation.mesh_id_transform = merged_transforms;
732 out.mesh_relation.original_id = -1;
733 out.calculate_bbox();
734 out.set_epsilon(-1.0, false);
735 crate::edge_op::remove_degenerates(&mut out, 0);
737 out.sort_geometry();
738 out.increment_mesh_ids();
739 out.set_normals_and_coplanar();
740 out
741}
742
743pub fn boolean(mesh_a: &ManifoldImpl, mesh_b: &ManifoldImpl, op: OpType) -> ManifoldImpl {
752 if mesh_a.is_empty() {
753 return match op {
754 OpType::Add => mesh_b.clone(),
755 OpType::Intersect => ManifoldImpl::new(),
756 OpType::Subtract => ManifoldImpl::new(),
757 };
758 }
759 if mesh_b.is_empty() {
760 return match op {
761 OpType::Add | OpType::Subtract => mesh_a.clone(),
762 OpType::Intersect => ManifoldImpl::new(),
763 };
764 }
765
766 if !mesh_a.bbox.does_overlap_box(&mesh_b.bbox) {
767 match op {
770 OpType::Add => return compose_meshes(&[mesh_a.clone(), mesh_b.clone()]),
771 OpType::Intersect => return ManifoldImpl::new(),
772 OpType::Subtract => {} }
774 }
775
776 let bool3 = Boolean3::new(mesh_a, mesh_b, op);
778 if !bool3.valid {
779 return ManifoldImpl::new();
780 }
781
782 let result = crate::boolean_result::boolean_result(mesh_a, mesh_b, op, &bool3);
783 result
784}
785
786pub fn ray_cast(mesh: &ManifoldImpl, origin: Vec3, endpoint: Vec3) -> Vec<RayHit> {
793 if mesh.is_empty() {
794 return vec![];
795 }
796 let dir = endpoint - origin;
797 if dot(dir, dir) == 0.0 {
798 return vec![];
799 }
800
801 let mut ray_impl = ManifoldImpl::new();
804 ray_impl.vert_pos = vec![origin, endpoint];
805 ray_impl.vert_normal = vec![Vec3::splat(0.0), Vec3::splat(0.0)];
806 ray_impl.halfedge = vec![
807 Halfedge { start_vert: 0, end_vert: 1, paired_halfedge: 1, prop_vert: 0 },
808 Halfedge { start_vert: 1, end_vert: 0, paired_halfedge: 0, prop_vert: 0 },
809 ];
810 ray_impl.face_normal = vec![Vec3::splat(0.0)];
811
812 let (face_box, face_morton) = get_face_box_morton(mesh);
814 let collider = Collider::new(face_box, face_morton);
815
816 let ray_box = BBox::from_points(
818 Vec3::new(origin.x.min(endpoint.x), origin.y.min(endpoint.y), origin.z.min(endpoint.z)),
819 Vec3::new(origin.x.max(endpoint.x), origin.y.max(endpoint.y), origin.z.max(endpoint.z)),
820 );
821
822 let abs_dir = Vec3::new(dir.x.abs(), dir.y.abs(), dir.z.abs());
824 let t_axis = if abs_dir.x > abs_dir.y && abs_dir.x > abs_dir.z {
825 0usize
826 } else if abs_dir.y > abs_dir.z {
827 1
828 } else {
829 2
830 };
831
832 let mut hits: Vec<RayHit> = Vec::new();
833
834 collider.collisions_with_boxes(std::slice::from_ref(&ray_box), false, |_qi, tri| {
836 let (s, v) = kernel12(0, tri, &ray_impl, mesh, &ray_impl, mesh, false, true);
838 if s != 0 && v.x.is_finite() {
839 let origin_t = [origin.x, origin.y, origin.z][t_axis];
841 let dir_t = [dir.x, dir.y, dir.z][t_axis];
842 let v_t = [v.x, v.y, v.z][t_axis];
843 let t = (v_t - origin_t) / dir_t;
844 if t >= 0.0 && t <= 1.0 {
845 hits.push(RayHit {
846 face_id: tri as u64,
847 distance: t,
848 position: v,
849 normal: mesh.face_normal[tri],
850 });
851 }
852 }
853 });
854
855 hits.sort_by(|a, b| a.distance.partial_cmp(&b.distance).unwrap_or(std::cmp::Ordering::Equal));
856 hits
857}
858
859#[cfg(test)]
860#[path = "boolean3_tests.rs"]
861mod tests;