1use super::is_valid_ray;
6use crate::collision::{Capsule, CastOutput, Circle, Polygon, RayCastInput, Segment};
7use crate::distance::{
8 make_proxy, shape_cast, shape_distance, DistanceInput, ShapeCastPairInput, SimplexCache,
9};
10use crate::math_functions::{
11 add, clamp_float, cross, distance_squared, dot, get_length_and_normalize, length_squared, lerp,
12 mul_add, mul_sub, mul_sv, neg, normalize, right_perp, sub, Vec2, TRANSFORM_IDENTITY,
13};
14
15pub fn point_in_circle(shape: &Circle, point: Vec2) -> bool {
17 let center = shape.center;
18 distance_squared(point, center) <= shape.radius * shape.radius
19}
20
21pub fn point_in_capsule(shape: &Capsule, point: Vec2) -> bool {
23 let rr = shape.radius * shape.radius;
24 let p1 = shape.center1;
25 let p2 = shape.center2;
26
27 let d = sub(p2, p1);
28 let dd = dot(d, d);
29 if dd == 0.0 {
30 return distance_squared(point, p1) <= rr;
32 }
33
34 let mut t = dot(sub(point, p1), d) / dd;
40 t = clamp_float(t, 0.0, 1.0);
41 let c = mul_add(p1, t, d);
42
43 distance_squared(point, c) <= rr
45}
46
47pub fn point_in_polygon(shape: &Polygon, point: Vec2) -> bool {
50 let input = DistanceInput {
51 proxy_a: make_proxy(&shape.vertices[..shape.count as usize], 0.0),
52 proxy_b: make_proxy(&[point], 0.0),
53 transform: TRANSFORM_IDENTITY,
54 use_radii: false,
55 };
56
57 let mut cache = SimplexCache::default();
58 let output = shape_distance(&input, &mut cache, None);
59
60 output.distance <= shape.radius
61}
62
63pub fn ray_cast_circle(shape: &Circle, input: &RayCastInput) -> CastOutput {
67 debug_assert!(is_valid_ray(input));
68
69 let p = shape.center;
70
71 let mut output = CastOutput::default();
72
73 let s = sub(input.origin, p);
75
76 let r = shape.radius;
77 let rr = r * r;
78
79 let mut length = 0.0;
80 let d = get_length_and_normalize(&mut length, input.translation);
81 if length == 0.0 {
82 if length_squared(s) < rr {
85 output.point = input.origin;
87 output.hit = true;
88 }
89
90 return output;
91 }
92
93 let t = -dot(s, d);
97
98 let c = mul_add(s, t, d);
100
101 let cc = dot(c, c);
102
103 if cc > rr {
104 return output;
106 }
107
108 let h = (rr - cc).sqrt();
110
111 let fraction = t - h;
112
113 if fraction < 0.0 || input.max_fraction * length < fraction {
114 if length_squared(s) < rr {
117 output.point = input.origin;
119 output.hit = true;
120 }
121
122 return output;
123 }
124
125 let hit_point = mul_add(s, fraction, d);
127
128 output.fraction = fraction / length;
129 output.normal = normalize(hit_point);
130 output.point = mul_add(p, shape.radius, output.normal);
131 output.hit = true;
132
133 output
134}
135
136pub fn ray_cast_capsule(shape: &Capsule, input: &RayCastInput) -> CastOutput {
138 debug_assert!(is_valid_ray(input));
139
140 let mut output = CastOutput::default();
141
142 let v1 = shape.center1;
143 let v2 = shape.center2;
144
145 let e = sub(v2, v1);
146
147 let mut capsule_length = 0.0;
148 let a = get_length_and_normalize(&mut capsule_length, e);
149
150 if capsule_length < f32::EPSILON {
151 let circle = Circle {
153 center: v1,
154 radius: shape.radius,
155 };
156 return ray_cast_circle(&circle, input);
157 }
158
159 let p1 = input.origin;
160 let d = input.translation;
161
162 let q = sub(p1, v1);
164 let qa = dot(q, a);
165
166 let qp = mul_add(q, -qa, a);
168
169 let radius = shape.radius;
170
171 if dot(qp, qp) < radius * radius {
173 if qa < 0.0 {
174 let circle = Circle {
176 center: v1,
177 radius: shape.radius,
178 };
179 return ray_cast_circle(&circle, input);
180 }
181
182 if qa > capsule_length {
183 let circle = Circle {
185 center: v2,
186 radius: shape.radius,
187 };
188 return ray_cast_circle(&circle, input);
189 }
190
191 output.point = input.origin;
193 output.hit = true;
194 return output;
195 }
196
197 let mut n = Vec2 { x: a.y, y: -a.x };
199
200 let mut ray_length = 0.0;
201 let u = get_length_and_normalize(&mut ray_length, d);
202
203 let den = -a.x * u.y + u.x * a.y;
214 if -f32::EPSILON < den && den < f32::EPSILON {
215 return output;
217 }
218
219 let b1 = mul_sub(q, radius, n);
220 let b2 = mul_add(q, radius, n);
221
222 let inv_den = 1.0 / den;
223
224 let s21 = (a.x * b1.y - b1.x * a.y) * inv_den;
226
227 let s22 = (a.x * b2.y - b2.x * a.y) * inv_den;
229
230 let s2;
231 let b;
232 if s21 < s22 {
233 s2 = s21;
234 b = b1;
235 } else {
236 s2 = s22;
237 b = b2;
238 n = neg(n);
239 }
240
241 if s2 < 0.0 || input.max_fraction * ray_length < s2 {
242 return output;
243 }
244
245 let s1 = (-b.x * u.y + u.x * b.y) * inv_den;
247
248 if s1 < 0.0 {
249 let circle = Circle {
251 center: v1,
252 radius: shape.radius,
253 };
254 ray_cast_circle(&circle, input)
255 } else if capsule_length < s1 {
256 let circle = Circle {
258 center: v2,
259 radius: shape.radius,
260 };
261 ray_cast_circle(&circle, input)
262 } else {
263 output.fraction = s2 / ray_length;
265 output.point = add(lerp(v1, v2, s1 / capsule_length), mul_sv(shape.radius, n));
266 output.normal = n;
267 output.hit = true;
268 output
269 }
270}
271
272pub fn ray_cast_segment(shape: &Segment, input: &RayCastInput, one_sided: bool) -> CastOutput {
277 if one_sided {
278 let offset = cross(
280 sub(input.origin, shape.point1),
281 sub(shape.point2, shape.point1),
282 );
283 if offset < 0.0 {
284 return CastOutput::default();
285 }
286 }
287
288 let p1 = input.origin;
290 let d = input.translation;
291
292 let v1 = shape.point1;
293 let v2 = shape.point2;
294 let e = sub(v2, v1);
295
296 let mut output = CastOutput::default();
297
298 let mut length = 0.0;
299 let e_unit = get_length_and_normalize(&mut length, e);
300 if length == 0.0 {
301 return output;
302 }
303
304 let mut normal = right_perp(e_unit);
306
307 let numerator = dot(normal, sub(v1, p1));
313 let denominator = dot(normal, d);
314
315 if denominator == 0.0 {
316 return output;
318 }
319
320 let t = numerator / denominator;
321 if t < 0.0 || input.max_fraction < t {
322 return output;
324 }
325
326 let p = mul_add(p1, t, d);
328
329 let s = dot(sub(p, v1), e_unit);
334 if s < 0.0 || length < s {
335 return output;
337 }
338
339 if numerator > 0.0 {
340 normal = neg(normal);
341 }
342
343 output.fraction = t;
344 output.point = p;
345 output.normal = normal;
346 output.hit = true;
347
348 output
349}
350
351pub fn ray_cast_polygon(shape: &Polygon, input: &RayCastInput) -> CastOutput {
353 debug_assert!(is_valid_ray(input));
354
355 if shape.radius == 0.0 {
356 let base = shape.vertices[0];
359
360 let p1 = sub(input.origin, base);
361 let d = input.translation;
362
363 let (mut lower, mut upper) = (0.0f32, input.max_fraction);
364
365 let mut index = -1i32;
366
367 let mut output = CastOutput::default();
368
369 for edge_index in 0..shape.count as usize {
370 let vertex = sub(shape.vertices[edge_index], base);
374 let numerator = dot(shape.normals[edge_index], sub(vertex, p1));
375 let denominator = dot(shape.normals[edge_index], d);
376
377 if denominator == 0.0 {
378 if numerator < 0.0 {
380 return output;
381 }
382 } else {
383 if denominator < 0.0 && numerator < lower * denominator {
388 lower = numerator / denominator;
391 index = edge_index as i32;
392 } else if denominator > 0.0 && numerator < upper * denominator {
393 upper = numerator / denominator;
396 }
397 }
398
399 if upper < lower {
400 return output;
402 }
403 }
404
405 debug_assert!(0.0 <= lower && lower <= input.max_fraction);
406
407 if index >= 0 {
408 output.fraction = lower;
409 output.normal = shape.normals[index as usize];
410 output.point = mul_add(input.origin, lower, d);
411 output.hit = true;
412 } else {
413 output.point = input.origin;
415 output.hit = true;
416 }
417
418 return output;
419 }
420
421 let cast_input = ShapeCastPairInput {
422 proxy_a: make_proxy(&shape.vertices[..shape.count as usize], shape.radius),
423 proxy_b: make_proxy(&[input.origin], 0.0),
424 transform: TRANSFORM_IDENTITY,
425 translation_b: input.translation,
426 max_fraction: input.max_fraction,
427 can_encroach: false,
428 };
429 shape_cast(&cast_input)
430}