Skip to content

Commit

Permalink
Big performance optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Rikatemu committed Jul 1, 2024
1 parent 679d3d8 commit c2b6b67
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "repath"
version = "0.0.8"
version = "0.0.9"
edition = "2021"
authors = ["Jaroslav Patočka <[email protected]>"]
description = "A fast pathfinding library using A* algorithm, caching, precomputation and path segmentation with concurrent pathfinding."
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RePath

[![Crate](https://img.shields.io/crates/v/repath.svg?label=crate)](https://crates.io/crates/repath)
[![Docs](https://docs.rs/repath/badge.svg)](https://docs.rs/repath/0.0.7/repath/)
[![Docs](https://docs.rs/repath/badge.svg)](https://docs.rs/repath/0.0.9/repath/)
[![Rust](https://github.com/Abyssall-Dev/RePath/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/Abyssall-Dev/RePath/actions/workflows/rust.yml)

RePath is a fast and efficient pathfinding library. It leverages the A* algorithm to provide rapid and precise pathfinding solution on OBJ navmeshes, essential for managing large numbers of NPCs in real-time environments.
Expand Down Expand Up @@ -32,7 +32,7 @@ Add RePath to your `Cargo.toml`:

```toml
[dependencies]
repath = "0.0.8"
repath = "0.0.9"
```

Make sure you have the OBJ file containing the navmesh in the same directory as your project.
Expand Down
53 changes: 25 additions & 28 deletions src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,16 @@ impl Graph {

pub fn a_star(&self, start: usize, goal: usize, cache: &Mutex<LruCache<(usize, usize), Option<Path>>>) -> Option<Path> {
let cache_key = (start, goal);
{
// Check if the path is already in cache
let mut cache = cache.lock().unwrap();
if let Some(result) = cache.get(&cache_key) {
return result.clone();
}

// Check if the path is already in cache
if let Some(result) = cache.lock().unwrap().get(&cache_key) {
return result.clone();
}

let mut open_set = BinaryHeap::new();
let mut came_from: HashMap<usize, usize> = HashMap::new();
let mut g_score: HashMap<usize, f64> = self.nodes.keys().map(|&k| (k, f64::INFINITY)).collect();
let mut f_score: HashMap<usize, f64> = self.nodes.keys().map(|&k| (k, f64::INFINITY)).collect();
let mut came_from = HashMap::new();
let mut g_score = HashMap::new();
let mut f_score = HashMap::new();
let mut closed_set = HashSet::new();

g_score.insert(start, 0.0);
Expand All @@ -66,7 +64,7 @@ impl Graph {
position: start,
});

while let Some(State { cost: _cost, position: current }) = open_set.pop() {
while let Some(State { cost: _, position: current }) = open_set.pop() {
if current == goal {
// Path found
let mut total_path = vec![self.nodes[&current]];
Expand All @@ -90,10 +88,7 @@ impl Graph {
let result = Some(total_path.clone());

// Cache the result
{
let mut cache = cache.lock().unwrap();
cache.put(cache_key, result.clone());
}
cache.lock().unwrap().put(cache_key, result.clone());

return result;
}
Expand All @@ -102,14 +97,19 @@ impl Graph {

if let Some(neighbors) = self.edges.get(&current) {
for edge in neighbors {
let tentative_g_score = g_score[&current] + edge.cost;
if closed_set.contains(&edge.to) {
continue;
}

let tentative_g_score = g_score.get(&current).unwrap_or(&f64::INFINITY) + edge.cost;

if tentative_g_score < g_score[&edge.to] {
if tentative_g_score < *g_score.get(&edge.to).unwrap_or(&f64::INFINITY) {
came_from.insert(edge.to, current);
g_score.insert(edge.to, tentative_g_score);
f_score.insert(edge.to, tentative_g_score + self.heuristic(edge.to, goal));
let f_score_value = tentative_g_score + self.heuristic(edge.to, goal);
f_score.insert(edge.to, f_score_value);
open_set.push(State {
cost: tentative_g_score + self.heuristic(edge.to, goal),
cost: f_score_value,
position: edge.to,
});
}
Expand All @@ -118,22 +118,19 @@ impl Graph {
}

// Cache the non-result, so that it doesn't try to find path next time
{
let mut cache = cache.lock().unwrap();
cache.put(cache_key, None);
}
cache.lock().unwrap().put(cache_key, None);

None
}

pub fn nearest_node(&self, x: f64, y: f64, z: f64) -> Option<usize> {
self.nodes.iter()
.min_by(|(_, a), (_, b)| {
let da = distance(&(a.x, a.y, a.z), &(x, y, z));
let db = distance(&(b.x, b.y, b.z), &(x, y, z));
da.partial_cmp(&db).unwrap()
.map(|(&id, node)| {
let d = distance(&(node.x, node.y, node.z), &(x, y, z));
(d, id)
})
.map(|(&id, _)| id)
.min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
.map(|(_, id)| id)
}

pub fn random_node(&self) -> Option<usize> {
Expand All @@ -155,7 +152,7 @@ pub struct State {

impl Ord for State {
fn cmp(&self, other: &Self) -> Ordering {
other.cost.partial_cmp(&self.cost).unwrap()
other.cost.partial_cmp(&self.cost).unwrap_or(Ordering::Equal)
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ mod tests {
let start = std::time::Instant::now();
let path1 = pathfinder.find_path(start_coords, end_coords);
println!("Time to find path singlethreaded: {:?}", start.elapsed());
if path1.clone().is_some() {
println!("Path found: {:?}", path1.clone().unwrap());
}

assert!(path1.is_some());

Expand Down

0 comments on commit c2b6b67

Please sign in to comment.