1use crate::{
2 astar::{Astar, PathResult},
3 terrain::Block,
4 vol::{BaseVol, ReadVol},
5};
6use common_base::span;
7use fxhash::FxBuildHasher;
8#[cfg(feature = "rrt_pathfinding")]
9use hashbrown::HashMap;
10#[cfg(feature = "rrt_pathfinding")]
11use kiddo::{SquaredEuclidean, float::kdtree::KdTree, nearest_neighbour::NearestNeighbour}; use rand::{Rng, thread_rng};
13#[cfg(feature = "rrt_pathfinding")]
14use rand::{
15 distributions::{Distribution, Uniform},
16 prelude::IteratorRandom,
17};
18#[cfg(feature = "rrt_pathfinding")]
19use std::f32::consts::PI;
20use std::iter::FromIterator;
21use vek::*;
22
23#[derive(Clone, Debug)]
26pub struct Path<T> {
27 pub nodes: Vec<T>,
28}
29
30impl<T> Default for Path<T> {
31 fn default() -> Self {
32 Self {
33 nodes: Vec::default(),
34 }
35 }
36}
37
38impl<T> FromIterator<T> for Path<T> {
39 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
40 Self {
41 nodes: iter.into_iter().collect(),
42 }
43 }
44}
45
46impl<T> IntoIterator for Path<T> {
47 type IntoIter = std::vec::IntoIter<T>;
48 type Item = T;
49
50 fn into_iter(self) -> Self::IntoIter { self.nodes.into_iter() }
51}
52
53impl<T> Path<T> {
54 pub fn is_empty(&self) -> bool { self.nodes.is_empty() }
55
56 pub fn len(&self) -> usize { self.nodes.len() }
57
58 pub fn iter(&self) -> impl Iterator<Item = &T> { self.nodes.iter() }
59
60 pub fn start(&self) -> Option<&T> { self.nodes.first() }
61
62 pub fn end(&self) -> Option<&T> { self.nodes.last() }
63
64 pub fn nodes(&self) -> &[T] { &self.nodes }
65}
66
67#[derive(Default, Clone, Debug)]
70pub struct Route {
71 path: Path<Vec3<i32>>,
72 next_idx: usize,
73}
74
75impl From<Path<Vec3<i32>>> for Route {
76 fn from(path: Path<Vec3<i32>>) -> Self { Self { path, next_idx: 0 } }
77}
78
79pub struct TraversalConfig {
80 pub node_tolerance: f32,
82 pub slow_factor: f32,
85 pub on_ground: bool,
87 pub in_liquid: bool,
89 pub min_tgt_dist: f32,
91 pub can_climb: bool,
93 pub can_fly: bool,
95 pub vectored_propulsion: bool,
97 pub is_target_loaded: bool,
99}
100
101const DIAGONALS: [Vec2<i32>; 8] = [
102 Vec2::new(1, 0),
103 Vec2::new(1, 1),
104 Vec2::new(0, 1),
105 Vec2::new(-1, 1),
106 Vec2::new(-1, 0),
107 Vec2::new(-1, -1),
108 Vec2::new(0, -1),
109 Vec2::new(1, -1),
110];
111
112impl Route {
113 pub fn path(&self) -> &Path<Vec3<i32>> { &self.path }
114
115 pub fn next(&self, i: usize) -> Option<Vec3<i32>> {
116 self.path.nodes.get(self.next_idx + i).copied()
117 }
118
119 pub fn is_finished(&self) -> bool { self.next(0).is_none() }
120
121 pub fn traverse<V>(
122 &mut self,
123 vol: &V,
124 pos: Vec3<f32>,
125 vel: Vec3<f32>,
126 traversal_cfg: &TraversalConfig,
127 ) -> Option<(Vec3<f32>, f32)>
128 where
129 V: BaseVol<Vox = Block> + ReadVol,
130 {
131 let (next0, next1, next_tgt, be_precise) = loop {
132 let next0 = self.next(0)?;
134 let next1 = self.next(1).unwrap_or(next0);
135
136 if !walkable(vol, next0) || !walkable(vol, next1) {
138 return None;
139 }
140
141 let open_space_nearby = DIAGONALS.iter().any(|pos| {
143 (-1..2).all(|z| {
144 vol.get(next0 + Vec3::new(pos.x, pos.y, z))
145 .map(|b| !b.is_solid())
146 .unwrap_or(false)
147 })
148 });
149
150 let wall_nearby = DIAGONALS.iter().any(|pos| {
152 (0..2).all(|z| {
153 vol.get(next0 + Vec3::new(pos.x, pos.y, z))
154 .map(|b| b.is_solid())
155 .unwrap_or(true)
156 })
157 });
158
159 let be_precise = open_space_nearby | wall_nearby;
161
162 let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
164 let closest_tgt = next_tgt.map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil()));
165 let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy());
167 if dist_sqrd
168 < traversal_cfg.node_tolerance.powi(2)
169 * if be_precise {
170 0.25
171 } else if traversal_cfg.in_liquid {
172 2.5
173 } else {
174 1.0
175 }
176 && (((pos.z - closest_tgt.z > 1.2 || (pos.z - closest_tgt.z > -0.2 && traversal_cfg.on_ground))
177 && (pos.z - closest_tgt.z < 1.2 || (pos.z - closest_tgt.z < 2.9 && vel.z < -0.05))
178 && vel.z <= 0.0
179 && (vol
181 .ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5)
182 .until(Block::is_solid)
183 .cast()
184 .0
185 > pos.distance(closest_tgt) * 0.9 || dist_sqrd < 0.5)
186 && self.next_idx < self.path.len())
187 || (traversal_cfg.in_liquid
188 && pos.z < closest_tgt.z + 0.8
189 && pos.z > closest_tgt.z))
190 {
191 self.next_idx += 1;
193 } else {
194 break (next0, next1, next_tgt, be_precise);
196 }
197 };
198
199 fn gradient(line: LineSegment2<f32>) -> f32 {
200 let r = (line.start.y - line.end.y) / (line.start.x - line.end.x);
201 if r.is_nan() { 100000.0 } else { r }
202 }
203
204 fn intersect(a: LineSegment2<f32>, b: LineSegment2<f32>) -> Option<Vec2<f32>> {
205 let ma = gradient(a);
206 let mb = gradient(b);
207
208 let ca = a.start.y - ma * a.start.x;
209 let cb = b.start.y - mb * b.start.x;
210
211 if (ma - mb).abs() < 0.0001 || (ca - cb).abs() < 0.0001 {
212 None
213 } else {
214 let x = (cb - ca) / (ma - mb);
215 let y = ma * x + ca;
216
217 Some(Vec2::new(x, y))
218 }
219 }
220
221 let corners = [
233 Vec2::new(0, 0),
234 Vec2::new(1, 0),
235 Vec2::new(1, 1),
236 Vec2::new(0, 1),
237 Vec2::new(0, 0), ];
239
240 let vel_line = LineSegment2 {
241 start: pos.xy(),
242 end: pos.xy() + vel.xy() * 100.0,
243 };
244
245 let align = |block_pos: Vec3<i32>, precision: f32| {
246 let lerp_block =
247 |x, precision| Lerp::lerp(x, block_pos.xy().map(|e| e as f32), precision);
248
249 (0..4)
250 .filter_map(|i| {
251 let edge_line = LineSegment2 {
252 start: lerp_block(
253 (block_pos.xy() + corners[i]).map(|e| e as f32),
254 precision,
255 ),
256 end: lerp_block(
257 (block_pos.xy() + corners[i + 1]).map(|e| e as f32),
258 precision,
259 ),
260 };
261 intersect(vel_line, edge_line).filter(|intersect| {
262 intersect
263 .clamped(
264 block_pos.xy().map(|e| e as f32),
265 block_pos.xy().map(|e| e as f32 + 1.0),
266 )
267 .distance_squared(*intersect)
268 < 0.001
269 })
270 })
271 .min_by_key(|intersect: &Vec2<f32>| {
272 (intersect.distance_squared(vel_line.end) * 1000.0) as i32
273 })
274 .unwrap_or_else(|| {
275 (0..2)
276 .flat_map(|i| (0..2).map(move |j| Vec2::new(i, j)))
277 .map(|rpos| block_pos + rpos)
278 .map(|block_pos| {
279 let block_posf = block_pos.xy().map(|e| e as f32);
280 let proj = vel_line.projected_point(block_posf);
281 let clamped = lerp_block(
282 proj.clamped(
283 block_pos.xy().map(|e| e as f32),
284 block_pos.xy().map(|e| e as f32),
285 ),
286 precision,
287 );
288
289 (proj.distance_squared(clamped), clamped)
290 })
291 .min_by_key(|(d2, _)| (d2 * 1000.0) as i32)
292 .unwrap()
293 .1
294 })
295 };
296
297 let bez = CubicBezier2 {
298 start: pos.xy(),
299 ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0,
300 ctrl1: align(next0, 1.0),
301 end: align(next1, 1.0),
302 };
303
304 let next_dir = bez
309 .evaluate_derivative(0.85)
310 .try_normalized()
311 .unwrap_or_default();
312 let straight_factor = next_dir
313 .dot(vel.xy().try_normalized().unwrap_or(next_dir))
314 .max(0.0)
315 .powi(2);
316
317 let bez = CubicBezier2 {
318 start: pos.xy(),
319 ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0,
320 ctrl1: align(
321 next0,
322 (1.0 - if (next0.z as f32 - pos.z).abs() < 0.25 && !be_precise {
323 straight_factor
324 } else {
325 0.0
326 })
327 .max(0.1),
328 ),
329 end: align(next1, 1.0),
330 };
331
332 let tgt2d = bez.evaluate(if (next0.z as f32 - pos.z).abs() < 0.25 {
333 0.25
334 } else {
335 0.5
336 });
337 let tgt = if be_precise {
338 next_tgt
339 } else {
340 Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z
341 };
342
343 Some((
344 tgt - pos,
345 straight_factor * traversal_cfg.slow_factor + (1.0 - traversal_cfg.slow_factor),
349 ))
350 .filter(|(bearing, _)| bearing.z < 2.1)
351 }
352}
353
354#[derive(Default, Clone, Debug)]
357pub struct Chaser {
358 last_search_tgt: Option<Vec3<f32>>,
359 route: Option<(Route, bool)>,
361 astar: Option<Astar<Vec3<i32>, FxBuildHasher>>,
366}
367
368impl Chaser {
369 pub fn chase<V>(
373 &mut self,
374 vol: &V,
375 pos: Vec3<f32>,
376 vel: Vec3<f32>,
377 tgt: Vec3<f32>,
378 traversal_cfg: TraversalConfig,
379 ) -> Option<(Vec3<f32>, f32)>
380 where
381 V: BaseVol<Vox = Block> + ReadVol,
382 {
383 span!(_guard, "chase", "Chaser::chase");
384 let pos_to_tgt = pos.distance(tgt);
385
386 let end = self
388 .route
389 .as_ref()
390 .and_then(|(r, _)| r.path.end().copied())
391 .map(|e| e.map(|e| e as f32 + 0.5))
392 .unwrap_or(tgt);
393 if ((pos - end) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared()
394 < traversal_cfg.min_tgt_dist.powi(2)
395 {
396 self.route = None;
397 return None;
398 }
399
400 let bearing = if let Some((end, complete)) = self
401 .route
402 .as_ref()
403 .and_then(|(r, complete)| Some((r.path().end().copied()?, *complete)))
404 {
405 let end_to_tgt = end.map(|e| e as f32).distance(tgt);
406 if end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete && traversal_cfg.is_target_loaded {
413 None
414 } else if thread_rng().gen::<f32>() < 0.01 {
415 self.route = None;
416 None
417 } else {
418 self.route
419 .as_mut()
420 .and_then(|(r, _)| r.traverse(vol, pos, vel, &traversal_cfg))
421 }
422 } else {
423 None
425 };
426
427 if let Some((bearing, speed)) = bearing {
429 Some((bearing, speed))
430 } else {
431 let tgt_dir = (tgt - pos).xy().try_normalized().unwrap_or_default();
435
436 if self
440 .last_search_tgt
441 .map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0)
442 .unwrap_or(true)
443 || self.astar.is_some()
444 || self.route.is_none()
445 || !traversal_cfg.is_target_loaded
446 {
447 self.last_search_tgt = Some(tgt);
448
449 let (path, complete) = {
453 find_path(&mut self.astar, vol, pos, tgt, &traversal_cfg)
454 };
455
456 self.route = path.map(|path| {
457 let start_index = path
458 .iter()
459 .enumerate()
460 .min_by_key(|(_, node)| {
461 node.map(|e| e as f32).distance_squared(pos + tgt_dir) as i32
462 })
463 .map(|(idx, _)| idx);
464
465 (
466 Route {
467 path,
468 next_idx: start_index.unwrap_or(0),
469 },
470 complete,
471 )
472 });
473 }
474 if let Some(bearing) = self
476 .route
477 .as_mut()
478 .and_then(|(r, _)| r.traverse(vol, pos, vel, &traversal_cfg))
479 {
480 Some(bearing)
481 } else {
482 let walking_towards_edge = (-8..2).all(|z| {
487 vol.get(
488 (pos + Vec3::<f32>::from(tgt_dir) * 2.5).map(|e| e as i32)
489 + Vec3::unit_z() * z,
490 )
491 .map(|b| b.is_air())
492 .unwrap_or(false)
493 });
494
495 if traversal_cfg.can_fly {
500 Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.5), 1.0))
501 } else if !walking_towards_edge {
502 Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 1.0))
503 } else {
504 None
509 }
510 }
511 }
512 }
513}
514
515fn walkable<V>(vol: &V, pos: Vec3<i32>) -> bool
516where
517 V: BaseVol<Vox = Block> + ReadVol,
518{
519 let below = vol
520 .get(pos - Vec3::unit_z())
521 .ok()
522 .copied()
523 .unwrap_or_else(Block::empty);
524 let a = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
525 let b = vol
526 .get(pos + Vec3::unit_z())
527 .ok()
528 .copied()
529 .unwrap_or_else(Block::empty);
530
531 let on_ground = below.is_filled();
532 let in_liquid = a.is_liquid();
533 (on_ground || in_liquid) && !a.is_solid() && !b.is_solid()
534}
535
536fn find_path<V>(
539 astar: &mut Option<Astar<Vec3<i32>, FxBuildHasher>>,
540 vol: &V,
541 startf: Vec3<f32>,
542 endf: Vec3<f32>,
543 traversal_cfg: &TraversalConfig,
544) -> (Option<Path<Vec3<i32>>>, bool)
545where
546 V: BaseVol<Vox = Block> + ReadVol,
547{
548 let is_walkable = |pos: &Vec3<i32>| walkable(vol, *pos);
549 let get_walkable_z = |pos| {
550 let mut z_incr = 0;
551 for _ in 0..32 {
552 let test_pos = pos + Vec3::unit_z() * z_incr;
553 if is_walkable(&test_pos) {
554 return Some(test_pos);
555 }
556 z_incr = -z_incr + i32::from(z_incr <= 0);
557 }
558 None
559 };
560
561 let (start, end) = match (
562 get_walkable_z(startf.map(|e| e.floor() as i32)),
563 get_walkable_z(endf.map(|e| e.floor() as i32)),
564 ) {
565 (Some(start), Some(end)) => (start, end),
566
567 (Some(start), None) if !traversal_cfg.is_target_loaded => {
569 (start, endf.map(|e| e.floor() as i32))
570 },
571
572 _ => return (None, false),
573 };
574
575 let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt();
576 let transition = |a: Vec3<i32>, b: Vec3<i32>| {
577 let crow_line = LineSegment2 {
578 start: startf.xy(),
579 end: endf.xy(),
580 };
581
582 1.0 + crow_line.distance_to_point(b.xy().map(|e| e as f32)) * 0.025
585 + (b.z - a.z - 1).max(0) as f32 * 10.0
586 };
587 let neighbors = |pos: &Vec3<i32>| {
588 let pos = *pos;
589 const DIRS: [Vec3<i32>; 17] = [
590 Vec3::new(0, 1, 0), Vec3::new(0, 1, 1), Vec3::new(0, 1, -1), Vec3::new(0, 1, -2), Vec3::new(1, 0, 0), Vec3::new(1, 0, 1), Vec3::new(1, 0, -1), Vec3::new(1, 0, -2), Vec3::new(0, -1, 0), Vec3::new(0, -1, 1), Vec3::new(0, -1, -1), Vec3::new(0, -1, -2), Vec3::new(-1, 0, 0), Vec3::new(-1, 0, 1), Vec3::new(-1, 0, -1), Vec3::new(-1, 0, -2), Vec3::new(0, 0, -1), ];
608
609 const JUMPS: [Vec3<i32>; 4] = [
610 Vec3::new(0, 1, 2), Vec3::new(1, 0, 2), Vec3::new(0, -1, 2), Vec3::new(-1, 0, 2), ];
615
616 DIRS.iter()
635 .chain(
636 Some(JUMPS.iter())
637 .filter(|_| {
638 vol.get(pos - Vec3::unit_z())
639 .map(|b| !b.is_liquid())
640 .unwrap_or(traversal_cfg.is_target_loaded)
641 || traversal_cfg.can_climb
642 || traversal_cfg.can_fly
643 })
644 .into_iter()
645 .flatten(),
646 )
647 .map(move |dir| (pos, dir))
648 .filter(move |(pos, dir)| {
649 (traversal_cfg.can_fly || is_walkable(pos) && is_walkable(&(*pos + **dir)))
650 && ((dir.z < 1
651 || vol
652 .get(pos + Vec3::unit_z() * 2)
653 .map(|b| !b.is_solid())
654 .unwrap_or(traversal_cfg.is_target_loaded))
655 && (dir.z < 2
656 || vol
657 .get(pos + Vec3::unit_z() * 3)
658 .map(|b| !b.is_solid())
659 .unwrap_or(traversal_cfg.is_target_loaded))
660 && (dir.z >= 0
661 || vol
662 .get(pos + *dir + Vec3::unit_z() * 2)
663 .map(|b| !b.is_solid())
664 .unwrap_or(traversal_cfg.is_target_loaded)))
665 })
666 .map(|(pos, dir)| {
667 let destination = pos + dir;
668 (destination, transition(pos, destination))
669 })
670 };
679
680 let satisfied = |pos: &Vec3<i32>| pos == &end;
681
682 let mut new_astar = match astar.take() {
683 None => Astar::new(
684 if traversal_cfg.is_target_loaded {
685 25_000
687 } else {
688 500
691 },
692 start,
693 FxBuildHasher::default(),
694 ),
695 Some(astar) => astar,
696 };
697
698 let path_result = new_astar.poll(100, heuristic, neighbors, satisfied);
699
700 match path_result {
701 PathResult::Path(path, _cost) => (Some(path), true),
702 PathResult::None(path) => (Some(path), false),
703 PathResult::Exhausted(path) => (Some(path), false),
704
705 PathResult::Pending => {
706 *astar = Some(new_astar);
708
709 (None, false)
710 },
711 }
712}
713
714#[cfg(feature = "rrt_pathfinding")]
716fn find_air_path<V>(
717 vol: &V,
718 startf: Vec3<f32>,
719 endf: Vec3<f32>,
720 traversal_cfg: &TraversalConfig,
721) -> (Option<Path<Vec3<i32>>>, bool)
722where
723 V: BaseVol<Vox = Block> + ReadVol,
724{
725 let radius = traversal_cfg.node_tolerance;
726 let total_dist_sqrd = startf.distance_squared(endf);
727 if vol
729 .ray(startf + Vec3::unit_z(), endf + Vec3::unit_z())
730 .until(Block::is_opaque)
731 .cast()
732 .0
733 .powi(2)
734 >= total_dist_sqrd
735 {
736 let mut path = Vec::new();
737 path.push(endf.map(|e| e.floor() as i32));
738 let connect = true;
739 (Some(path.into_iter().collect()), connect)
740 } else {
742 let is_traversable = |start: &Vec3<f32>, end: &Vec3<f32>| {
743 vol.ray(*start, *end)
744 .until(Block::is_solid)
745 .cast()
746 .0
747 .powi(2)
748 > (*start).distance_squared(*end)
749 };
752 informed_rrt_connect(vol, startf, endf, is_traversable, radius)
753 }
754}
755
756#[cfg(feature = "rrt_pathfinding")]
767fn informed_rrt_connect<V>(
768 vol: &V,
769 startf: Vec3<f32>,
770 endf: Vec3<f32>,
771 is_valid_edge: impl Fn(&Vec3<f32>, &Vec3<f32>) -> bool,
772 radius: f32,
773) -> (Option<Path<Vec3<i32>>>, bool)
774where
775 V: BaseVol<Vox = Block> + ReadVol,
776{
777 const MAX_POINTS: usize = 7000;
778 let mut path = Vec::new();
779
780 let mut node_index1: usize = 0;
782 let mut node_index2: usize = 0;
783 let mut nodes1 = Vec::new();
784 let mut nodes2 = Vec::new();
785
786 let mut parents1 = HashMap::new();
789 let mut parents2 = HashMap::new();
790
791 let mut path1 = Vec::new();
794 let mut path2 = Vec::new();
795
796 let mut kdtree1: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
798 let mut kdtree2: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
799
800 kdtree1.add(&[startf.x, startf.y, startf.z], node_index1);
802 nodes1.push(startf);
803 node_index1 += 1;
804
805 kdtree2.add(&[endf.x, endf.y, endf.z], node_index2);
807 nodes2.push(endf);
808 node_index2 += 1;
809
810 let mut connection1_idx = 0;
811 let mut connection2_idx = 0;
812
813 let mut connect = false;
814
815 let mut search_parameter = 0.01;
818
819 for _i in 0..MAX_POINTS {
821 if connect {
822 break;
823 }
824
825 let (sampled_point1, sampled_point2) = {
827 let point = point_on_prolate_spheroid(startf, endf, search_parameter);
828 (point, point)
829 };
830
831 let nearest_index1 = kdtree1
833 .nearest_one::<SquaredEuclidean>(&[
834 sampled_point1.x,
835 sampled_point1.y,
836 sampled_point1.z,
837 ])
838 .item;
839 let nearest_index2 = kdtree2
840 .nearest_one::<SquaredEuclidean>(&[
841 sampled_point2.x,
842 sampled_point2.y,
843 sampled_point2.z,
844 ])
845 .item;
846 let nearest1 = nodes1[nearest_index1];
847 let nearest2 = nodes2[nearest_index2];
848
849 let new_point1 = nearest1 + (sampled_point1 - nearest1).normalized().map(|a| a * radius);
851 let new_point2 = nearest2 + (sampled_point2 - nearest2).normalized().map(|a| a * radius);
852
853 if is_valid_edge(&nearest1, &new_point1) {
855 kdtree1.add(&[new_point1.x, new_point1.y, new_point1.z], node_index1);
856 nodes1.push(new_point1);
857 parents1.insert(node_index1, nearest_index1);
858 node_index1 += 1;
859 let NearestNeighbour {
861 distance: check,
862 item: index,
863 } = kdtree2.nearest_one::<SquaredEuclidean>(&[
864 new_point1.x,
865 new_point1.y,
866 new_point1.z,
867 ]);
868 if check < radius {
869 let connection = nodes2[index];
870 connection2_idx = index;
871 nodes1.push(connection);
872 connection1_idx = nodes1.len() - 1;
873 parents1.insert(node_index1, node_index1 - 1);
874 connect = true;
875 }
876 }
877
878 if is_valid_edge(&nearest2, &new_point2) {
880 kdtree2.add(&[new_point2.x, new_point2.y, new_point1.z], node_index2);
881 nodes2.push(new_point2);
882 parents2.insert(node_index2, nearest_index2);
883 node_index2 += 1;
884 let NearestNeighbour {
886 distance: check,
887 item: index,
888 } = kdtree1.nearest_one::<SquaredEuclidean>(&[
889 new_point2.x,
890 new_point2.y,
891 new_point1.z,
892 ]);
893 if check < radius {
894 let connection = nodes1[index];
895 connection1_idx = index;
896 nodes2.push(connection);
897 connection2_idx = nodes2.len() - 1;
898 parents2.insert(node_index2, node_index2 - 1);
899 connect = true;
900 }
901 }
902 search_parameter += 0.02;
904 }
905
906 if connect {
907 let mut current_node_index1 = connection1_idx;
909 while current_node_index1 > 0 {
910 current_node_index1 = *parents1.get(¤t_node_index1).unwrap_or(&0);
911 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
912 }
913 let mut current_node_index2 = connection2_idx;
914 while current_node_index2 > 0 {
915 current_node_index2 = *parents2.get(¤t_node_index2).unwrap_or(&0);
916 path2.push(nodes2[current_node_index2].map(|e| e.floor() as i32));
917 }
918 path1.pop();
920 path1.reverse();
921 path.append(&mut path1);
922 path.append(&mut path2);
923 path.dedup();
924 } else {
925 let mut current_node_index1 = kdtree1
928 .nearest_one::<SquaredEuclidean>(&[endf.x, endf.y, endf.z])
929 .item;
930 for _i in 0..3 {
932 if current_node_index1 == 0
933 || nodes1[current_node_index1].distance_squared(startf) < 4.0
934 {
935 if let Some(index) = parents1.values().into_iter().choose(&mut thread_rng()) {
936 current_node_index1 = *index;
937 } else {
938 break;
939 }
940 } else {
941 break;
942 }
943 }
944 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
945 while current_node_index1 != 0 && nodes1[current_node_index1].distance_squared(startf) > 4.0
947 {
948 current_node_index1 = *parents1.get(¤t_node_index1).unwrap_or(&0);
949 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
950 }
951
952 path1.reverse();
953 path.append(&mut path1);
954 }
955 let mut new_path = Vec::new();
956 let mut node = path[0];
957 new_path.push(node);
958 let mut node_idx = 0;
959 let num_nodes = path.len();
960 let end = path[num_nodes - 1];
961 while node != end {
962 let next_idx = if node_idx + 4 > num_nodes - 1 {
963 num_nodes - 1
964 } else {
965 node_idx + 4
966 };
967 let next_node = path[next_idx];
968 let start_pos = node.map(|e| e as f32 + 0.5);
969 let end_pos = next_node.map(|e| e as f32 + 0.5);
970 if vol
971 .ray(start_pos, end_pos)
972 .until(Block::is_solid)
973 .cast()
974 .0
975 .powi(2)
976 > (start_pos).distance_squared(end_pos)
977 {
978 node_idx = next_idx;
979 new_path.push(next_node);
980 } else {
981 node_idx += 1;
982 }
983 node = path[node_idx];
984 }
985 path = new_path;
986 (Some(path.into_iter().collect()), connect)
987}
988
989#[cfg(feature = "rrt_pathfinding")]
1000pub fn point_on_prolate_spheroid(
1001 focus1: Vec3<f32>,
1002 focus2: Vec3<f32>,
1003 search_parameter: f32,
1004) -> Vec3<f32> {
1005 let mut rng = thread_rng();
1006 let range = Uniform::from(0.0..1.0);
1008
1009 let midpoint = 0.5 * (focus1 + focus2);
1011 let radius: f32 = focus1.distance(focus2);
1013 let linear_eccentricity: f32 = 0.5 * radius;
1017
1018 let c: f32 = linear_eccentricity + search_parameter;
1025 let a: f32 = (c.powi(2) - linear_eccentricity.powi(2)).powf(0.5);
1028 let b: f32 = a;
1030
1031 let rtheta: f32 = PI * range.sample(&mut rng) - 0.5 * PI;
1043 let lambda: f32 = 2.0 * PI * range.sample(&mut rng);
1044 let point = Vec3::new(
1046 a * rtheta.cos() * lambda.cos(),
1047 b * rtheta.cos() * lambda.sin(),
1048 c * rtheta.sin(),
1049 );
1050 let dz = focus2.z - focus1.z;
1068 let theta: f32 = if radius > 0.0 {
1085 (dz / radius).acos()
1086 } else {
1087 0.0
1088 };
1089 let r_vec = focus2 - focus1;
1091 let perp_vec = Vec3::new(-1.0 * r_vec.y, r_vec.x, 0.0).normalized();
1093 let l = perp_vec.x;
1094 let m = perp_vec.y;
1095 let n = perp_vec.z;
1096 let rot_2_mat = Mat3::new(
1098 l * l * (1.0 - theta.cos()),
1099 m * l * (1.0 - theta.cos()) - n * theta.sin(),
1100 n * l * (1.0 - theta.cos()) + m * theta.sin(),
1101 l * m * (1.0 - theta.cos()) + n * theta.sin(),
1102 m * m * (1.0 - theta.cos()) + theta.cos(),
1103 n * m * (1.0 - theta.cos()) - l * theta.sin(),
1104 l * n * (1.0 - theta.cos()) - m * theta.sin(),
1105 m * n * (1.0 - theta.cos()) + l * theta.sin(),
1106 n * n * (1.0 - theta.cos()) + theta.cos(),
1107 );
1108
1109 midpoint + rot_2_mat * point
1113}