ncollide3d/query/visitors/
ray_intersection_cost_fn_visitor.rs

1use crate::bounding_volume::BoundingVolume;
2use crate::math::Isometry;
3use crate::partitioning::{BestFirstVisitStatus, BestFirstVisitor};
4use crate::query::{PointQuery, Ray, RayCast, RayIntersection};
5use na::RealField;
6use std::any::Any;
7
8use crate::pipeline::{BroadPhase, BroadPhaseProxyHandle};
9
10/// Bounding Volume Tree visitor collecting interferences with a given ray.
11pub struct RayIntersectionCostFnVisitor<'a, 'b, N: 'a + RealField + Copy, T, BV>
12where
13    BV: BoundingVolume<N> + RayCast<N> + PointQuery<N> + Any + Send + Sync + Clone,
14    T: Any + Send + Sync,
15{
16    /// Ray to be tested.
17    ray: &'b Ray<N>,
18
19    /// Maximum time-of-impact of the ray with the objects.
20    max_toi: N,
21
22    /// Used as a lookup to get the underlying data of the tree (uses `.query()`)
23    /// This is required as the broad phase stores the data in a separate
24    /// structure to the tree.
25    /// TODO: Can this be made more generic?
26    broad_phase: &'a dyn BroadPhase<N, BV, T>,
27
28    /// The cost function to apply to each leaf nodes data.
29    cost_fn: &'a dyn Fn(T, &'b Ray<N>, N) -> Option<(T, RayIntersection<N>)>,
30}
31
32impl<'a, 'b, N: RealField + Copy, T, BV> RayIntersectionCostFnVisitor<'a, 'b, N, T, BV>
33where
34    BV: BoundingVolume<N> + RayCast<N> + PointQuery<N> + Any + Send + Sync + Clone,
35    T: Any + Send + Sync,
36{
37    /// Creates a new `RayIntersectionCostFnVisitor`.
38    #[inline]
39    pub fn new(
40        ray: &'b Ray<N>,
41        max_toi: N,
42        broad_phase: &'a dyn BroadPhase<N, BV, T>,
43        cost_fn: &'a dyn Fn(T, &'b Ray<N>, N) -> Option<(T, RayIntersection<N>)>,
44    ) -> RayIntersectionCostFnVisitor<'a, 'b, N, T, BV> {
45        RayIntersectionCostFnVisitor {
46            ray,
47            max_toi,
48            broad_phase,
49            cost_fn,
50        }
51    }
52}
53
54impl<'a, 'b, N, BV, T> BestFirstVisitor<N, BroadPhaseProxyHandle, BV>
55    for RayIntersectionCostFnVisitor<'a, 'b, N, T, BV>
56where
57    N: RealField + Copy,
58    BV: BoundingVolume<N> + RayCast<N> + PointQuery<N> + Any + Send + Sync + Clone,
59    T: Any + Send + Sync + Clone,
60{
61    type Result = (T, RayIntersection<N>);
62
63    #[inline]
64    fn visit(
65        &mut self,
66        best_cost_so_far: N,
67        bv: &BV,
68        data: Option<&BroadPhaseProxyHandle>,
69    ) -> BestFirstVisitStatus<N, Self::Result> {
70        if let Some(rough_toi) =
71            bv.toi_with_ray(&Isometry::identity(), self.ray, self.max_toi, true)
72        {
73            let mut res = BestFirstVisitStatus::Continue {
74                cost: rough_toi,
75                result: None,
76            };
77
78            // If the node has data then it is a leaf
79            if let Some(data_handle) = data {
80                // rough_toi is less than or equal the cost of any subnode.
81                // Either: The ray origin is outside the bv, and so no point in the bv
82                //   could have a lower cost than rough_toi.
83                // Or: The ray origin is inside the bv, and rough_toi is 0
84                // We only check the data if it may be better than best_cost_so_far
85                if rough_toi < best_cost_so_far {
86                    // Possibly the best. Look up underlying data of the node...
87                    // TODO: Should this be `.expect()`?
88                    if let Some((_, leaf_data)) = self.broad_phase.proxy(*data_handle) {
89                        // and then run the cost function with the nodes data
90                        if let Some(result) =
91                            (self.cost_fn)(leaf_data.clone(), self.ray, self.max_toi)
92                        {
93                            res = BestFirstVisitStatus::Continue {
94                                cost: result.1.toi,
95                                result: Some(result),
96                            };
97                        }
98                    }
99                };
100            }
101
102            res
103        } else {
104            // No intersection so we can ignore all children
105            BestFirstVisitStatus::Stop
106        }
107    }
108}