1use na::{self, ComplexField, Unit};
4
5use crate::query::gjk::{CSOPoint, ConstantOrigin, VoronoiSimplex};
6use crate::shape::SupportMap;
7use crate::math::{Isometry, Point, Real, Vector, DIM};
9use crate::query::{self, Ray};
10
11use num::{Bounded, Zero};
12
13#[derive(Clone, Debug, PartialEq)]
15pub enum GJKResult {
16 Intersection,
18 ClosestPoints(Point<Real>, Point<Real>, Unit<Vector<Real>>),
23 Proximity(Unit<Vector<Real>>),
28 NoIntersection(Unit<Vector<Real>>),
33}
34
35pub fn eps_tol() -> Real {
37 let _eps = crate::math::DEFAULT_EPSILON;
38 _eps * 10.0
39}
40
41pub fn project_origin<G: ?Sized + SupportMap>(
50 m: &Isometry<Real>,
51 g: &G,
52 simplex: &mut VoronoiSimplex,
53) -> Option<Point<Real>> {
54 match closest_points(
55 &m.inverse(),
56 g,
57 &ConstantOrigin,
58 Real::max_value(),
59 true,
60 simplex,
61 ) {
62 GJKResult::Intersection => None,
63 GJKResult::ClosestPoints(p, _, _) => Some(p),
64 _ => unreachable!(),
65 }
66}
67
68pub fn closest_points<G1, G2>(
84 pos12: &Isometry<Real>,
85 g1: &G1,
86 g2: &G2,
87 max_dist: Real,
88 exact_dist: bool,
89 simplex: &mut VoronoiSimplex,
90) -> GJKResult
91where
92 G1: ?Sized + SupportMap,
93 G2: ?Sized + SupportMap,
94{
95 let _eps = crate::math::DEFAULT_EPSILON;
96 let _eps_tol: Real = eps_tol();
97 let _eps_rel: Real = ComplexField::sqrt(_eps_tol);
98
99 let mut proj = simplex.project_origin_and_reduce();
101
102 let mut old_dir;
103
104 if let Some(proj_dir) = Unit::try_new(proj.coords, 0.0) {
105 old_dir = -proj_dir;
106 } else {
107 return GJKResult::Intersection;
108 }
109
110 let mut max_bound = Real::max_value();
111 let mut dir;
112 let mut niter = 0;
113
114 loop {
115 let old_max_bound = max_bound;
116
117 if let Some((new_dir, dist)) = Unit::try_new_and_get(-proj.coords, _eps_tol) {
118 dir = new_dir;
119 max_bound = dist;
120 } else {
121 return GJKResult::Intersection;
123 }
124
125 if max_bound >= old_max_bound {
126 if exact_dist {
127 let (p1, p2) = result(simplex, true);
128 return GJKResult::ClosestPoints(p1, p2, old_dir); } else {
130 return GJKResult::Proximity(old_dir);
131 }
132 }
133
134 let cso_point = CSOPoint::from_shapes(pos12, g1, g2, &dir);
135 let min_bound = -dir.dot(&cso_point.point.coords);
136
137 assert!(min_bound.is_finite());
138
139 if min_bound > max_dist {
140 return GJKResult::NoIntersection(dir);
141 } else if !exact_dist && min_bound > 0.0 && max_bound <= max_dist {
142 return GJKResult::Proximity(old_dir);
143 } else if max_bound - min_bound <= _eps_rel * max_bound {
144 if exact_dist {
145 let (p1, p2) = result(simplex, false);
146 return GJKResult::ClosestPoints(p1, p2, dir); } else {
148 return GJKResult::Proximity(dir);
149 }
150 }
151
152 if !simplex.add_point(cso_point) {
153 if exact_dist {
154 let (p1, p2) = result(simplex, false);
155 return GJKResult::ClosestPoints(p1, p2, dir);
156 } else {
157 return GJKResult::Proximity(dir);
158 }
159 }
160
161 old_dir = dir;
162 proj = simplex.project_origin_and_reduce();
163
164 if simplex.dimension() == DIM {
165 if min_bound >= _eps_tol {
166 if exact_dist {
167 let (p1, p2) = result(simplex, true);
168 return GJKResult::ClosestPoints(p1, p2, old_dir);
169 } else {
170 return GJKResult::Proximity(old_dir);
172 }
173 } else {
174 return GJKResult::Intersection; }
176 }
177 niter += 1;
178
179 if niter == 100 {
180 return GJKResult::NoIntersection(Vector::x_axis());
181 }
182 }
183}
184
185pub fn cast_local_ray<G: ?Sized + SupportMap>(
187 shape: &G,
188 simplex: &mut VoronoiSimplex,
189 ray: &Ray,
190 max_time_of_impact: Real,
191) -> Option<(Real, Vector<Real>)> {
192 let g2 = ConstantOrigin;
193 minkowski_ray_cast(
194 &Isometry::identity(),
195 shape,
196 &g2,
197 ray,
198 max_time_of_impact,
199 simplex,
200 )
201}
202
203pub fn directional_distance<G1, G2>(
208 pos12: &Isometry<Real>,
209 g1: &G1,
210 g2: &G2,
211 dir: &Vector<Real>,
212 simplex: &mut VoronoiSimplex,
213) -> Option<(Real, Vector<Real>, Point<Real>, Point<Real>)>
214where
215 G1: ?Sized + SupportMap,
216 G2: ?Sized + SupportMap,
217{
218 let ray = Ray::new(Point::origin(), *dir);
219 minkowski_ray_cast(pos12, g1, g2, &ray, Real::max_value(), simplex).map(
220 |(time_of_impact, normal)| {
221 let witnesses = if !time_of_impact.is_zero() {
222 result(simplex, simplex.dimension() == DIM)
223 } else {
224 (Point::origin(), Point::origin())
227 };
228
229 (time_of_impact, normal, witnesses.0, witnesses.1)
230 },
231 )
232}
233
234fn minkowski_ray_cast<G1, G2>(
236 pos12: &Isometry<Real>,
237 g1: &G1,
238 g2: &G2,
239 ray: &Ray,
240 max_time_of_impact: Real,
241 simplex: &mut VoronoiSimplex,
242) -> Option<(Real, Vector<Real>)>
243where
244 G1: ?Sized + SupportMap,
245 G2: ?Sized + SupportMap,
246{
247 let _eps = crate::math::DEFAULT_EPSILON;
248 let _eps_tol: Real = eps_tol();
249 let _eps_rel: Real = ComplexField::sqrt(_eps_tol);
250
251 let ray_length = ray.dir.norm();
252
253 if relative_eq!(ray_length, 0.0) {
254 return None;
255 }
256
257 let mut ltoi = 0.0;
258 let mut curr_ray = Ray::new(ray.origin, ray.dir / ray_length);
259 let dir = -curr_ray.dir;
260 let mut ldir = dir;
261
262 let support_point = CSOPoint::from_shapes(pos12, g1, g2, &dir);
264 simplex.reset(support_point.translate(&-curr_ray.origin.coords));
265
266 let mut proj = simplex.project_origin_and_reduce();
268 let mut max_bound = Real::max_value();
269 let mut dir;
270 let mut niter = 0;
271 let mut last_chance = false;
272
273 loop {
274 let old_max_bound = max_bound;
275
276 if let Some((new_dir, dist)) = Unit::try_new_and_get(-proj.coords, _eps_tol) {
277 dir = new_dir;
278 max_bound = dist;
279 } else {
280 return Some((ltoi / ray_length, ldir));
281 }
282
283 let support_point = if max_bound >= old_max_bound {
284 last_chance = true;
286 CSOPoint::single_point(proj + curr_ray.origin.coords)
287 } else {
288 CSOPoint::from_shapes(pos12, g1, g2, &dir)
289 };
290
291 if last_chance && ltoi > 0.0 {
292 return Some((ltoi / ray_length, ldir));
294 }
295
296 match query::details::ray_toi_with_halfspace(&support_point.point, &dir, &curr_ray) {
305 Some(t) => {
306 if dir.dot(&curr_ray.dir) < 0.0 && t > 0.0 {
307 ldir = *dir;
309 ltoi += t;
310
311 if ltoi / ray_length > max_time_of_impact {
315 return None;
316 }
317
318 let shift = curr_ray.dir * t;
319 curr_ray.origin += shift;
320 max_bound = Real::max_value();
321 simplex.modify_pnts(&|pt| pt.translate_mut(&-shift));
322 last_chance = false;
323 }
324 }
325 None => {
326 if dir.dot(&curr_ray.dir) > _eps_tol {
327 return None;
329 }
330 }
331 }
332
333 if last_chance {
334 return None;
335 }
336
337 let min_bound = -dir.dot(&(support_point.point.coords - curr_ray.origin.coords));
338
339 assert!(min_bound.is_finite());
340
341 if max_bound - min_bound <= _eps_rel * max_bound {
342 if cfg!(feature = "improved_fixed_point_support") {
348 return Some((ltoi / ray_length, ldir));
349 } else {
350 return None;
351 }
352 }
353
354 let _ = simplex.add_point(support_point.translate(&-curr_ray.origin.coords));
355 proj = simplex.project_origin_and_reduce();
356
357 if simplex.dimension() == DIM {
358 if min_bound >= _eps_tol {
359 return None;
360 } else {
361 return Some((ltoi / ray_length, ldir)); }
363 }
364
365 niter += 1;
366 if niter == 100 {
367 return None;
368 }
369 }
370}
371
372fn result(simplex: &VoronoiSimplex, prev: bool) -> (Point<Real>, Point<Real>) {
373 let mut res = (Point::origin(), Point::origin());
374 if prev {
375 for i in 0..simplex.prev_dimension() + 1 {
376 let coord = simplex.prev_proj_coord(i);
377 let point = simplex.prev_point(i);
378 res.0 += point.orig1.coords * coord;
379 res.1 += point.orig2.coords * coord;
380 }
381
382 res
383 } else {
384 for i in 0..simplex.dimension() + 1 {
385 let coord = simplex.proj_coord(i);
386 let point = simplex.point(i);
387 res.0 += point.orig1.coords * coord;
388 res.1 += point.orig2.coords * coord;
389 }
390
391 res
392 }
393}