phys_geom/interval.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//! One-dimensional interval arithmetic utilities.
16//!
17//! This module provides functionality for working with one-dimensional intervals
18//! on the real number line. Intervals are fundamental for various geometric
19//! algorithms including:
20//! - Collision detection and response
21//! - Spatial partitioning and culling
22//! - Numerical robustness in geometric computations
23//! - Bounding box projections and intersections
24//!
25//! # Overview
26//!
27//! An interval represents a closed range [min, max] on the real number line.
28//! The interval is inclusive, meaning both endpoints are considered part of
29//! the interval.
30//!
31//! # Examples
32//!
33//! ```rust
34//! use phys_geom::Interval;
35//!
36//! // Create an interval from 0.0 to 1.0
37//! let interval = Interval::new(0.0, 1.0);
38//!
39//! // Check properties
40//! assert_eq!(interval.min(), 0.0);
41//! assert_eq!(interval.max(), 1.0);
42//!
43//! // Check overlap with another interval
44//! let other = Interval::new(0.5, 1.5);
45//! assert!(interval.overlaps(&other));
46//! ```
47//! # Mathematical Properties
48//!
49//! The interval maintains the invariant that `min <= max`. This is enforced
50//! at creation time and all operations preserve this invariant.
51
52use crate::math::Real;
53
54/// A one-dimensional interval on the real number line.
55///
56/// An interval represents a closed range [min, max] where both endpoints
57/// are included. This is a fundamental building block for many geometric
58/// algorithms, particularly in collision detection and spatial queries.
59///
60/// # Mathematical Representation
61///
62/// The interval represents all real numbers x such that: `min <= x <= max`
63///
64/// # Examples
65///
66/// ```rust
67/// use phys_geom::Interval;
68///
69/// // Create an interval representing the range [0.0, 1.0]
70/// let interval = Interval::new(0.0, 1.0);
71///
72/// // Check if a value is within the interval
73/// let contains_0_5 = interval.min() <= 0.5 && 0.5 <= interval.max();
74/// assert!(contains_0_5);
75/// ```
76///
77/// # Common Applications
78///
79/// - **Separating Axis Theorem**: Testing overlap of projected shapes
80/// - **AABB Operations**: Computing intersections along specific axes
81/// - **Numerical Robustness**: Handling floating-point precision issues
82/// - **Spatial Partitioning**: Determining object overlaps in 1D
83///
84/// # Invariants
85///
86/// The struct maintains the invariant that `min <= max`. This is enforced
87/// at construction time and preserved by all operations.
88///
89/// # Memory Layout
90///
91/// The struct contains exactly two `Real` values and is designed to be
92/// efficient for both computation and memory usage.
93#[derive(Debug, PartialEq, Clone, Copy)]
94pub struct Interval {
95 /// The minimum (lower) bound of the interval
96 min: Real,
97 /// The maximum (upper) bound of the interval
98 max: Real,
99}
100
101impl Interval {
102 /// Creates a new interval with the specified bounds.
103 ///
104 /// This function creates a closed interval [min, max] that includes both endpoints.
105 ///
106 /// # Arguments
107 ///
108 /// * `min` - The minimum (lower) bound of the interval
109 /// * `max` - The maximum (upper) bound of the interval
110 ///
111 /// # Returns
112 ///
113 /// A new `Interval` instance representing the range [min, max].
114 ///
115 /// # Panics
116 ///
117 /// Panics if `min > max`, as this would violate the interval invariant.
118 ///
119 /// # Examples
120 ///
121 /// ```rust
122 /// use phys_geom::Interval;
123 ///
124 /// // Create a valid interval
125 /// let interval = Interval::new(0.0, 1.0);
126 /// assert_eq!(interval.min(), 0.0);
127 /// assert_eq!(interval.max(), 1.0);
128 ///
129 /// // This would panic: Interval::new(1.0, 0.0);
130 /// ```
131 ///
132 /// # Edge Cases
133 ///
134 /// - `min == max`: Creates a zero-width interval (single point)
135 /// - `min < max`: Creates a normal interval with positive width
136 #[must_use]
137 #[inline]
138 pub fn new(min: Real, max: Real) -> Self {
139 assert!(min <= max);
140 Self { min, max }
141 }
142
143 /// Returns the minimum (lower) bound of the interval.
144 ///
145 /// # Returns
146 ///
147 /// The minimum value as a `Real`.
148 ///
149 /// # Examples
150 ///
151 /// ```rust
152 /// use phys_geom::Interval;
153 ///
154 /// let interval = Interval::new(0.0, 1.0);
155 /// assert_eq!(interval.min(), 0.0);
156 /// ```
157 #[must_use]
158 #[inline]
159 pub fn min(&self) -> Real {
160 self.min
161 }
162
163 /// Returns the maximum (upper) bound of the interval.
164 ///
165 /// # Returns
166 ///
167 /// The maximum value as a `Real`.
168 ///
169 /// # Examples
170 ///
171 /// ```rust
172 /// use phys_geom::Interval;
173 ///
174 /// let interval = Interval::new(0.0, 1.0);
175 /// assert_eq!(interval.max(), 1.0);
176 /// ```
177 #[must_use]
178 #[inline]
179 pub fn max(&self) -> Real {
180 self.max
181 }
182
183 /// Checks if this interval overlaps with another interval.
184 ///
185 /// Two intervals overlap if they have at least one point in common.
186 /// Since these are closed intervals, the overlap condition is:
187 /// `self.min <= other.max && self.max >= other.min`
188 ///
189 /// # Arguments
190 ///
191 /// * `other` - The other interval to test for overlap
192 ///
193 /// # Returns
194 ///
195 /// `true` if the intervals overlap, `false` otherwise.
196 ///
197 /// # Examples
198 ///
199 /// ```rust
200 /// use phys_geom::Interval;
201 ///
202 /// let a = Interval::new(0.0, 2.0);
203 /// let b = Interval::new(1.0, 3.0);
204 /// let c = Interval::new(2.5, 4.0);
205 ///
206 /// assert!(a.overlaps(&b)); // Overlap in [1.0, 2.0]
207 /// assert!(!a.overlaps(&c)); // No overlap
208 /// assert!(b.overlaps(&c)); // Overlap at point 2.5
209 /// ```
210 ///
211 /// # Edge Cases
212 ///
213 /// - **Touching intervals**: `[0,1]` and `[1,2]` overlap at point 1
214 /// - **Zero-width intervals**: `[1,1]` overlaps with `[0,2]` but not with `[2,3]`
215 /// - **Identical intervals**: Always overlap with themselves
216 ///
217 /// # Mathematical Definition
218 ///
219 /// The overlap condition can be written as:
220 /// `overlap = [max(self.min, other.min), min(self.max, other.max)]` is non-empty
221 ///
222 /// Note: This is a closed interval, meaning that the bounds are included
223 /// in the overlap calculation.
224 #[must_use]
225 #[inline]
226 pub fn overlaps(&self, other: &Self) -> bool {
227 self.min <= other.max && self.max >= other.min
228 }
229}