1use std::{collections::BinaryHeap, ops::ControlFlow};
2
3use wide::f64x4;
4
5#[cfg(target_arch = "x86_64")]
6use crate::leftpack::leftpack4;
7use crate::{
8 config::{DEFAULT_NEIGHBOR_QUEUE_CAPACITY, DEFAULT_SEARCH_STACK_CAPACITY},
9 geometry::Box2D,
10 neighbors::{NeighborNodeState, NeighborState, NeighborWorkspace},
11 ray::Ray2D,
12 traversal::{SearchWorkspace, upper_bound_level},
13};
14
15use super::{SimdIndex2D, load4};
16
17impl SimdIndex2D {
18 #[inline]
19 fn box_at_soa(&self, pos: usize) -> Box2D {
20 Box2D::new(
21 self.min_xs[pos],
22 self.min_ys[pos],
23 self.max_xs[pos],
24 self.max_ys[pos],
25 )
26 }
27
28 pub fn raycast_closest_with(
35 &self,
36 ray: Ray2D,
37 workspace: &mut NeighborWorkspace,
38 ) -> Option<(usize, f64)> {
39 #[cfg(target_arch = "x86_64")]
40 {
41 if std::is_x86_feature_detected!("avx512f") {
42 return unsafe {
46 if ray.has_zero_direction() {
47 self.raycast_closest_avx512_masked(ray, workspace)
48 } else {
49 self.raycast_closest_avx512(ray, workspace)
50 }
51 };
52 }
53 }
54 self.raycast_closest_wide(ray, workspace)
55 }
56
57 fn raycast_closest_wide(
58 &self,
59 ray: Ray2D,
60 workspace: &mut NeighborWorkspace,
61 ) -> Option<(usize, f64)> {
62 let queue = &mut workspace.node_queue;
63 queue.clear();
64 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
65 return None;
66 }
67 let root = self.min_xs.len() - 1;
68 let root_t = ray.enter_t(self.box_at_soa(root))?;
69 let mut best_t = ray.max_distance;
70 let mut best_index = None;
71 queue.push(NeighborNodeState::new(root, root_t));
72
73 let ox = f64x4::splat(ray.origin.x);
74 let oy = f64x4::splat(ray.origin.y);
75 let ix = f64x4::splat(ray.inv_dir_x);
76 let iy = f64x4::splat(ray.inv_dir_y);
77 let zero = f64x4::splat(0.0);
78 let maxd = f64x4::splat(ray.max_distance);
79 let pos_inf = f64x4::splat(f64::INFINITY);
80 let neg_inf = f64x4::splat(f64::NEG_INFINITY);
81 let (zx, zy) = (ray.dir_x == 0.0, ray.dir_y == 0.0);
84 let axis = |mn: f64x4, mx: f64x4, o: f64x4, inv: f64x4, degenerate: bool| {
85 if degenerate {
86 let inside = mn.simd_le(o) & o.simd_le(mx);
87 (
88 inside.blend(neg_inf, pos_inf),
89 inside.blend(pos_inf, neg_inf),
90 )
91 } else {
92 let t1 = (mn - o) * inv;
93 let t2 = (mx - o) * inv;
94 (t1.fast_min(t2), t1.fast_max(t2))
95 }
96 };
97
98 while let Some(node) = queue.pop() {
99 if node.dist >= best_t {
100 break;
101 }
102 let upper = upper_bound_level(&self.level_bounds, node.index);
103 let end = (node.index + self.node_size).min(self.level_bounds[upper]);
104 let is_leaf = node.index < self.num_items;
105
106 let mut pos = node.index;
107 while pos + 4 <= end {
108 let (nx, fx) = axis(
109 load4(&self.min_xs, pos),
110 load4(&self.max_xs, pos),
111 ox,
112 ix,
113 zx,
114 );
115 let (ny, fy) = axis(
116 load4(&self.min_ys, pos),
117 load4(&self.max_ys, pos),
118 oy,
119 iy,
120 zy,
121 );
122 let near = nx.fast_max(ny).fast_max(zero);
123 let far = fx.fast_min(fy).fast_min(maxd);
124 let bits = near.simd_le(far).to_bitmask();
125 if bits != 0 {
126 let tn = near.to_array();
127 #[allow(clippy::needless_range_loop)]
129 for k in 0..4 {
130 if bits & (1 << k) != 0 && tn[k] < best_t {
131 if is_leaf {
132 best_t = tn[k];
133 best_index = Some(self.indices[pos + k]);
134 } else {
135 queue.push(NeighborNodeState::new(self.indices[pos + k], tn[k]));
136 }
137 }
138 }
139 }
140 pos += 4;
141 }
142 while pos < end {
143 if let Some(t) = ray.enter_t(self.box_at_soa(pos))
144 && t < best_t
145 {
146 if is_leaf {
147 best_t = t;
148 best_index = Some(self.indices[pos]);
149 } else {
150 queue.push(NeighborNodeState::new(self.indices[pos], t));
151 }
152 }
153 pos += 1;
154 }
155 }
156
157 best_index.map(|index| (index, best_t))
158 }
159
160 #[cfg(target_arch = "x86_64")]
162 #[target_feature(enable = "avx512f")]
163 unsafe fn raycast_closest_avx512(
164 &self,
165 ray: Ray2D,
166 workspace: &mut NeighborWorkspace,
167 ) -> Option<(usize, f64)> {
168 use std::arch::x86_64::*;
169
170 let queue = &mut workspace.node_queue;
171 queue.clear();
172 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
173 return None;
174 }
175 let root = self.min_xs.len() - 1;
176 let root_t = ray.enter_t(self.box_at_soa(root))?;
177 let mut best_t = ray.max_distance;
178 let mut best_index = None;
179 queue.push(NeighborNodeState::new(root, root_t));
180
181 let ox = _mm512_set1_pd(ray.origin.x);
182 let oy = _mm512_set1_pd(ray.origin.y);
183 let ix = _mm512_set1_pd(ray.inv_dir_x);
184 let iy = _mm512_set1_pd(ray.inv_dir_y);
185 let zero = _mm512_setzero_pd();
186 let maxd = _mm512_set1_pd(ray.max_distance);
187
188 while let Some(node) = queue.pop() {
189 if node.dist >= best_t {
190 break;
191 }
192 let upper = upper_bound_level(&self.level_bounds, node.index);
193 let end = (node.index + self.node_size).min(self.level_bounds[upper]);
194 let is_leaf = node.index < self.num_items;
195
196 let mut pos = node.index;
197 while pos + 8 <= end {
198 let (mnx, mxx, mny, mxy) = unsafe {
200 (
201 _mm512_loadu_pd(self.min_xs.as_ptr().add(pos)),
202 _mm512_loadu_pd(self.max_xs.as_ptr().add(pos)),
203 _mm512_loadu_pd(self.min_ys.as_ptr().add(pos)),
204 _mm512_loadu_pd(self.max_ys.as_ptr().add(pos)),
205 )
206 };
207 let t1x = _mm512_mul_pd(_mm512_sub_pd(mnx, ox), ix);
208 let t2x = _mm512_mul_pd(_mm512_sub_pd(mxx, ox), ix);
209 let t1y = _mm512_mul_pd(_mm512_sub_pd(mny, oy), iy);
210 let t2y = _mm512_mul_pd(_mm512_sub_pd(mxy, oy), iy);
211 let near = _mm512_max_pd(
212 _mm512_max_pd(_mm512_min_pd(t1x, t2x), _mm512_min_pd(t1y, t2y)),
213 zero,
214 );
215 let far = _mm512_min_pd(
216 _mm512_min_pd(_mm512_max_pd(t1x, t2x), _mm512_max_pd(t1y, t2y)),
217 maxd,
218 );
219 let mut bits: u8 = _mm512_cmp_pd_mask::<_CMP_LE_OQ>(near, far);
220 if bits != 0 {
221 let mut tn = [0.0f64; 8];
222 unsafe { _mm512_storeu_pd(tn.as_mut_ptr(), near) };
224 while bits != 0 {
225 let k = bits.trailing_zeros() as usize;
226 bits &= bits - 1;
227 if tn[k] < best_t {
228 if is_leaf {
229 best_t = tn[k];
230 best_index = Some(self.indices[pos + k]);
231 } else {
232 queue.push(NeighborNodeState::new(self.indices[pos + k], tn[k]));
233 }
234 }
235 }
236 }
237 pos += 8;
238 }
239 while pos < end {
240 if let Some(t) = ray.enter_t(self.box_at_soa(pos))
241 && t < best_t
242 {
243 if is_leaf {
244 best_t = t;
245 best_index = Some(self.indices[pos]);
246 } else {
247 queue.push(NeighborNodeState::new(self.indices[pos], t));
248 }
249 }
250 pos += 1;
251 }
252 }
253
254 best_index.map(|index| (index, best_t))
255 }
256
257 #[cfg(target_arch = "x86_64")]
261 #[target_feature(enable = "avx512f")]
262 unsafe fn raycast_closest_avx512_masked(
263 &self,
264 ray: Ray2D,
265 workspace: &mut NeighborWorkspace,
266 ) -> Option<(usize, f64)> {
267 use std::arch::x86_64::*;
268
269 let queue = &mut workspace.node_queue;
270 queue.clear();
271 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
272 return None;
273 }
274 let root = self.min_xs.len() - 1;
275 let root_t = ray.enter_t(self.box_at_soa(root))?;
276 let mut best_t = ray.max_distance;
277 let mut best_index = None;
278 queue.push(NeighborNodeState::new(root, root_t));
279
280 let ox = _mm512_set1_pd(ray.origin.x);
281 let oy = _mm512_set1_pd(ray.origin.y);
282 let ix = _mm512_set1_pd(ray.inv_dir_x);
283 let iy = _mm512_set1_pd(ray.inv_dir_y);
284 let zero = _mm512_setzero_pd();
285 let maxd = _mm512_set1_pd(ray.max_distance);
286 let pos_inf = _mm512_set1_pd(f64::INFINITY);
287 let neg_inf = _mm512_set1_pd(f64::NEG_INFINITY);
288 let (zx, zy) = (ray.dir_x == 0.0, ray.dir_y == 0.0);
289
290 let axis = |mn, mx, o, inv, degenerate: bool| {
291 if degenerate {
292 let inside = _mm512_cmp_pd_mask::<_CMP_LE_OQ>(mn, o)
293 & _mm512_cmp_pd_mask::<_CMP_LE_OQ>(o, mx);
294 (
295 _mm512_mask_blend_pd(inside, pos_inf, neg_inf),
296 _mm512_mask_blend_pd(inside, neg_inf, pos_inf),
297 )
298 } else {
299 let t1 = _mm512_mul_pd(_mm512_sub_pd(mn, o), inv);
300 let t2 = _mm512_mul_pd(_mm512_sub_pd(mx, o), inv);
301 (_mm512_min_pd(t1, t2), _mm512_max_pd(t1, t2))
302 }
303 };
304
305 while let Some(node) = queue.pop() {
306 if node.dist >= best_t {
307 break;
308 }
309 let upper = upper_bound_level(&self.level_bounds, node.index);
310 let end = (node.index + self.node_size).min(self.level_bounds[upper]);
311 let is_leaf = node.index < self.num_items;
312
313 let mut pos = node.index;
314 while pos + 8 <= end {
315 let (nx, fx, ny, fy) = unsafe {
317 let (mnx, mxx, mny, mxy) = (
318 _mm512_loadu_pd(self.min_xs.as_ptr().add(pos)),
319 _mm512_loadu_pd(self.max_xs.as_ptr().add(pos)),
320 _mm512_loadu_pd(self.min_ys.as_ptr().add(pos)),
321 _mm512_loadu_pd(self.max_ys.as_ptr().add(pos)),
322 );
323 let (nx, fx) = axis(mnx, mxx, ox, ix, zx);
324 let (ny, fy) = axis(mny, mxy, oy, iy, zy);
325 (nx, fx, ny, fy)
326 };
327 let near = _mm512_max_pd(_mm512_max_pd(nx, ny), zero);
328 let far = _mm512_min_pd(_mm512_min_pd(fx, fy), maxd);
329 let mut bits: u8 = _mm512_cmp_pd_mask::<_CMP_LE_OQ>(near, far);
330 if bits != 0 {
331 let mut tn = [0.0f64; 8];
332 unsafe { _mm512_storeu_pd(tn.as_mut_ptr(), near) };
334 while bits != 0 {
335 let k = bits.trailing_zeros() as usize;
336 bits &= bits - 1;
337 if tn[k] < best_t {
338 if is_leaf {
339 best_t = tn[k];
340 best_index = Some(self.indices[pos + k]);
341 } else {
342 queue.push(NeighborNodeState::new(self.indices[pos + k], tn[k]));
343 }
344 }
345 }
346 }
347 pos += 8;
348 }
349 while pos < end {
350 if let Some(t) = ray.enter_t(self.box_at_soa(pos))
351 && t < best_t
352 {
353 if is_leaf {
354 best_t = t;
355 best_index = Some(self.indices[pos]);
356 } else {
357 queue.push(NeighborNodeState::new(self.indices[pos], t));
358 }
359 }
360 pos += 1;
361 }
362 }
363 best_index.map(|index| (index, best_t))
364 }
365
366 pub fn raycast_closest(&self, ray: Ray2D) -> Option<(usize, f64)> {
374 let mut workspace = NeighborWorkspace::new();
375 self.raycast_closest_with(ray, &mut workspace)
376 }
377
378 pub fn raycast(&self, ray: Ray2D) -> Vec<usize> {
380 let mut results = Vec::new();
381 self.raycast_into(ray, &mut results);
382 results
383 }
384
385 pub fn raycast_into(&self, ray: Ray2D, results: &mut Vec<usize>) {
387 let mut stack = Vec::with_capacity(DEFAULT_SEARCH_STACK_CAPACITY);
388 self.raycast_into_stack(ray, results, &mut stack);
389 }
390
391 pub fn raycast_with<'a>(&self, ray: Ray2D, workspace: &'a mut SearchWorkspace) -> &'a [usize] {
393 self.raycast_into_stack(ray, &mut workspace.results, &mut workspace.stack);
394 &workspace.results
395 }
396
397 #[doc(hidden)]
402 pub fn raycast_into_stack(&self, ray: Ray2D, results: &mut Vec<usize>, stack: &mut Vec<usize>) {
403 #[cfg(target_arch = "x86_64")]
404 {
405 if !ray.has_zero_direction() {
406 if std::is_x86_feature_detected!("avx512f") {
407 unsafe { self.raycast_collect_avx512(ray, results, stack) };
409 return;
410 }
411 if std::is_x86_feature_detected!("avx2") {
412 unsafe { self.raycast_collect_avx2(ray, results, stack) };
414 return;
415 }
416 }
417 }
418 self.raycast_collect_wide(ray, results, stack);
419 }
420
421 #[doc(hidden)]
423 pub fn raycast_wide_into(&self, ray: Ray2D, results: &mut Vec<usize>) {
424 let mut stack = Vec::new();
425 self.raycast_collect_wide(ray, results, &mut stack);
426 }
427
428 #[doc(hidden)]
430 pub fn raycast_avx2_into(&self, ray: Ray2D, results: &mut Vec<usize>) {
431 let mut stack = Vec::new();
432 #[cfg(target_arch = "x86_64")]
433 {
434 if !ray.has_zero_direction() && std::is_x86_feature_detected!("avx2") {
435 unsafe { self.raycast_collect_avx2(ray, results, &mut stack) };
437 return;
438 }
439 }
440 self.raycast_collect_wide(ray, results, &mut stack);
441 }
442
443 fn raycast_collect_wide(&self, ray: Ray2D, results: &mut Vec<usize>, stack: &mut Vec<usize>) {
444 results.clear();
445 stack.clear();
446 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
447 return;
448 }
449
450 let ox = f64x4::splat(ray.origin.x);
451 let oy = f64x4::splat(ray.origin.y);
452 let ix = f64x4::splat(ray.inv_dir_x);
453 let iy = f64x4::splat(ray.inv_dir_y);
454 let zero = f64x4::splat(0.0);
455 let maxd = f64x4::splat(ray.max_distance);
456 let pos_inf = f64x4::splat(f64::INFINITY);
457 let neg_inf = f64x4::splat(f64::NEG_INFINITY);
458 let (zx, zy) = (ray.dir_x == 0.0, ray.dir_y == 0.0);
463 let axis = |mn: f64x4, mx: f64x4, o: f64x4, inv: f64x4, degenerate: bool| {
464 if degenerate {
465 let inside = mn.simd_le(o) & o.simd_le(mx);
466 (
467 inside.blend(neg_inf, pos_inf),
468 inside.blend(pos_inf, neg_inf),
469 )
470 } else {
471 let t1 = (mn - o) * inv;
472 let t2 = (mx - o) * inv;
473 (t1.fast_min(t2), t1.fast_max(t2))
474 }
475 };
476
477 let mut node_index = self.min_xs.len() - 1;
478 let mut level = self.level_bounds.len() - 1;
479 loop {
480 let end = (node_index + self.node_size).min(self.level_bounds[level]);
481 let is_leaf = node_index < self.num_items;
482 let child_level = level.wrapping_sub(1);
483
484 let mut pos = node_index;
485 while pos + 4 <= end {
486 let (nx, fx) = axis(
487 load4(&self.min_xs, pos),
488 load4(&self.max_xs, pos),
489 ox,
490 ix,
491 zx,
492 );
493 let (ny, fy) = axis(
494 load4(&self.min_ys, pos),
495 load4(&self.max_ys, pos),
496 oy,
497 iy,
498 zy,
499 );
500 let near = nx.fast_max(ny).fast_max(zero);
501 let far = fx.fast_min(fy).fast_min(maxd);
502 let mut bits = near.simd_le(far).to_bitmask();
503 while bits != 0 {
504 let k = bits.trailing_zeros() as usize;
505 bits &= bits - 1;
506 let index = self.indices[pos + k];
507 if is_leaf {
508 results.push(index);
509 } else {
510 stack.push(index);
511 stack.push(child_level);
512 }
513 }
514 pos += 4;
515 }
516 while pos < end {
517 if ray.intersects_box(self.box_at_soa(pos)) {
518 let index = self.indices[pos];
519 if is_leaf {
520 results.push(index);
521 } else {
522 stack.push(index);
523 stack.push(child_level);
524 }
525 }
526 pos += 1;
527 }
528
529 if stack.len() > 1 {
530 level = stack.pop().unwrap();
531 node_index = stack.pop().unwrap();
532 } else {
533 return;
534 }
535 }
536 }
537
538 #[cfg(target_arch = "x86_64")]
542 #[target_feature(enable = "avx512f")]
543 unsafe fn raycast_collect_avx512(
544 &self,
545 ray: Ray2D,
546 results: &mut Vec<usize>,
547 stack: &mut Vec<usize>,
548 ) {
549 use std::arch::x86_64::*;
550
551 results.clear();
552 stack.clear();
553 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
554 return;
555 }
556
557 let ox = _mm512_set1_pd(ray.origin.x);
558 let oy = _mm512_set1_pd(ray.origin.y);
559 let ix = _mm512_set1_pd(ray.inv_dir_x);
560 let iy = _mm512_set1_pd(ray.inv_dir_y);
561 let zero = _mm512_setzero_pd();
562 let maxd = _mm512_set1_pd(ray.max_distance);
563
564 let mut node_index = self.min_xs.len() - 1;
565 let mut level = self.level_bounds.len() - 1;
566 loop {
567 let end = (node_index + self.node_size).min(self.level_bounds[level]);
568 let is_leaf = node_index < self.num_items;
569 let child_level = level.wrapping_sub(1);
570 if is_leaf {
571 results.reserve(end - node_index);
572 }
573
574 let mut pos = node_index;
575 while pos + 8 <= end {
576 let (mnx, mxx, mny, mxy) = unsafe {
578 (
579 _mm512_loadu_pd(self.min_xs.as_ptr().add(pos)),
580 _mm512_loadu_pd(self.max_xs.as_ptr().add(pos)),
581 _mm512_loadu_pd(self.min_ys.as_ptr().add(pos)),
582 _mm512_loadu_pd(self.max_ys.as_ptr().add(pos)),
583 )
584 };
585 let t1x = _mm512_mul_pd(_mm512_sub_pd(mnx, ox), ix);
586 let t2x = _mm512_mul_pd(_mm512_sub_pd(mxx, ox), ix);
587 let t1y = _mm512_mul_pd(_mm512_sub_pd(mny, oy), iy);
588 let t2y = _mm512_mul_pd(_mm512_sub_pd(mxy, oy), iy);
589 let near = _mm512_max_pd(
590 _mm512_max_pd(_mm512_min_pd(t1x, t2x), _mm512_min_pd(t1y, t2y)),
591 zero,
592 );
593 let far = _mm512_min_pd(
594 _mm512_min_pd(_mm512_max_pd(t1x, t2x), _mm512_max_pd(t1y, t2y)),
595 maxd,
596 );
597 let mut bits: u8 = _mm512_cmp_pd_mask::<_CMP_LE_OQ>(near, far);
598 if is_leaf {
599 unsafe {
603 let dst = results.as_mut_ptr().add(results.len()) as *mut i64;
604 let vidx = _mm512_loadu_epi64(self.indices.as_ptr().add(pos) as *const i64);
605 _mm512_mask_compressstoreu_epi64(dst, bits, vidx);
606 results.set_len(results.len() + bits.count_ones() as usize);
607 }
608 } else {
609 while bits != 0 {
610 let k = bits.trailing_zeros() as usize;
611 bits &= bits - 1;
612 stack.push(self.indices[pos + k]);
613 stack.push(child_level);
614 }
615 }
616 pos += 8;
617 }
618 while pos < end {
619 if ray.intersects_box(self.box_at_soa(pos)) {
620 let index = self.indices[pos];
621 if is_leaf {
622 results.push(index);
623 } else {
624 stack.push(index);
625 stack.push(child_level);
626 }
627 }
628 pos += 1;
629 }
630
631 if stack.len() > 1 {
632 level = stack.pop().unwrap();
633 node_index = stack.pop().unwrap();
634 } else {
635 return;
636 }
637 }
638 }
639
640 #[cfg(target_arch = "x86_64")]
642 #[target_feature(enable = "avx2")]
643 unsafe fn raycast_collect_avx2(
644 &self,
645 ray: Ray2D,
646 results: &mut Vec<usize>,
647 stack: &mut Vec<usize>,
648 ) {
649 use std::arch::x86_64::*;
650
651 results.clear();
652 stack.clear();
653 if self.num_items == 0 || ray.max_distance < 0.0 || ray.max_distance.is_nan() {
654 return;
655 }
656
657 let ox = _mm256_set1_pd(ray.origin.x);
658 let oy = _mm256_set1_pd(ray.origin.y);
659 let ix = _mm256_set1_pd(ray.inv_dir_x);
660 let iy = _mm256_set1_pd(ray.inv_dir_y);
661 let zero = _mm256_setzero_pd();
662 let maxd = _mm256_set1_pd(ray.max_distance);
663
664 let mut node_index = self.min_xs.len() - 1;
665 let mut level = self.level_bounds.len() - 1;
666 loop {
667 let end = (node_index + self.node_size).min(self.level_bounds[level]);
668 let is_leaf = node_index < self.num_items;
669 let child_level = level.wrapping_sub(1);
670 if is_leaf {
671 results.reserve(end - node_index + 4);
672 }
673
674 let mut pos = node_index;
675 while pos + 4 <= end {
676 let (mnx, mxx, mny, mxy) = unsafe {
678 (
679 _mm256_loadu_pd(self.min_xs.as_ptr().add(pos)),
680 _mm256_loadu_pd(self.max_xs.as_ptr().add(pos)),
681 _mm256_loadu_pd(self.min_ys.as_ptr().add(pos)),
682 _mm256_loadu_pd(self.max_ys.as_ptr().add(pos)),
683 )
684 };
685 let t1x = _mm256_mul_pd(_mm256_sub_pd(mnx, ox), ix);
686 let t2x = _mm256_mul_pd(_mm256_sub_pd(mxx, ox), ix);
687 let t1y = _mm256_mul_pd(_mm256_sub_pd(mny, oy), iy);
688 let t2y = _mm256_mul_pd(_mm256_sub_pd(mxy, oy), iy);
689 let near = _mm256_max_pd(
690 _mm256_max_pd(_mm256_min_pd(t1x, t2x), _mm256_min_pd(t1y, t2y)),
691 zero,
692 );
693 let far = _mm256_min_pd(
694 _mm256_min_pd(_mm256_max_pd(t1x, t2x), _mm256_max_pd(t1y, t2y)),
695 maxd,
696 );
697 let mut bits = _mm256_movemask_pd(_mm256_cmp_pd::<_CMP_LE_OQ>(near, far)) as usize;
698 if is_leaf {
699 if bits != 0 {
700 unsafe {
703 let added = leftpack4(
704 self.indices.as_ptr().add(pos),
705 bits as u32,
706 results.as_mut_ptr().add(results.len()),
707 );
708 results.set_len(results.len() + added);
709 }
710 }
711 } else {
712 while bits != 0 {
713 let k = bits.trailing_zeros() as usize;
714 bits &= bits - 1;
715 stack.push(self.indices[pos + k]);
716 stack.push(child_level);
717 }
718 }
719 pos += 4;
720 }
721 while pos < end {
722 if ray.intersects_box(self.box_at_soa(pos)) {
723 let index = self.indices[pos];
724 if is_leaf {
725 results.push(index);
726 } else {
727 stack.push(index);
728 stack.push(child_level);
729 }
730 }
731 pos += 1;
732 }
733
734 if stack.len() > 1 {
735 level = stack.pop().unwrap();
736 node_index = stack.pop().unwrap();
737 } else {
738 return;
739 }
740 }
741 }
742}
743
744impl SimdIndex2D {
745 pub fn visit_raycast<B, F>(&self, ray: Ray2D, mut visitor: F) -> ControlFlow<B>
751 where
752 F: FnMut(usize, f64) -> ControlFlow<B>,
753 {
754 let mut queue = BinaryHeap::with_capacity(DEFAULT_NEIGHBOR_QUEUE_CAPACITY);
755 if self.num_items == 0 {
756 return ControlFlow::Continue(());
757 }
758
759 let mut node_index = self.min_xs.len() - 1;
760 loop {
761 let upper = upper_bound_level(&self.level_bounds, node_index);
762 let end = (node_index + self.node_size).min(self.level_bounds[upper]);
763 let is_leaf = node_index < self.num_items;
764
765 for pos in node_index..end {
766 if let Some(t) = ray.enter_t(self.box_at_soa(pos)) {
767 queue.push(NeighborState::new(self.indices[pos], is_leaf, t));
768 }
769 }
770
771 let mut continue_search = false;
772 while let Some(state) = queue.pop() {
773 if state.is_leaf {
774 visitor(state.index, state.dist)?;
775 } else {
776 node_index = state.index;
777 continue_search = true;
778 break;
779 }
780 }
781 if !continue_search {
782 return ControlFlow::Continue(());
783 }
784 }
785 }
786}