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
#![deny(missing_docs)]
use collide::{Collider, CollisionInfo};
use num_traits::{real::Real, Zero};
use vector_space::{InnerSpace, VectorSpace};
#[derive(Copy, Clone)]
pub struct Capsule<V: VectorSpace> {
pub start: V,
pub end: V,
pub rad: V::Scalar,
}
impl<V: InnerSpace> Capsule<V> {
pub fn new(rad: V::Scalar, start: V, end: V) -> Self {
Self { start, end, rad }
}
pub fn point(pos: V) -> Self {
Self {
start: pos,
end: pos,
rad: V::Scalar::zero(),
}
}
pub fn line(start: V, end: V) -> Self {
Self {
start,
end,
rad: V::Scalar::zero(),
}
}
pub fn sphere(pos: V, rad: V::Scalar) -> Self {
Self {
start: pos,
end: pos,
rad,
}
}
fn closest(&self, point: V) -> V {
if self.start == self.end {
self.start
} else {
let dis = self.end - self.start;
let mag = dis.magnitude();
let dir = dis / mag;
let dot = dir.dot(point - self.start);
self.start + dir * dot.max(V::Scalar::zero()).min(mag)
}
}
}
impl<V: InnerSpace> Collider for Capsule<V> {
type Vector = V;
fn collision_info(&self, other: &Self) -> Option<CollisionInfo<Self::Vector>> {
let start_to_other_start = (other.start - self.start).magnitude2();
let start_to_other_end = (other.end - self.start).magnitude2();
let end_to_other_start = (other.start - self.end).magnitude2();
let end_to_other_end = (other.end - self.end).magnitude2();
let point = if end_to_other_start < start_to_other_start
|| end_to_other_start < start_to_other_end
|| end_to_other_end < start_to_other_start
|| end_to_other_end < start_to_other_end
{
self.end
} else {
self.start
};
let other_point = other.closest(point);
let point = self.closest(other_point);
let dis = other_point - point;
let mag = dis.magnitude();
let rad = self.rad + other.rad;
if mag <= rad {
let dir = dis / mag;
Some(CollisionInfo {
self_contact: point + dir * self.rad,
other_contact: other_point - dir * other.rad,
vector: dir * (mag - rad),
})
} else {
None
}
}
}