use crate::core::math::Vector2;
use crate::ffi;
use crate::math::{Matrix, RayCollision};
use crate::models::Mesh;
#[inline]
#[must_use]
pub fn check_collision_circle_line(
center: impl Into<ffi::Vector2>,
radius: f32,
p1: impl Into<ffi::Vector2>,
p2: impl Into<ffi::Vector2>,
) -> bool {
unsafe { ffi::CheckCollisionCircleLine(center.into(), radius, p1.into(), p2.into()) }
}
#[inline]
#[must_use]
pub fn check_collision_circles(
center1: impl Into<ffi::Vector2>,
radius1: f32,
center2: impl Into<ffi::Vector2>,
radius2: f32,
) -> bool {
unsafe { ffi::CheckCollisionCircles(center1.into(), radius1, center2.into(), radius2) }
}
#[inline]
#[must_use]
pub fn check_collision_point_circle(
point: impl Into<ffi::Vector2>,
center: impl Into<ffi::Vector2>,
radius: f32,
) -> bool {
unsafe { ffi::CheckCollisionPointCircle(point.into(), center.into(), radius) }
}
#[inline]
#[must_use]
pub fn check_collision_point_poly(point: impl Into<ffi::Vector2>, points: &[Vector2]) -> bool {
unsafe { ffi::CheckCollisionPointPoly(point.into(), points.as_ptr(), points.len() as i32) }
}
#[inline]
#[must_use]
pub fn check_collision_point_line(
point: impl Into<ffi::Vector2>,
p1: impl Into<ffi::Vector2>,
p2: impl Into<ffi::Vector2>,
threshold: i32,
) -> bool {
unsafe { ffi::CheckCollisionPointLine(point.into(), p1.into(), p2.into(), threshold) }
}
#[inline]
#[must_use]
pub fn check_collision_point_triangle(
point: impl Into<ffi::Vector2>,
p1: impl Into<ffi::Vector2>,
p2: impl Into<ffi::Vector2>,
p3: impl Into<ffi::Vector2>,
) -> bool {
unsafe { ffi::CheckCollisionPointTriangle(point.into(), p1.into(), p2.into(), p3.into()) }
}
#[inline]
#[must_use]
pub fn check_collision_lines(
start_pos1: impl Into<ffi::Vector2>,
end_pos1: impl Into<ffi::Vector2>,
start_pos2: impl Into<ffi::Vector2>,
end_pos2: impl Into<ffi::Vector2>,
) -> Option<Vector2> {
let mut out = ffi::Vector2 { x: 0.0, y: 0.0 };
let collision = unsafe {
ffi::CheckCollisionLines(
start_pos1.into(),
end_pos1.into(),
start_pos2.into(),
end_pos2.into(),
&mut out,
)
};
if collision { Some(out) } else { None }
}
#[inline]
#[must_use]
pub fn check_collision_spheres(
center_a: impl Into<ffi::Vector3>,
radius_a: f32,
center_b: impl Into<ffi::Vector3>,
radius_b: f32,
) -> bool {
unsafe { ffi::CheckCollisionSpheres(center_a.into(), radius_a, center_b.into(), radius_b) }
}
#[inline]
#[must_use]
pub fn get_ray_collision_sphere(
ray: impl Into<ffi::Ray>,
sphere_position: impl Into<ffi::Vector3>,
sphere_radius: f32,
) -> RayCollision {
unsafe { ffi::GetRayCollisionSphere(ray.into(), sphere_position.into(), sphere_radius).into() }
}
#[inline]
#[must_use]
pub fn get_ray_collision_model(
ray: impl Into<ffi::Ray>,
model: &Mesh,
transform: &Matrix,
) -> RayCollision {
unsafe { ffi::GetRayCollisionMesh(ray.into(), model.0, *transform).into() }
}
#[inline]
#[must_use]
pub fn get_ray_collision_triangle(
ray: impl Into<ffi::Ray>,
p1: impl Into<ffi::Vector3>,
p2: impl Into<ffi::Vector3>,
p3: impl Into<ffi::Vector3>,
) -> RayCollision {
unsafe { ffi::GetRayCollisionTriangle(ray.into(), p1.into(), p2.into(), p3.into()).into() }
}
#[inline]
#[must_use]
pub fn get_ray_collision_quad(
ray: impl Into<ffi::Ray>,
p1: impl Into<ffi::Vector3>,
p2: impl Into<ffi::Vector3>,
p3: impl Into<ffi::Vector3>,
p4: impl Into<ffi::Vector3>,
) -> RayCollision {
unsafe {
ffi::GetRayCollisionQuad(ray.into(), p1.into(), p2.into(), p3.into(), p4.into()).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::math::{Vector2, Vector3};
#[test]
fn circles_overlap() {
assert!(check_collision_circles(
Vector2::new(0.0, 0.0),
2.0,
Vector2::new(1.0, 0.0),
2.0
));
}
#[test]
fn circles_separate() {
assert!(!check_collision_circles(
Vector2::new(0.0, 0.0),
1.0,
Vector2::new(10.0, 0.0),
1.0
));
}
#[test]
fn circles_touching_boundary() {
assert!(check_collision_circles(
Vector2::new(0.0, 0.0),
1.0,
Vector2::new(2.0, 0.0),
1.0
));
}
#[test]
fn circle_line_intersects() {
assert!(check_collision_circle_line(
Vector2::new(0.0, 0.5),
1.0,
Vector2::new(-5.0, 0.0),
Vector2::new(5.0, 0.0),
));
}
#[test]
fn circle_line_misses() {
assert!(!check_collision_circle_line(
Vector2::new(0.0, 5.0),
1.0,
Vector2::new(-5.0, 0.0),
Vector2::new(5.0, 0.0),
));
}
#[test]
fn point_inside_circle() {
assert!(check_collision_point_circle(
Vector2::new(0.5, 0.5),
Vector2::new(0.0, 0.0),
2.0,
));
}
#[test]
fn point_outside_circle() {
assert!(!check_collision_point_circle(
Vector2::new(10.0, 10.0),
Vector2::new(0.0, 0.0),
1.0,
));
}
#[test]
fn point_inside_triangle() {
assert!(check_collision_point_triangle(
Vector2::new(1.0, 1.0),
Vector2::new(0.0, 0.0),
Vector2::new(4.0, 0.0),
Vector2::new(0.0, 4.0),
));
}
#[test]
fn point_outside_triangle() {
assert!(!check_collision_point_triangle(
Vector2::new(5.0, 5.0),
Vector2::new(0.0, 0.0),
Vector2::new(4.0, 0.0),
Vector2::new(0.0, 4.0),
));
}
#[test]
fn point_inside_square_poly() {
let square = vec![
Vector2::new(0.0, 0.0),
Vector2::new(1.0, 0.0),
Vector2::new(1.0, 1.0),
Vector2::new(0.0, 1.0),
];
assert!(check_collision_point_poly(Vector2::new(0.5, 0.5), &square));
}
#[test]
fn point_outside_square_poly() {
let square = vec![
Vector2::new(0.0, 0.0),
Vector2::new(1.0, 0.0),
Vector2::new(1.0, 1.0),
Vector2::new(0.0, 1.0),
];
assert!(!check_collision_point_poly(Vector2::new(2.0, 2.0), &square));
}
#[test]
fn point_on_line_within_threshold() {
assert!(check_collision_point_line(
Vector2::new(1.0, 0.0),
Vector2::new(0.0, 0.0),
Vector2::new(10.0, 0.0),
1,
));
}
#[test]
fn point_far_from_line() {
assert!(!check_collision_point_line(
Vector2::new(5.0, 5.0),
Vector2::new(0.0, 0.0),
Vector2::new(10.0, 0.0),
1,
));
}
#[test]
fn lines_cross() {
let result = check_collision_lines(
Vector2::new(-1.0, -1.0),
Vector2::new(1.0, 1.0),
Vector2::new(-1.0, 1.0),
Vector2::new(1.0, -1.0),
);
assert!(result.is_some());
let pt = result.unwrap();
assert!(
(pt.x).abs() < 1e-4,
"intersection x should be ~0, got {}",
pt.x
);
assert!(
(pt.y).abs() < 1e-4,
"intersection y should be ~0, got {}",
pt.y
);
}
#[test]
fn lines_parallel_no_collision() {
let result = check_collision_lines(
Vector2::new(0.0, 0.0),
Vector2::new(10.0, 0.0),
Vector2::new(0.0, 1.0),
Vector2::new(10.0, 1.0),
);
assert!(result.is_none());
}
#[test]
fn spheres_overlap_3d() {
assert!(check_collision_spheres(
Vector3::new(0.0, 0.0, 0.0),
2.0,
Vector3::new(1.0, 0.0, 0.0),
2.0,
));
}
#[test]
fn spheres_separate_3d() {
assert!(!check_collision_spheres(
Vector3::new(0.0, 0.0, 0.0),
1.0,
Vector3::new(10.0, 0.0, 0.0),
1.0,
));
}
}