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}