veloren_common/util/
find_dist.rs1use vek::*;
3
4pub trait FindDist<T> {
5 fn approx_in_range(self, other: T, range: f32) -> bool;
11 fn min_distance(self, other: T) -> f32;
13}
14
15#[derive(Clone, Copy, Debug)]
17pub struct Cylinder {
18 pub center: Vec3<f32>,
20 pub radius: f32,
22 pub height: f32,
24}
25
26impl Cylinder {
27 fn aabb(&self) -> Aabb<f32> {
28 Aabb {
29 min: self.center - Vec3::new(self.radius, self.radius, self.height / 2.0),
30 max: self.center + Vec3::new(self.radius, self.radius, self.height / 2.0),
31 }
32 }
33
34 #[inline]
35 pub fn from_components(
36 pos: Vec3<f32>,
37 scale: Option<crate::comp::Scale>,
38 collider: Option<&crate::comp::Collider>,
39 char_state: Option<&crate::comp::CharacterState>,
40 ) -> Self {
41 let scale = scale.map_or(1.0, |s| s.0);
42 let radius = collider.as_ref().map_or(0.5, |c| c.bounding_radius()) * scale;
43 let z_limit_modifier = char_state
44 .filter(|char_state| char_state.is_dodge())
45 .map_or(1.0, |_| 0.5)
46 * scale;
47 let (z_bottom, z_top) = collider
48 .map(|c| c.get_z_limits(z_limit_modifier))
49 .unwrap_or((-0.5 * z_limit_modifier, 0.5 * z_limit_modifier));
50
51 Self {
52 center: pos + Vec3::unit_z() * (z_top + z_bottom) / 2.0,
53 radius,
54 height: z_top - z_bottom,
55 }
56 }
57}
58
59#[derive(Clone, Copy, Debug)]
61pub struct Cube {
62 pub min: Vec3<f32>,
64 pub side_length: f32,
66}
67
68impl FindDist<Cylinder> for Cube {
69 #[inline]
70 fn approx_in_range(self, other: Cylinder, range: f32) -> bool {
71 let cube_plus_range_aabb = Aabb {
72 min: self.min - range,
73 max: self.min + self.side_length + range,
74 };
75 let cylinder_aabb = other.aabb();
76
77 cube_plus_range_aabb.collides_with_aabb(cylinder_aabb)
78 }
79
80 #[inline]
81 fn min_distance(self, other: Cylinder) -> f32 {
82 let z_center_dist = (self.min.z + self.side_length / 2.0 - other.center.z).abs();
84 let z_dist = (z_center_dist - (self.side_length + other.height) / 2.0).max(0.0);
86 let square_aabr = Aabr {
88 min: self.min.xy(),
89 max: self.min.xy() + self.side_length,
90 };
91 let xy_dist = (square_aabr.distance_to_point(other.center.xy()) - other.radius).max(0.0);
92 (z_dist.powi(2) + xy_dist.powi(2)).sqrt()
94 }
95}
96
97impl FindDist<Cube> for Cylinder {
98 #[inline]
99 fn approx_in_range(self, other: Cube, range: f32) -> bool { other.approx_in_range(self, range) }
100
101 #[inline]
102 fn min_distance(self, other: Cube) -> f32 { other.min_distance(self) }
103}
104
105impl FindDist<Cylinder> for Cylinder {
106 #[inline]
107 fn approx_in_range(self, other: Cylinder, range: f32) -> bool {
108 let mut aabb = self.aabb();
109 aabb.min -= range;
110 aabb.max += range;
111
112 aabb.collides_with_aabb(other.aabb())
113 }
114
115 #[inline]
116 fn min_distance(self, other: Cylinder) -> f32 {
117 let z_center_dist = (self.center.z - other.center.z).abs();
119 let z_dist = (z_center_dist - (self.height + other.height) / 2.0).max(0.0);
121 let xy_dist =
123 (self.center.xy().distance(other.center.xy()) - self.radius - other.radius).max(0.0);
124 (z_dist.powi(2) + xy_dist.powi(2)).sqrt()
126 }
127}
128
129impl FindDist<Vec3<f32>> for Cylinder {
130 #[inline]
131 fn approx_in_range(self, other: Vec3<f32>, range: f32) -> bool {
132 let mut aabb = self.aabb();
133 aabb.min -= range;
134 aabb.max += range;
135
136 aabb.contains_point(other)
137 }
138
139 #[inline]
140 fn min_distance(self, other: Vec3<f32>) -> f32 {
141 let z_center_dist = (self.center.z - other.z).abs();
143 let z_dist = (z_center_dist - self.height / 2.0).max(0.0);
145 let xy_dist = (self.center.xy().distance(other.xy()) - self.radius).max(0.0);
147 (z_dist.powi(2) + xy_dist.powi(2)).sqrt()
149 }
150}
151
152impl FindDist<Cylinder> for Vec3<f32> {
153 #[inline]
154 fn approx_in_range(self, other: Cylinder, range: f32) -> bool {
155 other.approx_in_range(self, range)
156 }
157
158 #[inline]
159 fn min_distance(self, other: Cylinder) -> f32 { other.min_distance(self) }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn cylinder_vs_cube() {
168 let offset = Vec3::zero();
170 let cylinder = Cylinder {
171 center: Vec3::new(0.0, 0.0, 0.0) + offset,
172 radius: 2.0,
173 height: 4.0,
174 };
175
176 let cube = Cube {
177 min: Vec3::new(-0.5, -0.5, -0.5) + offset,
178 side_length: 1.0,
179 };
180
181 assert!(cube.approx_in_range(cylinder, 0.0));
182 assert!(cube.min_distance(cylinder).abs() < f32::EPSILON);
183 assert!((cube.min_distance(cylinder) - cylinder.min_distance(cube)).abs() < 0.001);
184
185 let cube = Cube {
186 min: cube.min + Vec3::unit_x() * 50.0,
187 side_length: 1.0,
188 };
189
190 assert!(!cube.approx_in_range(cylinder, 5.0)); assert!(cube.approx_in_range(cylinder, 47.51));
192 assert!((cube.min_distance(cylinder) - 47.5).abs() < 0.001);
193 assert!((cube.min_distance(cylinder) - cylinder.min_distance(cube)).abs() < 0.001);
194 }
195
196 #[test]
197 fn zero_size_cylinder() {
198 let cylinder = Cylinder {
199 center: Vec3::new(1.0, 2.0, 3.0),
200 radius: 0.0,
201 height: 0.0,
202 };
203
204 let point = Vec3::new(1.0, 2.5, 3.5);
205
206 assert!(cylinder.approx_in_range(point, 0.71));
207 assert!(cylinder.min_distance(point) < 0.71);
208 assert!(cylinder.min_distance(point) > 0.70);
209
210 let cube = Cube {
211 min: Vec3::new(0.5, 1.9, 2.1),
212 side_length: 1.0,
213 };
214
215 assert!(cylinder.approx_in_range(cube, 0.0));
216 assert!(cylinder.min_distance(cube) < f32::EPSILON);
217
218 let cube = Cube {
219 min: Vec3::new(1.0, 2.0, 4.5),
220 side_length: 1.0,
221 };
222
223 assert!(cylinder.approx_in_range(cube, 1.51));
224 assert!(cylinder.approx_in_range(cube, 100.51));
225 assert!(cylinder.min_distance(cube) < 1.501);
226 assert!(cylinder.min_distance(cube) > 1.499);
227 }
228}