phys-raycast 2.0.0

Ray casting functionality for 3D physics shapes
Documentation
// Copyright (C) 2020-2025 phys-raycast authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Core traits and types for ray casting functionality.

use geom::math::unit_vec3;
use phys_geom::math::{Real, UnitVec3};

/// Result returned from [`Raycast::raycast`]
///
/// Contains information about a ray-shape intersection including the distance from the ray origin
/// to the hit point and the surface normal at the hit location.
#[repr(C)]
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RaycastHitResult {
    /// Distance from the ray origin to the hit point
    pub distance: Real,
    /// Surface normal at the hit point (pointing outward from the shape)
    pub normal: UnitVec3,
}

impl RaycastHitResult {
    /// Creates a new raycast hit result.
    ///
    /// # Arguments
    ///
    /// * `distance` - Distance from ray origin to hit point
    /// * `normal` - Surface normal at hit point
    #[inline]
    #[must_use]
    pub fn new(distance: Real, normal: impl Into<[Real; 3]>) -> Self {
        let [x, y, z] = normal.into();
        Self {
            distance,
            normal: unit_vec3(x, y, z),
        }
    }
}

/// Trait for objects that can be intersected by rays.
///
/// This trait defines the interface for ray casting operations against geometric shapes.
/// Implementations should compute the closest intersection point between a ray and the shape,
/// returning `None` if there is no intersection.
///
/// # Examples
///
/// ```rust
/// use phys_raycast::{Raycast, RaycastHitResult};
/// use phys_geom::{Ray, shape::Sphere, math::{Point3, Vec3}};
///
/// let sphere = Sphere::new(1.0);
/// let ray = Ray::new(Point3::new(-2.0, 0.0, 0.0), Vec3::x_axis());
///
/// if let Some(hit) = sphere.raycast(ray, 10.0, false) {
///     assert_eq!(hit.distance, 1.0);
///     assert_eq!(hit.normal, -Vec3::x_axis());
/// }
/// ```
pub trait Raycast {
    /// Casts a ray against this shape and returns the closest hit.
    ///
    /// # Arguments
    ///
    /// * `local_ray` - Ray in the shape's local coordinate space
    /// * `max_distance` - Maximum distance to check for intersections
    /// * `discard_inside_hit` - If true, returns None when ray origin is inside the shape
    ///
    /// # Returns
    ///
    /// `Some(RaycastHitResult)` if there is an intersection within `max_distance`,
    /// otherwise `None`.
    ///
    /// # Implementation Notes
    ///
    /// - The ray should be in the shape's local coordinate system
    /// - Only intersections with `distance >= 0.0` should be considered
    /// - When `discard_inside_hit` is true and the ray origin is inside the shape, the method
    ///   should return `None` rather than a hit at distance 0
    fn raycast(
        &self,
        local_ray: phys_geom::Ray,
        max_distance: Real,
        discard_inside_hit: bool,
    ) -> Option<RaycastHitResult>;
}