pub mod node;
pub mod edge;
pub mod graph;
pub mod metrics;
mod path;
pub mod pathfinder;
pub mod settings;
pub mod utils;
pub use pathfinder::RePathfinder;
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::Graph;
use crate::settings::RePathSettings;
use rand::seq::SliceRandom;
use rand::thread_rng;
#[test]
fn test_pathfinding() {
let settings = RePathSettings {
navmesh_filename: "NavMesh.obj".to_string(),
precompute_radius: 10000.0,
total_precompute_pairs: 5000,
use_precomputed_cache: true,
};
let pathfinder = RePathfinder::new(settings);
fn print_graph_bounds(graph: &Graph) {
let mut min_x = f32::MAX;
let mut min_y = f32::MAX;
let mut min_z = f32::MAX;
let mut max_x = f32::MIN;
let mut max_y = f32::MIN;
let mut max_z = f32::MIN;
for node in &graph.nodes {
if node.x < min_x { min_x = node.x; }
if node.y < min_y { min_y = node.y; }
if node.z < min_z { min_z = node.z; }
if node.x > max_x { max_x = node.x; }
if node.y > max_y { max_y = node.y; }
if node.z > max_z { max_z = node.z; }
}
println!("Graph bounds:");
println!("X: {} to {}", min_x, max_x);
println!("Y: {} to {}", min_y, max_y);
println!("Z: {} to {}", min_z, max_z);
}
print_graph_bounds(&pathfinder.graph);
let start_node_id = find_non_isolated_start_node(&pathfinder.graph)
.expect("Could not find a non-isolated start node");
let start_node = &pathfinder.graph.nodes[start_node_id];
let start_coords = (start_node.x, start_node.y, start_node.z);
println!(
"Selected start node ID: {}, Position: {:?}",
start_node_id, start_node
);
println!(
"Edges from start node (ID: {}): {:?}",
start_node_id,
pathfinder.graph.edges[start_node_id]
);
println!(
"Number of edges from start node: {}",
pathfinder.graph.edges[start_node_id].len()
);
let end_node_id = find_connected_node(&pathfinder.graph, start_node_id)
.expect("Could not find a node connected to the start node");
let end_node = &pathfinder.graph.nodes[end_node_id];
println!(
"Selected end node ID: {}, Position: {:?}",
end_node_id, end_node
);
let end_coords = (end_node.x, end_node.y, end_node.z);
let connected = are_nodes_connected(&pathfinder.graph, start_node_id, end_node_id);
assert!(connected, "Start and end nodes are not connected");
let start_time = std::time::Instant::now();
let path1 = pathfinder.find_path(start_coords, end_coords);
println!("Time to find path single-threaded: {:?}", start_time.elapsed());
if let Some(path) = &path1 {
println!("Path found with {} nodes.", path.len());
} else {
println!("No path found between start_coords and end_coords");
}
assert!(path1.is_some(), "No path found between start_coords and end_coords");
let start_time = std::time::Instant::now();
let path2 = pathfinder.find_path_multithreaded(start_coords, end_coords, 4);
println!("Time to find path multi-threaded: {:?}", start_time.elapsed());
if let Some(path) = &path2 {
println!("Multithreaded path found with {} nodes.", path.len());
} else {
println!("No path found between start_coords and end_coords using multithreaded pathfinding");
}
assert!(
path2.is_some(),
"No path found between start_coords and end_coords with multithreaded pathfinding"
);
}
fn find_non_isolated_start_node(graph: &Graph) -> Option<usize> {
for (node_id, edges) in graph.edges.iter().enumerate() {
if !edges.is_empty() {
return Some(node_id);
}
}
None
}
fn find_connected_node(graph: &Graph, start_node_id: usize) -> Option<usize> {
let connected_nodes: Vec<usize> = graph.edges[start_node_id].iter().map(|edge| edge.to).collect();
if connected_nodes.is_empty() {
return None;
}
let mut rng = thread_rng();
let &potential_goal = connected_nodes.choose(&mut rng)?;
Some(potential_goal)
}
use std::collections::VecDeque;
fn are_nodes_connected(graph: &Graph, start: usize, goal: usize) -> bool {
let mut visited = vec![false; graph.nodes.len()];
let mut queue = VecDeque::new();
queue.push_back(start);
while let Some(current) = queue.pop_front() {
if current == goal {
return true;
}
if visited[current] {
continue;
}
visited[current] = true;
for edge in &graph.edges[current] {
if !visited[edge.to] {
queue.push_back(edge.to);
}
}
}
false
}
}