phys_geom/
ray.rs

1// Copyright (C) 2020-2025 phys-geom 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//! Ray casting utilities for 3D geometry operations.
16//!
17//! This module provides functionality for working with rays in 3D space, which are
18//! fundamental for various geometric operations including:
19//! - Ray casting for collision detection
20//! - Intersection testing with geometric shapes
21//! - Picking operations in graphics applications
22//! - Line-of-sight calculations
23//!
24//! # Overview
25//!
26//! A ray is defined by an origin point and a direction vector. The ray extends
27//! infinitely from the origin in the specified direction.
28//!
29//! # Usage Examples
30//!
31//! ```rust
32//! use phys_geom::{Ray, math::Point3, math::Vec3};
33//!
34//! // Create a ray from origin in the positive X direction
35//! let origin = Point3::new(0.0, 0.0, 0.0);
36//! let direction = Vec3::x_axis();
37//! let ray = Ray::new(origin, direction);
38//!
39//! // Create a ray from a vector direction (automatically normalized)
40//! let ray2 = Ray::new_with_vec3(origin, Vec3::new(1.0, 1.0, 0.0));
41//! ```
42//!
43//! # Ray Transformations
44//!
45//! Rays can be transformed using isometries (rigid body transformations):
46//!
47//! ```rust
48//! use phys_geom::{Ray, math::Point3, math::Vec3, math::Isometry3};
49//!
50//! let origin = Point3::new(1.0, 0.0, 0.0);
51//! let direction = Vec3::x_axis();
52//! let ray = Ray::new(origin, direction);
53//! let isometry = Isometry3::translation(1.0, 0.0, 0.0);
54//! let transformed_ray = ray.inverse_transform(&isometry);
55//! ```
56//!
57//! # Performance Considerations
58//!
59//! - Ray creation is cheap and involves minimal computation
60//! - Direction vectors are stored as unit vectors for efficiency
61//! - The `one_over_direction` method pre-computes values for efficient AABB intersection
62//! - Transform operations require inverse transformation calculations
63
64use crate::math::{Isometry3, Point3, UnitVec3, Vec3};
65use crate::Real;
66
67/// A ray composed of an origin point and a unit direction vector.
68///
69/// A ray represents a half-line that starts at the origin point and extends infinitely
70/// in the direction specified by the unit vector. Rays are fundamental for ray casting,
71/// intersection testing, and various geometric algorithms.
72///
73/// # Mathematical Representation
74///
75/// A ray can be parametrically represented as:
76/// `R(t) = origin + t * direction` where `t >= 0`
77///
78/// # Examples
79///
80/// ```rust
81/// use phys_geom::{Ray, math::Point3, math::Vec3};
82///
83/// // Create a ray from the origin along the X-axis
84/// let origin = Point3::new(0.0, 0.0, 0.0);
85/// let direction = Vec3::x_axis();
86/// let ray = Ray::new(origin, direction);
87///
88/// // Create a ray using automatic normalization
89/// let ray2 = Ray::new_with_vec3(origin, Vec3::new(2.0, 0.0, 0.0));
90/// ```
91///
92/// # Memory Layout
93///
94/// The struct uses `#[repr(C)]` for FFI compatibility and stores the origin as a Point3
95/// and direction as a UnitVec3 (guaranteed to be normalized).
96///
97/// # Common Use Cases
98///
99/// - **Collision Detection**: Testing intersections with geometric shapes
100/// - **Picking**: Selecting objects in 3D scenes
101/// - **Visibility Testing**: Determining line-of-sight
102/// - **Rendering**: Ray tracing and casting algorithms
103#[repr(C)]
104#[derive(Clone, Copy, Debug)]
105pub struct Ray {
106    /// The starting point of the ray
107    pub origin: Point3,
108    /// The direction of the ray (guaranteed to be a unit vector)
109    pub direction: UnitVec3,
110}
111
112impl Ray {
113    /// Creates a new ray with the specified origin and direction.
114    ///
115    /// # Arguments
116    ///
117    /// * `origin` - The starting point of the ray
118    /// * `direction` - The direction of the ray (must be a unit vector)
119    ///
120    /// # Returns
121    ///
122    /// A new `Ray` instance.
123    ///
124    /// # Examples
125    ///
126    /// ```rust
127    /// use phys_geom::{Ray, math::Point3, math::Vec3, math::UnitVec3};
128    ///
129    /// let origin = Point3::new(1.0, 2.0, 3.0);
130    /// let direction = UnitVec3::new_normalize(Vec3::new(1.0, 0.0, 0.0));
131    /// let ray = Ray::new(origin, direction);
132    /// ```
133    #[inline]
134    #[must_use]
135    pub fn new(origin: Point3, direction: UnitVec3) -> Self {
136        Self { origin, direction }
137    }
138
139    /// Creates a new ray, automatically normalizing the direction vector.
140    ///
141    /// This is a convenience method that creates a ray from any non-zero direction
142    /// vector by automatically normalizing it to unit length.
143    ///
144    /// # Arguments
145    ///
146    /// * `origin` - The starting point of the ray
147    /// * `direction` - The direction vector (will be normalized)
148    ///
149    /// # Returns
150    ///
151    /// A new `Ray` instance with normalized direction.
152    ///
153    /// # Panics
154    ///
155    /// Will panic if `direction` is zero or infinite length.
156    ///
157    /// # Examples
158    ///
159    /// ```rust
160    /// use phys_geom::{Ray, math::Point3, math::Vec3, math::UnitVec3};
161    ///
162    /// let origin = Point3::new(0.0, 0.0, 0.0);
163    /// let direction = Vec3::new(3.0, 4.0, 0.0); // Not normalized
164    /// let ray = Ray::new_with_vec3(origin, direction);
165    ///
166    /// // Direction is automatically normalized to unit length
167    /// let dir_array = ray.direction.as_ref();
168    /// let dir_length = (dir_array[0].powi(2) + dir_array[1].powi(2) + dir_array[2].powi(2)).sqrt();
169    /// assert!((dir_length - 1.0).abs() < 1e-6);
170    /// ```
171    #[inline]
172    #[must_use]
173    pub fn new_with_vec3(origin: impl Into<[Real; 3]>, direction: impl Into<[Real; 3]>) -> Self {
174        Self {
175            origin: origin.into().into(),
176            direction: UnitVec3::new_normalize(direction.into().into()),
177        }
178    }
179
180    /// Computes the reciprocal of direction components for efficient Ray-AABB intersection testing.
181    ///
182    /// This method computes optimized values used in slab-based ray-AABB intersection algorithms.
183    /// The result incorporates sign information and avoids division by zero by using a small
184    /// epsilon.
185    ///
186    /// # Mathematical Details
187    ///
188    /// For each component of the direction vector, computes:
189    /// `result[i] = sign(direction[i]) / max(|direction[i]|, ε)`
190    /// where ε = 1e-15 to prevent division by zero.
191    ///
192    /// # Arguments
193    ///
194    /// * `direction` - The unit direction vector to process
195    ///
196    /// # Returns
197    ///
198    /// A Vec3 containing the optimized reciprocal values. Note that this is **not** a unit vector.
199    ///
200    ///
201    /// # See Also
202    ///
203    /// - [`Self::one_over_direction`] - Instance method that uses this computation
204    /// - [`crate::Aabb3::raycast`] - Method that uses these values for intersection testing
205    ///
206    /// # Examples
207    ///
208    /// ```rust
209    /// use phys_geom::{Ray, math::Vec3, math::UnitVec3};
210    ///
211    /// let direction = UnitVec3::new_normalize(Vec3::new(1.0, 2.0, 3.0));
212    /// let one_over = Ray::compute_one_over_direction(direction);
213    ///
214    /// // The result preserves sign information
215    /// assert!(one_over.x > 0.0);
216    /// assert!(one_over.y > 0.0);
217    /// assert!(one_over.z > 0.0);
218    /// ```
219    #[inline]
220    #[must_use]
221    pub fn compute_one_over_direction(direction: UnitVec3) -> Vec3 {
222        let one = Vec3::new(
223            if direction.x < 0.0 { -1.0 } else { 1.0 },
224            if direction.y < 0.0 { -1.0 } else { 1.0 },
225            if direction.z < 0.0 { -1.0 } else { 1.0 },
226        );
227        let v = direction
228            .into_inner()
229            .abs()
230            .sup(&Vec3::new(1e-15, 1e-15, 1e-15));
231        one.component_div(&v)
232    }
233
234    /// Computes the reciprocal of this ray's direction for Ray-AABB intersection testing.
235    ///
236    /// This is a convenience method that calls [`Self::compute_one_over_direction`] with
237    /// this ray's direction vector. The result is optimized for slab-based AABB intersection
238    /// algorithms.
239    ///
240    /// # Returns
241    ///
242    /// A Vec3 containing optimized reciprocal values for this ray's direction.
243    /// Note that this is **not** a unit vector.
244    ///
245    /// # Performance Note
246    ///
247    /// Call this method once per ray and reuse the result for multiple AABB intersection
248    /// tests to maximize performance.
249    ///
250    /// # See Also
251    ///
252    /// - [`Self::compute_one_over_direction`] - Static method with detailed explanation
253    /// - [`crate::Aabb3::raycast`] - Method that uses these values
254    ///
255    /// # Examples
256    ///
257    /// ```rust
258    /// use phys_geom::{Ray, math::Point3, math::Vec3};
259    ///
260    /// let direction = Vec3::x_axis();
261    /// let ray = Ray::new(Point3::origin(), direction);
262    /// let one_over = ray.one_over_direction();
263    ///
264    /// // Can be used for efficient AABB testing
265    /// // let intersects = aabb.raycast_by_one_over_direction(ray.origin, one_over, max_distance);
266    /// ```
267    #[inline]
268    #[must_use]
269    pub fn one_over_direction(&self) -> Vec3 {
270        Self::compute_one_over_direction(self.direction)
271    }
272
273    /// Applies the inverse of the given transformation to this ray.
274    ///
275    /// This method transforms the ray by the inverse of the specified isometry,
276    /// which is useful for transforming rays from world space to object space.
277    ///
278    /// # Arguments
279    ///
280    /// * `transform` - The isometry to invert and apply to the ray
281    ///
282    /// # Returns
283    ///
284    /// A new `Ray` instance representing the transformed ray.
285    ///
286    /// # Mathematical Details
287    ///
288    /// The transformation applies:
289    /// - Origin: `origin' = transform⁻¹ * origin`
290    /// - Direction: `direction' = transform⁻¹ * direction` (rotation only)
291    ///
292    /// Note that the direction is only affected by the rotational component of the
293    /// transform, as directions are invariant under translation.
294    ///
295    /// # Use Cases
296    ///
297    /// - **Object-space ray casting**: Transform world-space rays to object space
298    /// - **Hierarchical collision testing**: Transform rays through scene graphs
299    /// - **Inverse transformations**: Computing original rays from transformed ones
300    ///
301    /// # Examples
302    ///
303    /// ```rust
304    /// use phys_geom::{Ray, math::Point3, math::Vec3, math::Isometry3};
305    ///
306    /// let ray = Ray::new(Point3::new(1.0, 0.0, 0.0), Vec3::x_axis());
307    /// let transform = Isometry3::translation(1.0, 0.0, 0.0);
308    ///
309    /// let transformed_ray = ray.inverse_transform(&transform);
310    ///
311    /// // The ray origin is now at (0.0, 0.0, 0.0) in the new coordinate system
312    /// assert_eq!(transformed_ray.origin, Point3::new(0.0, 0.0, 0.0));
313    /// ```
314    #[inline]
315    #[must_use]
316    pub fn inverse_transform(&self, transform: &Isometry3) -> Self {
317        Self {
318            origin: transform.inverse_transform_point(&self.origin),
319            direction: transform.rotation.inverse() * self.direction,
320        }
321    }
322}