phys_raycast/
traits.rs

1// Copyright (C) 2020-2025 phys-raycast authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Core traits and types for ray casting functionality.
16
17use geom::math::unit_vec3;
18use phys_geom::math::{Real, UnitVec3};
19
20/// Result returned from [`Raycast::raycast`]
21///
22/// Contains information about a ray-shape intersection including the distance from the ray origin
23/// to the hit point and the surface normal at the hit location.
24#[repr(C)]
25#[derive(Debug, PartialEq, Clone, Copy)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct RaycastHitResult {
28    /// Distance from the ray origin to the hit point
29    pub distance: Real,
30    /// Surface normal at the hit point (pointing outward from the shape)
31    pub normal: UnitVec3,
32}
33
34impl RaycastHitResult {
35    /// Creates a new raycast hit result.
36    ///
37    /// # Arguments
38    ///
39    /// * `distance` - Distance from ray origin to hit point
40    /// * `normal` - Surface normal at hit point
41    #[inline]
42    #[must_use]
43    pub fn new(distance: Real, normal: impl Into<[Real; 3]>) -> Self {
44        let [x, y, z] = normal.into();
45        Self {
46            distance,
47            normal: unit_vec3(x, y, z),
48        }
49    }
50}
51
52/// Trait for objects that can be intersected by rays.
53///
54/// This trait defines the interface for ray casting operations against geometric shapes.
55/// Implementations should compute the closest intersection point between a ray and the shape,
56/// returning `None` if there is no intersection.
57///
58/// # Examples
59///
60/// ```rust
61/// use phys_raycast::{Raycast, RaycastHitResult};
62/// use phys_geom::{Ray, shape::Sphere, math::{Point3, Vec3}};
63///
64/// let sphere = Sphere::new(1.0);
65/// let ray = Ray::new(Point3::new(-2.0, 0.0, 0.0), Vec3::x_axis());
66///
67/// if let Some(hit) = sphere.raycast(ray, 10.0, false) {
68///     assert_eq!(hit.distance, 1.0);
69///     assert_eq!(hit.normal, -Vec3::x_axis());
70/// }
71/// ```
72pub trait Raycast {
73    /// Casts a ray against this shape and returns the closest hit.
74    ///
75    /// # Arguments
76    ///
77    /// * `local_ray` - Ray in the shape's local coordinate space
78    /// * `max_distance` - Maximum distance to check for intersections
79    /// * `discard_inside_hit` - If true, returns None when ray origin is inside the shape
80    ///
81    /// # Returns
82    ///
83    /// `Some(RaycastHitResult)` if there is an intersection within `max_distance`,
84    /// otherwise `None`.
85    ///
86    /// # Implementation Notes
87    ///
88    /// - The ray should be in the shape's local coordinate system
89    /// - Only intersections with `distance >= 0.0` should be considered
90    /// - When `discard_inside_hit` is true and the ray origin is inside the shape, the method
91    ///   should return `None` rather than a hit at distance 0
92    fn raycast(
93        &self,
94        local_ray: phys_geom::Ray,
95        max_distance: Real,
96        discard_inside_hit: bool,
97    ) -> Option<RaycastHitResult>;
98}