collider/core/dur_hitbox/
mod.rs1mod solvers;
16
17use std::f64;
18use geom::*;
19use geom::shape::PlacedBounds;
20use core;
21
22#[derive(Clone)]
28pub struct DurHbVel {
29 pub value: Vec2,
30 pub resize: Vec2,
31 pub duration: f64,
32}
33
34impl DurHbVel {
35 pub fn still() -> DurHbVel {
36 DurHbVel {
37 value: Vec2::zero(),
38 resize: Vec2::zero(),
39 duration: f64::INFINITY,
40 }
41 }
42
43 fn is_still(&self) -> bool {
44 self.value == Vec2::zero() && self.resize == Vec2::zero()
45 }
46
47 fn negate(&self) -> DurHbVel {
48 DurHbVel {
49 value: -self.value,
50 resize: -self.resize,
51 duration: self.duration,
52 }
53 }
54}
55
56impl PlacedBounds for DurHbVel {
57 fn bounds_center(&self) -> &Vec2 { &self.value }
58 fn bounds_dims(&self) -> &Vec2 { &self.resize }
59}
60
61#[derive(Clone)]
62pub struct DurHitbox {
63 pub value: PlacedShape,
64 pub vel: DurHbVel,
65}
66
67impl DurHitbox {
68 pub fn new(value: PlacedShape) -> DurHitbox {
69 DurHitbox { value, vel: DurHbVel::still() }
70 }
71
72 pub fn advanced_shape(&self, time: f64) -> PlacedShape {
73 assert!(time < core::HIGH_TIME, "requires time < {}", core::HIGH_TIME);
74 self.value.advance(self.vel.value, self.vel.resize, time)
75 }
76
77 pub fn bounding_box(&self) -> PlacedShape {
78 self.bounding_box_for(self.vel.duration)
79 }
80
81 pub fn bounding_box_for(&self, duration: f64) -> PlacedShape {
82 if self.vel.is_still() {
83 self.value.as_rect()
84 } else {
85 let end_value = self.advanced_shape(duration);
86 self.value.bounding_box(&end_value)
87 }
88 }
89
90 pub fn collide_time(&self, other: &DurHitbox) -> f64 {
91 solvers::collide_time(self, other)
92 }
93
94 pub fn separate_time(&self, other: &DurHitbox, padding: f64) -> f64 {
95 solvers::separate_time(self, other, padding)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use geom::*;
102 use core::dur_hitbox::DurHitbox;
103 use std::f64;
104
105 #[test]
106 fn test_rect_rect_collision() {
107 let mut a = DurHitbox::new(PlacedShape::new(v2(-11.0, 0.0), Shape::rect(v2(2.0, 2.0))));
108 a.vel.value = v2(2.0, 0.0);
109 a.vel.duration = 100.0;
110 let mut b = DurHitbox::new(PlacedShape::new(v2(12.0, 2.0), Shape::rect(v2(2.0, 4.0))));
111 b.vel.value = v2(-0.5, 0.0);
112 b.vel.resize = v2(1.0, 0.0);
113 b.vel.duration = 100.0;
114 assert_eq!(a.collide_time(&b), 7.0);
115 assert_eq!(b.collide_time(&a), 7.0);
116 assert_eq!(a.separate_time(&b, 0.1), 0.0);
117 }
118
119 #[test]
120 fn test_circle_circle_collision() {
121 let sqrt2 = (2.0f64).sqrt();
122 let mut a = DurHitbox::new(PlacedShape::new(v2(-0.1*sqrt2, 0.0), Shape::circle(2.0)));
123 a.vel.value = v2(0.1, 0.0);
124 a.vel.duration = 100.0;
125 let mut b = DurHitbox::new(PlacedShape::new(v2(3.0*sqrt2, 0.0), Shape::circle(2.0 + sqrt2*0.1)));
126 b.vel.value = v2(-2.0, 1.0);
127 b.vel.resize = v2(-0.1, -0.1);
128 b.vel.duration = 100.0;
129 assert!((a.collide_time(&b) - sqrt2).abs() < 1e-7);
130 assert_eq!(a.separate_time(&b, 0.1), 0.0);
131 }
132
133 #[test]
134 fn test_rect_circle_collision() {
135 let mut a = DurHitbox::new(PlacedShape::new(v2(-11.0, 0.0), Shape::circle(2.0)));
136 a.vel.value = v2(2.0, 0.0);
137 a.vel.duration = 100.0;
138 let mut b = DurHitbox::new(PlacedShape::new(v2(12.0, 2.0), Shape::rect(v2(2.0, 4.0))));
139 b.vel.value = v2(-1.0, 0.0);
140 b.vel.duration = 100.0;
141 assert_eq!(a.collide_time(&b), 7.0);
142 assert_eq!(b.collide_time(&a), 7.0);
143 assert_eq!(a.separate_time(&b, 0.1), 0.0);
144 }
145
146 #[test]
147 fn test_rect_circle_angled_collision() {
148 let mut a = DurHitbox::new(PlacedShape::new(v2(0., 0.), Shape::square(2.)));
149 a.vel.duration = 100.0;
150 let mut b = DurHitbox::new(PlacedShape::new(v2(5., 5.), Shape::circle(2.)));
151 b.vel.value = v2(-1., -1.);
152 b.vel.duration = 100.0;
153 let collide_time = a.collide_time(&b);
154 let expected_time = 4. - 1. / 2f64.sqrt();
155 assert_eq!(collide_time, expected_time);
156 }
157
158 #[test]
159 fn test_rect_rect_separation() {
160 let mut a = DurHitbox::new(PlacedShape::new(v2(0.0, 0.0), Shape::rect(v2(6.0, 4.0))));
161 a.vel.value = v2(1.0, 1.0);
162 a.vel.duration = 100.0;
163 let mut b = DurHitbox::new(PlacedShape::new(v2(1.0, 0.0), Shape::rect(v2(4.0, 4.0))));
164 b.vel.value = v2(0.5, 0.0);
165 b.vel.duration = 100.0;
166 assert_eq!(a.separate_time(&b, 0.1), 4.1);
167 assert_eq!(b.separate_time(&a, 0.1), 4.1);
168 assert_eq!(a.collide_time(&b), 0.0);
169 }
170
171 #[test]
172 fn test_circle_circle_separation() {
173 let sqrt2 = (2.0f64).sqrt();
174 let mut a = DurHitbox::new(PlacedShape::new(v2(2.0, 5.0), Shape::circle(2.0)));
175 a.vel.duration = 100.0;
176 let mut b = DurHitbox::new(PlacedShape::new(v2(3.0, 4.0), Shape::circle(1.8)));
177 b.vel.value = v2(-1.0, 1.0);
178 b.vel.duration = 100.0;
179 assert_eq!(a.separate_time(&b, 0.1), 1.0 + sqrt2);
180 assert_eq!(b.separate_time(&a, 0.1), 1.0 + sqrt2);
181 assert_eq!(a.collide_time(&b), 0.0);
182 }
183
184 #[test]
185 fn test_rect_circle_separation() {
186 let sqrt2 = (2.0f64).sqrt();
187 let mut a = DurHitbox::new(PlacedShape::new(v2(4.0, 2.0), Shape::rect(v2(4.0, 6.0))));
188 a.vel.duration = 100.0;
189 let mut b = DurHitbox::new(PlacedShape::new(v2(3.0, 4.0), Shape::circle(3.8)));
190 b.vel.value = v2(-1.0, 1.0);
191 b.vel.duration = 100.0;
192 assert_eq!(a.separate_time(&b, 0.1), 1.0 + sqrt2);
193 assert_eq!(b.separate_time(&a, 0.1), 1.0 + sqrt2);
194 assert_eq!(a.collide_time(&b), 0.0);
195 }
196
197 #[test]
198 fn test_rect_circle_angled_separation() {
199 let mut a = DurHitbox::new(PlacedShape::new(v2(0., 0.), Shape::square(2.)));
200 a.vel.duration = 100.0;
201 let mut b = DurHitbox::new(PlacedShape::new(v2(-1., 1.), Shape::circle(2.)));
202 b.vel.value = v2(1., -1.);
203 b.vel.duration = 100.0;
204 let separate_time = a.separate_time(&b, 0.1);
205 let expected_time = 2. + 1.1 / 2f64.sqrt();
206 assert_eq!(separate_time, expected_time);
207 }
208
209 #[test]
210 fn test_no_collision() {
211 let mut a = DurHitbox::new(PlacedShape::new(v2(-11.0, 0.0), Shape::rect(v2(2.0, 2.0))));
212 a.vel.value = v2(2.0, 0.0);
213 a.vel.duration = 100.0;
214 let mut b = DurHitbox::new(PlacedShape::new(v2(12.0, 2.0), Shape::rect(v2(2.0, 4.0))));
215 b.vel.value = v2(-1.0, 1.0);
216 b.vel.duration = 100.0;
217 assert_eq!(a.collide_time(&b), f64::INFINITY);
218 assert_eq!(a.separate_time(&b, 0.1), 0.0);
219
220 b.value.shape == Shape::circle(2.0);
221 b.vel.resize == Vec2::zero();
222 assert_eq!(a.collide_time(&b), f64::INFINITY);
223 assert_eq!(a.separate_time(&b, 0.1), 0.0);
224
225 a.value.shape == Shape::circle(2.0);
226 a.vel.resize == Vec2::zero();
227 assert_eq!(a.collide_time(&b), f64::INFINITY);
228 assert_eq!(a.separate_time(&b, 0.1), 0.0);
229 }
230
231 #[test]
232 fn test_no_separation() {
233 let mut a = DurHitbox::new(PlacedShape::new(v2(5.0, 1.0), Shape::rect(v2(2.0, 2.0))));
234 a.vel.value = v2(2.0, 1.0);
235 a.vel.duration = 100.0;
236 let mut b = DurHitbox::new(PlacedShape::new(v2(5.0, 1.0), Shape::rect(v2(2.0, 4.0))));
237 b.vel.value = v2(2.0, 1.0);
238 b.vel.duration = 100.0;
239 assert_eq!(a.separate_time(&b, 0.1), f64::INFINITY);
240 assert_eq!(a.collide_time(&b), 0.0);
241
242 b.value.shape == Shape::circle(2.0);
243 b.vel.resize == Vec2::zero();
244 assert_eq!(a.separate_time(&b, 0.1), f64::INFINITY);
245 assert_eq!(a.collide_time(&b), 0.0);
246
247 a.value.shape == Shape::circle(2.0);
248 a.vel.resize == Vec2::zero();
249 assert_eq!(a.separate_time(&b, 0.1), f64::INFINITY);
250 assert_eq!(a.collide_time(&b), 0.0);
251 }
252
253 #[test]
254 fn test_low_duration() {
255 let sqrt2 = (2.0f64).sqrt();
256 let mut a = DurHitbox::new(PlacedShape::new(v2(0.0, 0.0), Shape::circle(2.0)));
257 a.vel.duration = 4.0 - sqrt2 + 0.01;
258 let mut b = DurHitbox::new(PlacedShape::new(v2(4.0, 4.0), Shape::circle(2.0)));
259 b.vel.value = v2(-1.0, -1.0);
260 b.vel.duration = 4.0 - sqrt2 + 0.01;
261 assert_eq!(a.collide_time(&b), 4.0 - sqrt2);
262 a.vel.duration -= 0.02;
263 assert_eq!(a.collide_time(&b), f64::INFINITY);
264 b.vel.duration -= 0.02;
265 assert_eq!(a.collide_time(&b), f64::INFINITY);
266 }
267}