1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
use std::num::Zero; use nalgebra::na::Translation; use nalgebra::na; use geom::Ball; use narrow::CollisionDetector; use contact::Contact; use ray::{Ray, ball_toi_with_ray}; use math::{N, V, M}; /// Collision detector between two balls. #[deriving(Encodable, Decodable)] pub struct BallBall { priv prediction: N, priv contact: Option<Contact> } impl Clone for BallBall { fn clone(&self) -> BallBall { BallBall { prediction: self.prediction.clone(), contact: self.contact.clone() } } } impl BallBall { /// Creates a new persistent collision detector between two balls. #[inline] pub fn new(prediction: N) -> BallBall { BallBall { prediction: prediction, contact: None } } } impl CollisionDetector<Ball, Ball> for BallBall { fn update(&mut self, ma: &M, a: &Ball, mb: &M, b: &Ball) { self.contact = collide( &ma.translation(), a, &mb.translation(), b, &self.prediction); } #[inline] fn num_colls(&self) -> uint { match self.contact { None => 0, Some(_) => 1 } } #[inline] fn colls(&self, out_colls: &mut ~[Contact]) { match self.contact { Some(ref c) => out_colls.push(c.clone()), None => () } } #[inline] fn toi(_: Option<BallBall>, c1: &M, dir: &V, _: &N, b1: &Ball, c2: &M, b2: &Ball) -> Option<N> { toi(c1, dir, b1, c2, b2) } } /// Computes the contact point between two balls. /// /// The balls must penetrate to have contact points. #[inline] pub fn collide(center1: &V, b1: &Ball, center2: &V, b2: &Ball, prediction: &N) -> Option<Contact> { let r1 = b1.radius(); let r2 = b2.radius(); let delta_pos = center2 - *center1; let sqdist = na::sqnorm(&delta_pos); let sum_radius = r1 + r2; let sum_radius_with_error = sum_radius + *prediction; if sqdist < sum_radius_with_error * sum_radius_with_error { let mut normal = na::normalize(&delta_pos); if sqdist.is_zero() { na::canonical_basis(|b| { normal = b; false }) } Some(Contact::new( center1 + normal * r1, center2 - normal * r2, normal, (sum_radius - sqdist.sqrt()))) } else { None } } /// Computes the closest points between two balls. /// /// If they are intersecting, the points corresponding to the penetration depth are returned. #[inline] pub fn closest_points(center1: &V, b1: &Ball, center2: &V, b2: &Ball) -> (V, V) { let r1 = b1.radius(); let r2 = b2.radius(); let normal = na::normalize(&(center2 - *center1)); (center1 + normal * r1, center2 - normal * r2) } /// Computes the Time Of Impact of two balls. /// /// Arguments: /// * `m1` - the first ball transform. /// * `dir` - the direction of the first geometry movement. /// * `b1` - the first ball. /// * `m2` - the second ball transform. /// * `b2` - the second ball. #[inline] pub fn toi(c1: &M, dir: &V, b1: &Ball, c2: &M, b2: &Ball) -> Option<N> { // Here again, we cast a ray on the CSO exept we know that our CSO is just another bigger ball! let radius = b1.radius() + b2.radius(); let center = c1.translation() - c2.translation(); ball_toi_with_ray(center, radius, &Ray::new(na::zero(), -dir)) }