feat(planar-incr-embed): make PmgAstar's evaluate_navmesh interface way more ergonomic

This commit is contained in:
Ellen Emilia Anna Zscheile 2025-03-31 01:35:18 +02:00
parent bb1cfc76d9
commit 3a2c9deff0
2 changed files with 137 additions and 103 deletions

View File

@ -7,6 +7,7 @@
use crate::{ use crate::{
algo::{Goal, PreparedGoal}, algo::{Goal, PreparedGoal},
mayrev::MaybeReversed,
navmesh::{EdgeIndex, EdgePaths, Navmesh, NavmeshRef, NavmeshRefMut}, navmesh::{EdgeIndex, EdgePaths, Navmesh, NavmeshRef, NavmeshRefMut},
Edge, NavmeshBase, NavmeshIndex, RelaxedPath, Edge, NavmeshBase, NavmeshIndex, RelaxedPath,
}; };
@ -69,19 +70,28 @@ pub struct TaskResult<B: NavmeshBase, Ctx> {
pub context: Ctx, pub context: Ctx,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct InsertionInfo<B: NavmeshBase> {
pub prev_node: NavmeshIndex<B::PrimalNodeIndex>,
pub cur_node: NavmeshIndex<B::PrimalNodeIndex>,
pub edge_meta: Edge<B::PrimalNodeIndex>,
pub epi: MaybeReversed<usize, RelaxedPath<B::EtchedPath, B::GapComment>>,
/// the introduction position re: `prev_node` -edge-> `cur_node`
pub intro: usize,
pub maybe_new_goal: Option<B::EtchedPath>,
}
pub trait EvaluateNavmesh<B: NavmeshBase, Ctx>: pub trait EvaluateNavmesh<B: NavmeshBase, Ctx>:
Fn(NavmeshRef<B>, &Ctx, EdgeIndex<NavmeshIndex<B::PrimalNodeIndex>>) -> Option<(B::Scalar, Ctx)> FnMut(NavmeshRef<B>, &Ctx, InsertionInfo<B>) -> Option<(B::Scalar, Ctx)>
{ {
} }
impl<B, Ctx, F> EvaluateNavmesh<B, Ctx> for F impl<B, Ctx, F> EvaluateNavmesh<B, Ctx> for F
where where
B: NavmeshBase, B: NavmeshBase,
F: Fn( F: FnMut(NavmeshRef<B>, &Ctx, InsertionInfo<B>) -> Option<(B::Scalar, Ctx)>,
NavmeshRef<B>,
&Ctx,
EdgeIndex<NavmeshIndex<B::PrimalNodeIndex>>,
) -> Option<(B::Scalar, Ctx)>,
{ {
} }
@ -200,54 +210,67 @@ impl<B: NavmeshBase<Scalar = Scalar>, Scalar: num_traits::Float + core::iter::Su
impl<B: NavmeshBase> PreparedGoal<B> impl<B: NavmeshBase> PreparedGoal<B>
where where
B::GapComment: Clone, B::EtchedPath: PartialOrd,
B::Scalar: num_traits::Float + core::iter::Sum, B::GapComment: PartialOrd + Clone,
B::Scalar: num_traits::Float + num_traits::float::TotalOrder + core::iter::Sum,
{ {
/// start processing the goal /// start processing the goal
fn start_pmga<'a, Ctx, F: EvaluateNavmesh<B, Ctx>>( fn start_pmga<Ctx, F: EvaluateNavmesh<B, Ctx>>(
&'a self, &self,
navmesh: NavmeshRef<'a, B>, navmesh: NavmeshRef<'_, B>,
goal_idx: usize, goal_idx: usize,
env: &'a PmgAstar<B, Ctx>, env: &PmgAstar<B, Ctx>,
context: &'a Ctx, context: &Ctx,
evaluate_navmesh: &'a F, evaluate_navmesh: &mut F,
) -> Option<impl Iterator<Item = Task<B, Ctx>> + 'a> { ) -> BinaryHeap<Task<B, Ctx>> {
let source = NavmeshIndex::Primal(self.source.clone()); let source = NavmeshIndex::Primal(self.source.clone());
let estimated_remaining_goals = env.estimate_remaining_goals_costs(goal_idx); let estimated_remaining_goals = env.estimate_remaining_goals_costs(goal_idx);
Some( let neighs = match navmesh.node_data(&source) {
navmesh None => return BinaryHeap::new(),
.node_data(&source)? Some(x) => &x.neighs,
.neighs };
.iter()
.filter_map({ let mut ret = BinaryHeap::new();
// NOTE: this uses a `for` loop to get around borrowing problems with `evaluate_navmesh`
for (neigh, emeta, epi, edge_len) in neighs.iter().filter_map({
let source = source.clone(); let source = source.clone();
move |neigh| { move |neigh| {
navmesh navmesh
.resolve_edge_data(source.clone(), neigh.clone()) .resolve_edge_data(source.clone(), neigh.clone())
.map(|(_, epi)| { .map(|(emeta, epi)| {
let edge_len = navmesh.access_edge_paths(epi).len(); let edge_len = navmesh.access_edge_paths(epi).len();
(neigh, epi, edge_len) (neigh, emeta.to_owned(), epi, edge_len)
}) })
} }
}) }) {
.flat_map(move |(neigh, epi, edge_len)| {
let eidx = EdgeIndex::from((source.clone(), neigh.clone()));
let source = source.clone(); let source = source.clone();
// A*-like remaining costs estimation // A*-like remaining costs estimation
let estimated_remaining = let estimated_remaining =
self.estimate_costs_for_source::<B::GapComment>(navmesh, neigh); self.estimate_costs_for_source::<B::GapComment>(navmesh, neigh);
(0..=edge_len).filter_map(move |i| { for i in 0..=edge_len {
let mut edge_paths = Box::from(navmesh.edge_paths); let mut edge_paths = Box::from(navmesh.edge_paths);
let mut navmesh = NavmeshRefMut { let mut navmesh = NavmeshRefMut {
nodes: navmesh.nodes, nodes: navmesh.nodes,
edges: navmesh.edges, edges: navmesh.edges,
edge_paths: &mut edge_paths, edge_paths: &mut edge_paths,
}; };
navmesh.access_edge_paths_mut(epi).with_borrow_mut(|mut j| { navmesh
j.insert(i, RelaxedPath::Normal(self.label.clone())) .access_edge_paths_mut(epi)
}); .with_borrow_mut(|mut j| j.insert(i, RelaxedPath::Normal(self.label.clone())));
evaluate_navmesh(navmesh.as_ref(), context, eidx.clone()).map( if let Some(new_task) = (*evaluate_navmesh)(
|(costs, context)| Task { navmesh.as_ref(),
context,
InsertionInfo {
prev_node: source.clone(),
cur_node: neigh.clone(),
edge_meta: emeta.clone(),
epi,
intro: i,
maybe_new_goal: Some(self.label.clone()),
},
)
.map(|(costs, context)| Task {
goal_idx, goal_idx,
costs, costs,
estimated_remaining, estimated_remaining,
@ -257,11 +280,13 @@ where
prev_node: source.clone(), prev_node: source.clone(),
cur_intro: edge_len - i, cur_intro: edge_len - i,
context, context,
}, }) {
) ret.push(new_task);
}) }
}), }
) }
ret
} }
} }
@ -309,7 +334,7 @@ where
fn progress<F: EvaluateNavmesh<B, Ctx>>( fn progress<F: EvaluateNavmesh<B, Ctx>>(
&self, &self,
env: &mut PmgAstar<B, Ctx>, env: &mut PmgAstar<B, Ctx>,
evaluate_navmesh: F, mut evaluate_navmesh: F,
) -> Vec<NavmeshIndex<B::PrimalNodeIndex>> { ) -> Vec<NavmeshIndex<B::PrimalNodeIndex>> {
let goal_idx = self.goal_idx; let goal_idx = self.goal_idx;
let navmesh = NavmeshRef { let navmesh = NavmeshRef {
@ -345,11 +370,11 @@ where
edges: &env.edges, edges: &env.edges,
edge_paths: &mut edge_paths, edge_paths: &mut edge_paths,
}; };
let eidx = EdgeIndex::from((self.selected_node.clone(), neigh.clone())); let (edge_meta, epi) = navmesh
let cur_intro = navmesh .resolve_edge_data(self.selected_node.clone(), neigh.clone())
.edge_data_mut(self.selected_node.clone(), neigh.clone()) .unwrap();
.unwrap() let edge_meta = edge_meta.to_owned();
.with_borrow_mut(|mut x| { let cur_intro = navmesh.access_edge_paths_mut(epi).with_borrow_mut(|mut x| {
x.insert( x.insert(
stop_data.insert_pos, stop_data.insert_pos,
RelaxedPath::Normal(goal.label.clone()), RelaxedPath::Normal(goal.label.clone()),
@ -357,8 +382,19 @@ where
x.len() - stop_data.insert_pos - 1 x.len() - stop_data.insert_pos - 1
}); });
ret.push(neigh.clone()); ret.push(neigh.clone());
evaluate_navmesh(navmesh.as_ref(), &self.context, eidx).map(|(costs, context)| { evaluate_navmesh(
Task { navmesh.as_ref(),
&self.context,
InsertionInfo {
prev_node: self.selected_node.clone(),
cur_node: neigh.clone(),
edge_meta,
epi,
intro: stop_data.insert_pos,
maybe_new_goal: None,
},
)
.map(|(costs, context)| Task {
goal_idx, goal_idx,
costs, costs,
estimated_remaining, estimated_remaining,
@ -368,7 +404,6 @@ where
prev_node: self.selected_node.clone(), prev_node: self.selected_node.clone(),
cur_intro, cur_intro,
context, context,
}
}) })
})); }));
@ -405,7 +440,7 @@ where
navmesh: &Navmesh<B>, navmesh: &Navmesh<B>,
goals: Vec<Goal<B::PrimalNodeIndex, B::EtchedPath>>, goals: Vec<Goal<B::PrimalNodeIndex, B::EtchedPath>>,
context: &Ctx, context: &Ctx,
evaluate_navmesh: F, mut evaluate_navmesh: F,
) -> Self { ) -> Self {
let mut this = Self { let mut this = Self {
queue: BinaryHeap::new(), queue: BinaryHeap::new(),
@ -428,14 +463,7 @@ where
edges: &this.edges, edges: &this.edges,
edge_paths: &navmesh.edge_paths, edge_paths: &navmesh.edge_paths,
}; };
let tmp = if let Some(iter) = first_goal.start_pmga(navmesh, 0, &this, context, &mut evaluate_navmesh)
first_goal.start_pmga(navmesh, 0, &this, context, &evaluate_navmesh)
{
iter.collect()
} else {
BinaryHeap::new()
};
tmp
}; };
} }
@ -449,7 +477,7 @@ where
/// run one step of the path-search /// run one step of the path-search
pub fn step<F: EvaluateNavmesh<B, Ctx>>( pub fn step<F: EvaluateNavmesh<B, Ctx>>(
&mut self, &mut self,
evaluate_navmesh: F, mut evaluate_navmesh: F,
) -> ControlFlow< ) -> ControlFlow<
Option<( Option<(
B::Scalar, B::Scalar,
@ -465,7 +493,7 @@ where
log::info!("found no complete result"); log::info!("found no complete result");
return ControlFlow::Break(None); return ControlFlow::Break(None);
}; };
ControlFlow::Continue(match task.run(self, &evaluate_navmesh) { ControlFlow::Continue(match task.run(self, &mut evaluate_navmesh) {
ControlFlow::Break(taskres) => { ControlFlow::Break(taskres) => {
let next_goal_idx = taskres.goal_idx + 1; let next_goal_idx = taskres.goal_idx + 1;
let navmesh = NavmeshRef { let navmesh = NavmeshRef {
@ -496,17 +524,13 @@ where
edge_count, edge_count,
taskres.costs, taskres.costs,
); );
let mut tmp = if let Some(iter) = next_goal.start_pmga( let mut tmp = next_goal.start_pmga(
navmesh, navmesh,
next_goal_idx, next_goal_idx,
self, self,
&taskres.context, &taskres.context,
&evaluate_navmesh, &mut evaluate_navmesh,
) { );
iter.collect()
} else {
BinaryHeap::new()
};
let forks = tmp.iter().map(|i| i.selected_node.clone()).collect(); let forks = tmp.iter().map(|i| i.selected_node.clone()).collect();
self.queue.append(&mut tmp); self.queue.append(&mut tmp);
IntermedResult { IntermedResult {

View File

@ -48,6 +48,16 @@ impl<PNI> Edge<PNI> {
} }
} }
impl<PNI: Clone> Edge<&PNI> {
#[inline]
pub fn to_owned(&self) -> Edge<PNI> {
Edge {
lhs: self.lhs.cloned(),
rhs: self.rhs.cloned(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr( #[cfg_attr(
any(test, feature = "serde"), any(test, feature = "serde"),