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 (-2..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 vol.get(next0 + Vec3::new(pos.x, pos.y, 1))
153 .map(|b| b.is_solid())
154 .unwrap_or(true)
155 });
156
157 let be_precise = open_space_nearby || wall_nearby;
159
160 let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
162 let closest_tgt = next_tgt
163 .map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil()))
164 .xy()
165 .with_z(next_tgt.z);
166 let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy());
168 if dist_sqrd
169 < (traversal_cfg.node_tolerance
170 * if be_precise {
171 0.5
172 } else if traversal_cfg.in_liquid {
173 2.5
174 } else {
175 1.0
176 })
177 .powi(2)
178 && ((pos.z - 0.75 - closest_tgt.z).abs() < 1.0
179 || (traversal_cfg.in_liquid
180 && pos.z < closest_tgt.z + 0.8
181 && pos.z > closest_tgt.z))
182 {
183 self.next_idx += 1;
185 } else {
186 break (next0, next1, next_tgt, be_precise);
188 }
189 };
190
191 fn gradient(line: LineSegment2<f32>) -> f32 {
192 let r = (line.start.y - line.end.y) / (line.start.x - line.end.x);
193 if r.is_nan() { 100000.0 } else { r }
194 }
195
196 fn intersect(a: LineSegment2<f32>, b: LineSegment2<f32>) -> Option<Vec2<f32>> {
197 let ma = gradient(a);
198 let mb = gradient(b);
199
200 let ca = a.start.y - ma * a.start.x;
201 let cb = b.start.y - mb * b.start.x;
202
203 if (ma - mb).abs() < 0.0001 || (ca - cb).abs() < 0.0001 {
204 None
205 } else {
206 let x = (cb - ca) / (ma - mb);
207 let y = ma * x + ca;
208
209 Some(Vec2::new(x, y))
210 }
211 }
212
213 let corners = [
225 Vec2::new(0, 0),
226 Vec2::new(1, 0),
227 Vec2::new(1, 1),
228 Vec2::new(0, 1),
229 Vec2::new(0, 0), ];
231
232 let vel_line = LineSegment2 {
233 start: pos.xy(),
234 end: pos.xy() + vel.xy() * 100.0,
235 };
236
237 let align = |block_pos: Vec3<i32>, precision: f32| {
238 let lerp_block =
239 |x, precision| Lerp::lerp(x, block_pos.xy().map(|e| e as f32), precision);
240
241 (0..4)
242 .filter_map(|i| {
243 let edge_line = LineSegment2 {
244 start: lerp_block(
245 (block_pos.xy() + corners[i]).map(|e| e as f32),
246 precision,
247 ),
248 end: lerp_block(
249 (block_pos.xy() + corners[i + 1]).map(|e| e as f32),
250 precision,
251 ),
252 };
253 intersect(vel_line, edge_line).filter(|intersect| {
254 intersect
255 .clamped(
256 block_pos.xy().map(|e| e as f32),
257 block_pos.xy().map(|e| e as f32 + 1.0),
258 )
259 .distance_squared(*intersect)
260 < 0.001
261 })
262 })
263 .min_by_key(|intersect: &Vec2<f32>| {
264 (intersect.distance_squared(vel_line.end) * 1000.0) as i32
265 })
266 .unwrap_or_else(|| {
267 (0..2)
268 .flat_map(|i| (0..2).map(move |j| Vec2::new(i, j)))
269 .map(|rpos| block_pos + rpos)
270 .map(|block_pos| {
271 let block_posf = block_pos.xy().map(|e| e as f32);
272 let proj = vel_line.projected_point(block_posf);
273 let clamped = lerp_block(
274 proj.clamped(
275 block_pos.xy().map(|e| e as f32),
276 block_pos.xy().map(|e| e as f32),
277 ),
278 precision,
279 );
280
281 (proj.distance_squared(clamped), clamped)
282 })
283 .min_by_key(|(d2, _)| (d2 * 1000.0) as i32)
284 .unwrap()
285 .1
286 })
287 };
288
289 let bez = CubicBezier2 {
290 start: pos.xy(),
291 ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0,
292 ctrl1: align(next0, 1.0),
293 end: align(next1, 1.0),
294 };
295
296 let next_dir = bez
301 .evaluate_derivative(0.85)
302 .try_normalized()
303 .unwrap_or_default();
304 let straight_factor = next_dir
305 .dot(vel.xy().try_normalized().unwrap_or(next_dir))
306 .max(0.0)
307 .powi(2);
308
309 let bez = CubicBezier2 {
310 start: pos.xy(),
311 ctrl0: pos.xy() + vel.xy().try_normalized().unwrap_or_default() * 1.0,
312 ctrl1: align(
313 next0,
314 (1.0 - if (next0.z as f32 - pos.z).abs() < 0.25 && !be_precise {
315 straight_factor
316 } else {
317 0.0
318 })
319 .max(0.1),
320 ),
321 end: align(next1, 1.0),
322 };
323
324 let tgt2d = bez.evaluate(if (next0.z as f32 - pos.z).abs() < 0.25 {
325 0.25
326 } else {
327 0.5
328 });
329 let tgt = if be_precise {
330 next_tgt
331 } else {
332 Vec3::from(tgt2d) + Vec3::unit_z() * next_tgt.z
333 };
334
335 Some((
336 tgt - pos,
337 1.0 - (traversal_cfg.slow_factor * (1.0 - straight_factor)).min(0.9),
341 ))
342 .filter(|(bearing, _)| bearing.z < 2.1)
343 }
344}
345
346#[derive(Default, Clone, Debug)]
349pub struct Chaser {
350 last_search_tgt: Option<Vec3<f32>>,
351 route: Option<(Route, bool)>,
353 astar: Option<Astar<Node, FxBuildHasher>>,
358}
359
360impl Chaser {
361 pub fn chase<V>(
365 &mut self,
366 vol: &V,
367 pos: Vec3<f32>,
368 vel: Vec3<f32>,
369 tgt: Vec3<f32>,
370 traversal_cfg: TraversalConfig,
371 ) -> Option<(Vec3<f32>, f32)>
372 where
373 V: BaseVol<Vox = Block> + ReadVol,
374 {
375 span!(_guard, "chase", "Chaser::chase");
376 let pos_to_tgt = pos.distance(tgt);
377
378 let end = self
380 .route
381 .as_ref()
382 .and_then(|(r, _)| r.path.end().copied())
383 .map(|e| e.map(|e| e as f32 + 0.5))
384 .unwrap_or(tgt);
385 if ((pos - end) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared()
386 < traversal_cfg.min_tgt_dist.powi(2)
387 {
388 self.route = None;
389 return None;
390 }
391
392 let bearing = if let Some((end, complete)) = self
393 .route
394 .as_ref()
395 .and_then(|(r, complete)| Some((r.path().end().copied()?, *complete)))
396 {
397 let end_to_tgt = end.map(|e| e as f32).distance(tgt);
398 if end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete && traversal_cfg.is_target_loaded {
405 self.astar = None;
406 None
407 } else if vel.magnitude_squared() < 0.2f32.powi(2)
408 && thread_rng().gen::<f32>() < 0.0025
409 && complete
410 {
411 self.route = None;
412 None
413 } else {
414 self.route
415 .as_mut()
416 .and_then(|(r, _)| r.traverse(vol, pos, vel, &traversal_cfg))
417 }
418 } else {
419 None
421 };
422
423 if let Some((bearing, speed)) = bearing {
425 Some((bearing, speed))
426 } else {
427 let tgt_dir = (tgt - pos).xy().try_normalized().unwrap_or_default();
431
432 if self
436 .last_search_tgt
437 .map(|last_tgt| last_tgt.distance(tgt) > pos_to_tgt * 0.15 + 5.0)
438 .unwrap_or(true)
439 || self.astar.is_some()
440 || self.route.is_none()
441 || !traversal_cfg.is_target_loaded
442 {
443 self.last_search_tgt = Some(tgt);
444
445 let (path, complete) = {
449 find_path(&mut self.astar, vol, pos, tgt, &traversal_cfg)
450 };
451
452 self.route = path.map(|path| {
453 let start_index = path
454 .iter()
455 .enumerate()
456 .min_by_key(|(_, node)| {
457 node.map(|e| e as f32).distance_squared(pos + tgt_dir) as i32
458 })
459 .map(|(idx, _)| idx);
460
461 (
462 Route {
463 path,
464 next_idx: start_index.unwrap_or(0),
465 },
466 complete,
467 )
468 });
469 }
470 if let Some(bearing) = self
472 .route
473 .as_mut()
474 .and_then(|(r, _)| r.traverse(vol, pos, vel, &traversal_cfg))
475 {
476 Some(bearing)
477 } else {
478 let walking_towards_edge = (-8..2).all(|z| {
483 vol.get(
484 (pos + Vec3::<f32>::from(tgt_dir) * 2.5).map(|e| e as i32)
485 + Vec3::unit_z() * z,
486 )
487 .map(|b| b.is_air())
488 .unwrap_or(false)
489 });
490
491 if traversal_cfg.can_fly {
496 Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.5), 1.0))
497 } else if !walking_towards_edge {
498 Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 1.0))
499 } else {
500 None
505 }
506 }
507 }
508 }
509}
510
511fn walkable<V>(vol: &V, pos: Vec3<i32>) -> bool
512where
513 V: BaseVol<Vox = Block> + ReadVol,
514{
515 let below = vol
516 .get(pos - Vec3::unit_z())
517 .ok()
518 .copied()
519 .unwrap_or_else(Block::empty);
520 let a = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
521 let b = vol
522 .get(pos + Vec3::unit_z())
523 .ok()
524 .copied()
525 .unwrap_or_else(Block::empty);
526
527 let on_ground = below.is_filled();
528 let in_liquid = a.is_liquid();
529 (on_ground || in_liquid) && !a.is_solid() && !b.is_solid()
530}
531
532#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
533pub struct Node {
534 pos: Vec3<i32>,
535 last_dir: Vec2<i32>,
536 last_dir_count: u32,
537}
538
539fn find_path<V>(
542 astar: &mut Option<Astar<Node, FxBuildHasher>>,
543 vol: &V,
544 startf: Vec3<f32>,
545 endf: Vec3<f32>,
546 traversal_cfg: &TraversalConfig,
547) -> (Option<Path<Vec3<i32>>>, bool)
548where
549 V: BaseVol<Vox = Block> + ReadVol,
550{
551 let is_walkable = |pos: &Vec3<i32>| walkable(vol, *pos);
552 let get_walkable_z = |pos| {
553 let mut z_incr = 0;
554 for _ in 0..32 {
555 let test_pos = pos + Vec3::unit_z() * z_incr;
556 if is_walkable(&test_pos) {
557 return Some(test_pos);
558 }
559 z_incr = -z_incr + i32::from(z_incr <= 0);
560 }
561 None
562 };
563
564 let (start, end) = match (
565 get_walkable_z(startf.map(|e| e.floor() as i32)),
566 get_walkable_z(endf.map(|e| e.floor() as i32)),
567 ) {
568 (Some(start), Some(end)) => (start, end),
569
570 (Some(start), None) if !traversal_cfg.is_target_loaded => {
572 (start, endf.map(|e| e.floor() as i32))
573 },
574
575 _ => return (None, false),
576 };
577
578 let heuristic = |node: &Node| node.pos.as_().distance(end.as_());
579 let transition = |a: Node, b: Node| {
580 1.0
581 + b.last_dir_count as f32 * 0.01
585 + (b.pos.z - a.pos.z).max(0) as f32 * 2.0
587 };
588 let neighbors = |node: &Node| {
589 let node = *node;
590 let pos = node.pos;
591 const DIRS: [Vec3<i32>; 17] = [
592 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), ];
610
611 const JUMPS: [Vec3<i32>; 4] = [
612 Vec3::new(0, 1, 2), Vec3::new(1, 0, 2), Vec3::new(0, -1, 2), Vec3::new(-1, 0, 2), ];
617
618 DIRS.iter()
637 .chain(
638 Some(JUMPS.iter())
639 .filter(|_| {
640 vol.get(pos - Vec3::unit_z())
641 .map(|b| !b.is_liquid())
642 .unwrap_or(traversal_cfg.is_target_loaded)
643 || traversal_cfg.can_climb
644 || traversal_cfg.can_fly
645 })
646 .into_iter()
647 .flatten(),
648 )
649 .map(move |dir| (pos, dir))
650 .filter(move |(pos, dir)| {
651 (traversal_cfg.can_fly || is_walkable(pos) && is_walkable(&(*pos + **dir)))
652 && ((dir.z < 1
653 || vol
654 .get(pos + Vec3::unit_z() * 2)
655 .map(|b| !b.is_solid())
656 .unwrap_or(traversal_cfg.is_target_loaded))
657 && (dir.z < 2
658 || vol
659 .get(pos + Vec3::unit_z() * 3)
660 .map(|b| !b.is_solid())
661 .unwrap_or(traversal_cfg.is_target_loaded))
662 && (dir.z >= 0
663 || vol
664 .get(pos + *dir + Vec3::unit_z() * 2)
665 .map(|b| !b.is_solid())
666 .unwrap_or(traversal_cfg.is_target_loaded)))
667 })
668 .map(move |(pos, dir)| {
669 let next_node = Node {
670 pos: pos + dir,
671 last_dir: dir.xy(),
672 last_dir_count: if node.last_dir == dir.xy() {
673 node.last_dir_count + 1
674 } else {
675 0
676 },
677 };
678
679 (next_node, transition(node, next_node))
680 })
681 };
690
691 let satisfied = |node: &Node| node.pos == end;
692
693 let mut new_astar = match astar.take() {
694 None => Astar::new(
695 if traversal_cfg.is_target_loaded {
696 50_000
698 } else {
699 500
702 },
703 Node {
704 pos: start,
705 last_dir: Vec2::zero(),
706 last_dir_count: 0,
707 },
708 FxBuildHasher::default(),
709 ),
710 Some(astar) => astar,
711 };
712
713 let path_result = new_astar.poll(250, heuristic, neighbors, satisfied);
714
715 let (path, finished) = match path_result {
716 PathResult::Path(path, _cost) => (Some(path), true),
717 PathResult::None(path) => (Some(path), false),
718 PathResult::Exhausted(path) => (Some(path), false),
719
720 PathResult::Pending => {
721 *astar = Some(new_astar);
723
724 (None, false)
725 },
726 };
727 (
728 path.map(|path| path.nodes.into_iter().map(|n| n.pos).collect()),
729 finished,
730 )
731}
732
733#[cfg(feature = "rrt_pathfinding")]
735fn find_air_path<V>(
736 vol: &V,
737 startf: Vec3<f32>,
738 endf: Vec3<f32>,
739 traversal_cfg: &TraversalConfig,
740) -> (Option<Path<Vec3<i32>>>, bool)
741where
742 V: BaseVol<Vox = Block> + ReadVol,
743{
744 let radius = traversal_cfg.node_tolerance;
745 let total_dist_sqrd = startf.distance_squared(endf);
746 if vol
748 .ray(startf + Vec3::unit_z(), endf + Vec3::unit_z())
749 .until(Block::is_opaque)
750 .cast()
751 .0
752 .powi(2)
753 >= total_dist_sqrd
754 {
755 let mut path = Vec::new();
756 path.push(endf.map(|e| e.floor() as i32));
757 let connect = true;
758 (Some(path.into_iter().collect()), connect)
759 } else {
761 let is_traversable = |start: &Vec3<f32>, end: &Vec3<f32>| {
762 vol.ray(*start, *end)
763 .until(Block::is_solid)
764 .cast()
765 .0
766 .powi(2)
767 > (*start).distance_squared(*end)
768 };
771 informed_rrt_connect(vol, startf, endf, is_traversable, radius)
772 }
773}
774
775#[cfg(feature = "rrt_pathfinding")]
786fn informed_rrt_connect<V>(
787 vol: &V,
788 startf: Vec3<f32>,
789 endf: Vec3<f32>,
790 is_valid_edge: impl Fn(&Vec3<f32>, &Vec3<f32>) -> bool,
791 radius: f32,
792) -> (Option<Path<Vec3<i32>>>, bool)
793where
794 V: BaseVol<Vox = Block> + ReadVol,
795{
796 const MAX_POINTS: usize = 7000;
797 let mut path = Vec::new();
798
799 let mut node_index1: usize = 0;
801 let mut node_index2: usize = 0;
802 let mut nodes1 = Vec::new();
803 let mut nodes2 = Vec::new();
804
805 let mut parents1 = HashMap::new();
808 let mut parents2 = HashMap::new();
809
810 let mut path1 = Vec::new();
813 let mut path2 = Vec::new();
814
815 let mut kdtree1: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
817 let mut kdtree2: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
818
819 kdtree1.add(&[startf.x, startf.y, startf.z], node_index1);
821 nodes1.push(startf);
822 node_index1 += 1;
823
824 kdtree2.add(&[endf.x, endf.y, endf.z], node_index2);
826 nodes2.push(endf);
827 node_index2 += 1;
828
829 let mut connection1_idx = 0;
830 let mut connection2_idx = 0;
831
832 let mut connect = false;
833
834 let mut search_parameter = 0.01;
837
838 for _i in 0..MAX_POINTS {
840 if connect {
841 break;
842 }
843
844 let (sampled_point1, sampled_point2) = {
846 let point = point_on_prolate_spheroid(startf, endf, search_parameter);
847 (point, point)
848 };
849
850 let nearest_index1 = kdtree1
852 .nearest_one::<SquaredEuclidean>(&[
853 sampled_point1.x,
854 sampled_point1.y,
855 sampled_point1.z,
856 ])
857 .item;
858 let nearest_index2 = kdtree2
859 .nearest_one::<SquaredEuclidean>(&[
860 sampled_point2.x,
861 sampled_point2.y,
862 sampled_point2.z,
863 ])
864 .item;
865 let nearest1 = nodes1[nearest_index1];
866 let nearest2 = nodes2[nearest_index2];
867
868 let new_point1 = nearest1 + (sampled_point1 - nearest1).normalized().map(|a| a * radius);
870 let new_point2 = nearest2 + (sampled_point2 - nearest2).normalized().map(|a| a * radius);
871
872 if is_valid_edge(&nearest1, &new_point1) {
874 kdtree1.add(&[new_point1.x, new_point1.y, new_point1.z], node_index1);
875 nodes1.push(new_point1);
876 parents1.insert(node_index1, nearest_index1);
877 node_index1 += 1;
878 let NearestNeighbour {
880 distance: check,
881 item: index,
882 } = kdtree2.nearest_one::<SquaredEuclidean>(&[
883 new_point1.x,
884 new_point1.y,
885 new_point1.z,
886 ]);
887 if check < radius {
888 let connection = nodes2[index];
889 connection2_idx = index;
890 nodes1.push(connection);
891 connection1_idx = nodes1.len() - 1;
892 parents1.insert(node_index1, node_index1 - 1);
893 connect = true;
894 }
895 }
896
897 if is_valid_edge(&nearest2, &new_point2) {
899 kdtree2.add(&[new_point2.x, new_point2.y, new_point1.z], node_index2);
900 nodes2.push(new_point2);
901 parents2.insert(node_index2, nearest_index2);
902 node_index2 += 1;
903 let NearestNeighbour {
905 distance: check,
906 item: index,
907 } = kdtree1.nearest_one::<SquaredEuclidean>(&[
908 new_point2.x,
909 new_point2.y,
910 new_point1.z,
911 ]);
912 if check < radius {
913 let connection = nodes1[index];
914 connection1_idx = index;
915 nodes2.push(connection);
916 connection2_idx = nodes2.len() - 1;
917 parents2.insert(node_index2, node_index2 - 1);
918 connect = true;
919 }
920 }
921 search_parameter += 0.02;
923 }
924
925 if connect {
926 let mut current_node_index1 = connection1_idx;
928 while current_node_index1 > 0 {
929 current_node_index1 = *parents1.get(¤t_node_index1).unwrap_or(&0);
930 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
931 }
932 let mut current_node_index2 = connection2_idx;
933 while current_node_index2 > 0 {
934 current_node_index2 = *parents2.get(¤t_node_index2).unwrap_or(&0);
935 path2.push(nodes2[current_node_index2].map(|e| e.floor() as i32));
936 }
937 path1.pop();
939 path1.reverse();
940 path.append(&mut path1);
941 path.append(&mut path2);
942 path.dedup();
943 } else {
944 let mut current_node_index1 = kdtree1
947 .nearest_one::<SquaredEuclidean>(&[endf.x, endf.y, endf.z])
948 .item;
949 for _i in 0..3 {
951 if current_node_index1 == 0
952 || nodes1[current_node_index1].distance_squared(startf) < 4.0
953 {
954 if let Some(index) = parents1.values().into_iter().choose(&mut thread_rng()) {
955 current_node_index1 = *index;
956 } else {
957 break;
958 }
959 } else {
960 break;
961 }
962 }
963 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
964 while current_node_index1 != 0 && nodes1[current_node_index1].distance_squared(startf) > 4.0
966 {
967 current_node_index1 = *parents1.get(¤t_node_index1).unwrap_or(&0);
968 path1.push(nodes1[current_node_index1].map(|e| e.floor() as i32));
969 }
970
971 path1.reverse();
972 path.append(&mut path1);
973 }
974 let mut new_path = Vec::new();
975 let mut node = path[0];
976 new_path.push(node);
977 let mut node_idx = 0;
978 let num_nodes = path.len();
979 let end = path[num_nodes - 1];
980 while node != end {
981 let next_idx = if node_idx + 4 > num_nodes - 1 {
982 num_nodes - 1
983 } else {
984 node_idx + 4
985 };
986 let next_node = path[next_idx];
987 let start_pos = node.map(|e| e as f32 + 0.5);
988 let end_pos = next_node.map(|e| e as f32 + 0.5);
989 if vol
990 .ray(start_pos, end_pos)
991 .until(Block::is_solid)
992 .cast()
993 .0
994 .powi(2)
995 > (start_pos).distance_squared(end_pos)
996 {
997 node_idx = next_idx;
998 new_path.push(next_node);
999 } else {
1000 node_idx += 1;
1001 }
1002 node = path[node_idx];
1003 }
1004 path = new_path;
1005 (Some(path.into_iter().collect()), connect)
1006}
1007
1008#[cfg(feature = "rrt_pathfinding")]
1019pub fn point_on_prolate_spheroid(
1020 focus1: Vec3<f32>,
1021 focus2: Vec3<f32>,
1022 search_parameter: f32,
1023) -> Vec3<f32> {
1024 let mut rng = thread_rng();
1025 let range = Uniform::from(0.0..1.0);
1027
1028 let midpoint = 0.5 * (focus1 + focus2);
1030 let radius: f32 = focus1.distance(focus2);
1032 let linear_eccentricity: f32 = 0.5 * radius;
1036
1037 let c: f32 = linear_eccentricity + search_parameter;
1044 let a: f32 = (c.powi(2) - linear_eccentricity.powi(2)).powf(0.5);
1047 let b: f32 = a;
1049
1050 let rtheta: f32 = PI * range.sample(&mut rng) - 0.5 * PI;
1062 let lambda: f32 = 2.0 * PI * range.sample(&mut rng);
1063 let point = Vec3::new(
1065 a * rtheta.cos() * lambda.cos(),
1066 b * rtheta.cos() * lambda.sin(),
1067 c * rtheta.sin(),
1068 );
1069 let dz = focus2.z - focus1.z;
1087 let theta: f32 = if radius > 0.0 {
1104 (dz / radius).acos()
1105 } else {
1106 0.0
1107 };
1108 let r_vec = focus2 - focus1;
1110 let perp_vec = Vec3::new(-1.0 * r_vec.y, r_vec.x, 0.0).normalized();
1112 let l = perp_vec.x;
1113 let m = perp_vec.y;
1114 let n = perp_vec.z;
1115 let rot_2_mat = Mat3::new(
1117 l * l * (1.0 - theta.cos()),
1118 m * l * (1.0 - theta.cos()) - n * theta.sin(),
1119 n * l * (1.0 - theta.cos()) + m * theta.sin(),
1120 l * m * (1.0 - theta.cos()) + n * theta.sin(),
1121 m * m * (1.0 - theta.cos()) + theta.cos(),
1122 n * m * (1.0 - theta.cos()) - l * theta.sin(),
1123 l * n * (1.0 - theta.cos()) - m * theta.sin(),
1124 m * n * (1.0 - theta.cos()) + l * theta.sin(),
1125 n * n * (1.0 - theta.cos()) + theta.cos(),
1126 );
1127
1128 midpoint + rot_2_mat * point
1132}