Skip to main content

anvilkit_core/math/
geometry.rs

1//! # 几何图形和边界框
2//! 
3//! 提供游戏开发中常用的几何图形类型和空间查询功能。
4//! 
5//! ## 核心类型
6//! 
7//! - [`Rect`]: 2D 矩形
8//! - [`Circle`]: 2D 圆形
9//! - [`Bounds2D`]: 2D 轴对齐边界框
10//! - [`Bounds3D`]: 3D 轴对齐边界框
11//! 
12//! ## 使用示例
13//! 
14//! ```rust
15//! use anvilkit_core::math::geometry::{Rect, Circle, Bounds3D};
16//! use glam::{Vec2, Vec3};
17//! 
18//! // 创建 2D 矩形
19//! let rect = Rect::from_center_size(Vec2::ZERO, Vec2::new(10.0, 20.0));
20//! 
21//! // 创建圆形
22//! let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
23//! 
24//! // 检查碰撞
25//! if rect.intersects_circle(&circle) {
26//!     println!("矩形和圆形相交!");
27//! }
28//! ```
29
30use glam::{Vec2, Vec3};
31
32/// 2D 矩形,用于边界检测和 UI 布局
33/// 
34/// 矩形使用最小点和最大点表示,确保 `min.x <= max.x` 和 `min.y <= max.y`。
35#[derive(Debug, Clone, Copy, PartialEq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct Rect {
38    /// 最小点(左下角)
39    pub min: Vec2,
40    /// 最大点(右上角)
41    pub max: Vec2,
42}
43
44impl Rect {
45    /// 零大小的矩形
46    pub const ZERO: Self = Self {
47        min: Vec2::ZERO,
48        max: Vec2::ZERO,
49    };
50
51    /// 创建新的矩形
52    /// 
53    /// # 参数
54    /// 
55    /// - `min`: 最小点
56    /// - `max`: 最大点
57    /// 
58    /// # 注意
59    /// 
60    /// 如果 `min` 的某个分量大于 `max` 的对应分量,会自动交换以确保有效性。
61    /// 
62    /// # 示例
63    /// 
64    /// ```rust
65    /// use anvilkit_core::math::geometry::Rect;
66    /// use glam::Vec2;
67    /// 
68    /// let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 20.0));
69    /// assert_eq!(rect.width(), 10.0);
70    /// assert_eq!(rect.height(), 20.0);
71    /// ```
72    pub fn new(min: Vec2, max: Vec2) -> Self {
73        Self {
74            min: min.min(max),
75            max: min.max(max),
76        }
77    }
78
79    /// 从中心点和大小创建矩形
80    /// 
81    /// # 示例
82    /// 
83    /// ```rust
84    /// use anvilkit_core::math::geometry::Rect;
85    /// use glam::Vec2;
86    /// 
87    /// let rect = Rect::from_center_size(Vec2::new(5.0, 5.0), Vec2::new(10.0, 20.0));
88    /// assert_eq!(rect.center(), Vec2::new(5.0, 5.0));
89    /// ```
90    pub fn from_center_size(center: Vec2, size: Vec2) -> Self {
91        let half_size = size * 0.5;
92        Self::new(center - half_size, center + half_size)
93    }
94
95    /// 从位置和大小创建矩形(位置为左下角)
96    pub fn from_position_size(position: Vec2, size: Vec2) -> Self {
97        Self::new(position, position + size)
98    }
99
100    /// 获取矩形宽度
101    pub fn width(&self) -> f32 {
102        self.max.x - self.min.x
103    }
104
105    /// 获取矩形高度
106    pub fn height(&self) -> f32 {
107        self.max.y - self.min.y
108    }
109
110    /// 获取矩形大小
111    pub fn size(&self) -> Vec2 {
112        self.max - self.min
113    }
114
115    /// 获取矩形中心点
116    pub fn center(&self) -> Vec2 {
117        (self.min + self.max) * 0.5
118    }
119
120    /// 获取矩形面积
121    pub fn area(&self) -> f32 {
122        let size = self.size();
123        size.x * size.y
124    }
125
126    /// 获取矩形周长
127    pub fn perimeter(&self) -> f32 {
128        let size = self.size();
129        2.0 * (size.x + size.y)
130    }
131
132    /// 检查点是否在矩形内
133    /// 
134    /// # 示例
135    /// 
136    /// ```rust
137    /// use anvilkit_core::math::geometry::Rect;
138    /// use glam::Vec2;
139    /// 
140    /// let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
141    /// assert!(rect.contains(Vec2::new(5.0, 5.0)));
142    /// assert!(!rect.contains(Vec2::new(15.0, 5.0)));
143    /// ```
144    pub fn contains(&self, point: Vec2) -> bool {
145        point.x >= self.min.x && point.x <= self.max.x &&
146        point.y >= self.min.y && point.y <= self.max.y
147    }
148
149    /// 检查是否与另一个矩形相交
150    /// 
151    /// # 示例
152    /// 
153    /// ```rust
154    /// use anvilkit_core::math::geometry::Rect;
155    /// use glam::Vec2;
156    /// 
157    /// let rect1 = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
158    /// let rect2 = Rect::new(Vec2::new(5.0, 5.0), Vec2::new(15.0, 15.0));
159    /// assert!(rect1.intersects(&rect2));
160    /// ```
161    pub fn intersects(&self, other: &Rect) -> bool {
162        self.min.x <= other.max.x && self.max.x >= other.min.x &&
163        self.min.y <= other.max.y && self.max.y >= other.min.y
164    }
165
166    /// 检查是否与圆形相交
167    pub fn intersects_circle(&self, circle: &Circle) -> bool {
168        // 找到矩形上距离圆心最近的点
169        let closest_point = Vec2::new(
170            circle.center.x.clamp(self.min.x, self.max.x),
171            circle.center.y.clamp(self.min.y, self.max.y),
172        );
173        
174        // 检查距离是否小于半径
175        let distance_squared = (circle.center - closest_point).length_squared();
176        distance_squared <= circle.radius * circle.radius
177    }
178
179    /// 计算与另一个矩形的交集
180    /// 
181    /// # 返回
182    /// 
183    /// 如果两个矩形相交,返回交集矩形;否则返回 `None`。
184    pub fn intersection(&self, other: &Rect) -> Option<Rect> {
185        if !self.intersects(other) {
186            return None;
187        }
188
189        Some(Rect::new(
190            self.min.max(other.min),
191            self.max.min(other.max),
192        ))
193    }
194
195    /// 计算包含两个矩形的最小矩形
196    pub fn union(&self, other: &Rect) -> Rect {
197        Rect::new(
198            self.min.min(other.min),
199            self.max.max(other.max),
200        )
201    }
202
203    /// 扩展矩形以包含指定点
204    pub fn expand_to_include(&mut self, point: Vec2) {
205        self.min = self.min.min(point);
206        self.max = self.max.max(point);
207    }
208
209    /// 按指定量扩展矩形
210    /// 
211    /// # 参数
212    /// 
213    /// - `amount`: 扩展量,正值扩展,负值收缩
214    pub fn expand(&self, amount: f32) -> Rect {
215        let expansion = Vec2::splat(amount);
216        Rect::new(self.min - expansion, self.max + expansion)
217    }
218
219    /// 检查矩形是否有效(非负大小)
220    pub fn is_valid(&self) -> bool {
221        self.min.x <= self.max.x && self.min.y <= self.max.y
222    }
223}
224
225/// 2D 圆形
226#[derive(Debug, Clone, Copy, PartialEq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct Circle {
229    /// 圆心
230    pub center: Vec2,
231    /// 半径
232    pub radius: f32,
233}
234
235impl Circle {
236    /// 创建新的圆形
237    /// 
238    /// # 参数
239    /// 
240    /// - `center`: 圆心位置
241    /// - `radius`: 半径(必须为非负数)
242    /// 
243    /// # 示例
244    /// 
245    /// ```rust
246    /// use anvilkit_core::math::geometry::Circle;
247    /// use glam::Vec2;
248    /// 
249    /// let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
250    /// assert_eq!(circle.area(), std::f32::consts::PI * 9.0);
251    /// ```
252    pub fn new(center: Vec2, radius: f32) -> Self {
253        Self {
254            center,
255            radius: radius.max(0.0), // 确保半径非负
256        }
257    }
258
259    /// 获取圆的面积
260    pub fn area(&self) -> f32 {
261        std::f32::consts::PI * self.radius * self.radius
262    }
263
264    /// 获取圆的周长
265    pub fn circumference(&self) -> f32 {
266        2.0 * std::f32::consts::PI * self.radius
267    }
268
269    /// 检查点是否在圆内
270    pub fn contains(&self, point: Vec2) -> bool {
271        (point - self.center).length_squared() <= self.radius * self.radius
272    }
273
274    /// 检查是否与另一个圆相交
275    pub fn intersects(&self, other: &Circle) -> bool {
276        let distance_squared = (self.center - other.center).length_squared();
277        let radius_sum = self.radius + other.radius;
278        distance_squared <= radius_sum * radius_sum
279    }
280
281    /// 检查是否与矩形相交
282    pub fn intersects_rect(&self, rect: &Rect) -> bool {
283        rect.intersects_circle(self)
284    }
285
286    /// 获取圆的边界矩形
287    pub fn bounding_rect(&self) -> Rect {
288        let radius_vec = Vec2::splat(self.radius);
289        Rect::new(self.center - radius_vec, self.center + radius_vec)
290    }
291}
292
293/// 2D 轴对齐边界框
294pub type Bounds2D = Rect;
295
296/// 3D 轴对齐边界框
297#[derive(Debug, Clone, Copy, PartialEq)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
299pub struct Bounds3D {
300    /// 最小点
301    pub min: Vec3,
302    /// 最大点
303    pub max: Vec3,
304}
305
306impl Bounds3D {
307    /// 零大小的边界框
308    pub const ZERO: Self = Self {
309        min: Vec3::ZERO,
310        max: Vec3::ZERO,
311    };
312
313    /// 创建新的 3D 边界框
314    pub fn new(min: Vec3, max: Vec3) -> Self {
315        Self {
316            min: min.min(max),
317            max: min.max(max),
318        }
319    }
320
321    /// 从中心点和大小创建边界框
322    pub fn from_center_size(center: Vec3, size: Vec3) -> Self {
323        let half_size = size * 0.5;
324        Self::new(center - half_size, center + half_size)
325    }
326
327    /// 获取边界框大小
328    pub fn size(&self) -> Vec3 {
329        self.max - self.min
330    }
331
332    /// 获取边界框中心点
333    pub fn center(&self) -> Vec3 {
334        (self.min + self.max) * 0.5
335    }
336
337    /// 获取边界框体积
338    pub fn volume(&self) -> f32 {
339        let size = self.size();
340        size.x * size.y * size.z
341    }
342
343    /// 检查点是否在边界框内
344    pub fn contains(&self, point: Vec3) -> bool {
345        point.x >= self.min.x && point.x <= self.max.x &&
346        point.y >= self.min.y && point.y <= self.max.y &&
347        point.z >= self.min.z && point.z <= self.max.z
348    }
349
350    /// 检查是否与另一个边界框相交
351    pub fn intersects(&self, other: &Bounds3D) -> bool {
352        self.min.x <= other.max.x && self.max.x >= other.min.x &&
353        self.min.y <= other.max.y && self.max.y >= other.min.y &&
354        self.min.z <= other.max.z && self.max.z >= other.min.z
355    }
356
357    /// 扩展边界框以包含指定点
358    pub fn expand_to_include(&mut self, point: Vec3) {
359        self.min = self.min.min(point);
360        self.max = self.max.max(point);
361    }
362
363    /// 计算与另一个边界框的交集
364    pub fn intersection(&self, other: &Bounds3D) -> Option<Bounds3D> {
365        if !self.intersects(other) {
366            return None;
367        }
368
369        Some(Bounds3D::new(
370            self.min.max(other.min),
371            self.max.min(other.max),
372        ))
373    }
374
375    /// 计算包含两个边界框的最小边界框
376    pub fn union(&self, other: &Bounds3D) -> Bounds3D {
377        Bounds3D::new(
378            self.min.min(other.min),
379            self.max.max(other.max),
380        )
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    use approx::assert_relative_eq;
388
389    #[test]
390    fn test_rect_creation() {
391        let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 20.0));
392        assert_eq!(rect.width(), 10.0);
393        assert_eq!(rect.height(), 20.0);
394        assert_eq!(rect.area(), 200.0);
395    }
396
397    #[test]
398    fn test_rect_from_center_size() {
399        let rect = Rect::from_center_size(Vec2::new(5.0, 10.0), Vec2::new(10.0, 20.0));
400        assert_eq!(rect.center(), Vec2::new(5.0, 10.0));
401        assert_eq!(rect.size(), Vec2::new(10.0, 20.0));
402    }
403
404    #[test]
405    fn test_rect_contains() {
406        let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
407        assert!(rect.contains(Vec2::new(5.0, 5.0)));
408        assert!(rect.contains(Vec2::ZERO)); // 边界点
409        assert!(rect.contains(Vec2::new(10.0, 10.0))); // 边界点
410        assert!(!rect.contains(Vec2::new(15.0, 5.0)));
411    }
412
413    #[test]
414    fn test_rect_intersection() {
415        let rect1 = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
416        let rect2 = Rect::new(Vec2::new(5.0, 5.0), Vec2::new(15.0, 15.0));
417        
418        assert!(rect1.intersects(&rect2));
419        
420        let intersection = rect1.intersection(&rect2).unwrap();
421        assert_eq!(intersection.min, Vec2::new(5.0, 5.0));
422        assert_eq!(intersection.max, Vec2::new(10.0, 10.0));
423    }
424
425    #[test]
426    fn test_rect_union() {
427        let rect1 = Rect::new(Vec2::ZERO, Vec2::new(5.0, 5.0));
428        let rect2 = Rect::new(Vec2::new(3.0, 3.0), Vec2::new(8.0, 8.0));
429        
430        let union = rect1.union(&rect2);
431        assert_eq!(union.min, Vec2::ZERO);
432        assert_eq!(union.max, Vec2::new(8.0, 8.0));
433    }
434
435    #[test]
436    fn test_circle_creation() {
437        let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
438        assert_eq!(circle.center, Vec2::new(5.0, 5.0));
439        assert_eq!(circle.radius, 3.0);
440        assert_relative_eq!(circle.area(), std::f32::consts::PI * 9.0, epsilon = 1e-6);
441    }
442
443    #[test]
444    fn test_circle_contains() {
445        let circle = Circle::new(Vec2::ZERO, 5.0);
446        assert!(circle.contains(Vec2::new(3.0, 4.0))); // 3-4-5 三角形
447        assert!(!circle.contains(Vec2::new(4.0, 4.0))); // 超出半径
448    }
449
450    #[test]
451    fn test_circle_intersection() {
452        let circle1 = Circle::new(Vec2::ZERO, 5.0);
453        let circle2 = Circle::new(Vec2::new(8.0, 0.0), 5.0);
454        
455        assert!(circle1.intersects(&circle2)); // 相交
456        
457        let circle3 = Circle::new(Vec2::new(12.0, 0.0), 5.0);
458        assert!(!circle1.intersects(&circle3)); // 不相交
459    }
460
461    #[test]
462    fn test_rect_circle_intersection() {
463        let rect = Rect::new(Vec2::ZERO, Vec2::new(10.0, 10.0));
464        let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
465        
466        assert!(rect.intersects_circle(&circle));
467        assert!(circle.intersects_rect(&rect));
468    }
469
470    #[test]
471    fn test_bounds3d() {
472        let bounds = Bounds3D::from_center_size(Vec3::ZERO, Vec3::ONE);
473        assert_eq!(bounds.center(), Vec3::ZERO);
474        assert_eq!(bounds.volume(), 1.0);
475        assert!(bounds.contains(Vec3::new(0.4, 0.4, 0.4)));
476        assert!(!bounds.contains(Vec3::new(0.6, 0.6, 0.6)));
477    }
478
479    #[test]
480    fn test_rect_expand() {
481        let rect = Rect::new(Vec2::new(2.0, 2.0), Vec2::new(8.0, 8.0));
482        let expanded = rect.expand(1.0);
483        
484        assert_eq!(expanded.min, Vec2::new(1.0, 1.0));
485        assert_eq!(expanded.max, Vec2::new(9.0, 9.0));
486    }
487
488    #[test]
489    fn test_rect_auto_correct() {
490        // 测试自动纠正最小值和最大值
491        let rect = Rect::new(Vec2::new(10.0, 10.0), Vec2::ZERO);
492        assert_eq!(rect.min, Vec2::ZERO);
493        assert_eq!(rect.max, Vec2::new(10.0, 10.0));
494    }
495
496    #[test]
497    fn test_rect_zero() {
498        let rect = Rect::ZERO;
499        assert_eq!(rect.width(), 0.0);
500        assert_eq!(rect.height(), 0.0);
501        assert_eq!(rect.area(), 0.0);
502        assert!(rect.contains(Vec2::ZERO));
503    }
504
505    #[test]
506    fn test_rect_from_position_size() {
507        let rect = Rect::from_position_size(Vec2::new(2.0, 3.0), Vec2::new(4.0, 5.0));
508        assert_eq!(rect.min, Vec2::new(2.0, 3.0));
509        assert_eq!(rect.max, Vec2::new(6.0, 8.0));
510        assert_eq!(rect.width(), 4.0);
511        assert_eq!(rect.height(), 5.0);
512    }
513
514    #[test]
515    fn test_rect_perimeter() {
516        let rect = Rect::new(Vec2::ZERO, Vec2::new(3.0, 4.0));
517        assert_eq!(rect.perimeter(), 14.0);
518    }
519
520    #[test]
521    fn test_rect_no_intersection() {
522        let rect1 = Rect::new(Vec2::ZERO, Vec2::new(1.0, 1.0));
523        let rect2 = Rect::new(Vec2::new(5.0, 5.0), Vec2::new(6.0, 6.0));
524        assert!(!rect1.intersects(&rect2));
525        assert!(rect1.intersection(&rect2).is_none());
526    }
527
528    #[test]
529    fn test_rect_expand_to_include() {
530        let mut rect = Rect::new(Vec2::new(1.0, 1.0), Vec2::new(3.0, 3.0));
531        rect.expand_to_include(Vec2::new(5.0, 0.0));
532        assert_eq!(rect.min, Vec2::new(1.0, 0.0));
533        assert_eq!(rect.max, Vec2::new(5.0, 3.0));
534    }
535
536    #[test]
537    fn test_rect_expand_negative() {
538        let rect = Rect::new(Vec2::new(0.0, 0.0), Vec2::new(10.0, 10.0));
539        let contracted = rect.expand(-2.0);
540        assert_eq!(contracted.min, Vec2::new(2.0, 2.0));
541        assert_eq!(contracted.max, Vec2::new(8.0, 8.0));
542    }
543
544    #[test]
545    fn test_rect_is_valid() {
546        let valid = Rect::new(Vec2::ZERO, Vec2::ONE);
547        assert!(valid.is_valid());
548
549        let also_valid = Rect::ZERO;
550        assert!(also_valid.is_valid());
551    }
552
553    #[test]
554    fn test_circle_negative_radius() {
555        let circle = Circle::new(Vec2::ZERO, -5.0);
556        assert_eq!(circle.radius, 0.0); // 负半径被钳制为0
557    }
558
559    #[test]
560    fn test_circle_zero_radius() {
561        let circle = Circle::new(Vec2::new(1.0, 1.0), 0.0);
562        assert_eq!(circle.area(), 0.0);
563        assert_eq!(circle.circumference(), 0.0);
564        assert!(circle.contains(Vec2::new(1.0, 1.0))); // 中心点仍被包含
565    }
566
567    #[test]
568    fn test_circle_bounding_rect() {
569        let circle = Circle::new(Vec2::new(5.0, 5.0), 3.0);
570        let bounding = circle.bounding_rect();
571        assert_eq!(bounding.min, Vec2::new(2.0, 2.0));
572        assert_eq!(bounding.max, Vec2::new(8.0, 8.0));
573    }
574
575    #[test]
576    fn test_circle_touching() {
577        // 两个圆恰好相切
578        let c1 = Circle::new(Vec2::ZERO, 3.0);
579        let c2 = Circle::new(Vec2::new(6.0, 0.0), 3.0);
580        assert!(c1.intersects(&c2)); // 相切也算相交
581    }
582
583    #[test]
584    fn test_bounds3d_intersection() {
585        let b1 = Bounds3D::new(Vec3::ZERO, Vec3::splat(5.0));
586        let b2 = Bounds3D::new(Vec3::splat(3.0), Vec3::splat(8.0));
587
588        let intersection = b1.intersection(&b2).unwrap();
589        assert_eq!(intersection.min, Vec3::splat(3.0));
590        assert_eq!(intersection.max, Vec3::splat(5.0));
591    }
592
593    #[test]
594    fn test_bounds3d_no_intersection() {
595        let b1 = Bounds3D::new(Vec3::ZERO, Vec3::ONE);
596        let b2 = Bounds3D::new(Vec3::splat(5.0), Vec3::splat(6.0));
597        assert!(!b1.intersects(&b2));
598        assert!(b1.intersection(&b2).is_none());
599    }
600
601    #[test]
602    fn test_bounds3d_union() {
603        let b1 = Bounds3D::new(Vec3::ZERO, Vec3::splat(2.0));
604        let b2 = Bounds3D::new(Vec3::splat(3.0), Vec3::splat(5.0));
605        let union = b1.union(&b2);
606        assert_eq!(union.min, Vec3::ZERO);
607        assert_eq!(union.max, Vec3::splat(5.0));
608    }
609
610    #[test]
611    fn test_bounds3d_expand_to_include() {
612        let mut bounds = Bounds3D::new(Vec3::ONE, Vec3::splat(3.0));
613        bounds.expand_to_include(Vec3::new(-1.0, 5.0, 2.0));
614        assert_eq!(bounds.min, Vec3::new(-1.0, 1.0, 1.0));
615        assert_eq!(bounds.max, Vec3::new(3.0, 5.0, 3.0));
616    }
617
618    #[test]
619    fn test_bounds3d_contains_boundary() {
620        let bounds = Bounds3D::new(Vec3::ZERO, Vec3::splat(10.0));
621        // 边界上的点
622        assert!(bounds.contains(Vec3::ZERO));
623        assert!(bounds.contains(Vec3::splat(10.0)));
624        // 刚刚超出
625        assert!(!bounds.contains(Vec3::new(10.001, 5.0, 5.0)));
626    }
627}