Skip to main content

ranim_core/utils/
math.rs

1use glam::{DVec2, DVec3, IVec2, dvec2};
2
3/// Cross product of 2d points
4pub fn cross2d(a: DVec2, b: DVec2) -> f64 {
5    a.x * b.y - b.x * a.y
6}
7
8/// Get the intersection point of two ray
9pub fn intersection(p1: DVec3, v1: DVec3, p2: DVec3, v2: DVec3) -> Option<DVec3> {
10    // println!("p1: {:?}, v1: {:?}, p2: {:?}, v2: {:?}", p1, v1, p2, v2);
11    let cross = v1.cross(v2);
12    let denom = cross.length_squared();
13    if denom < f64::EPSILON {
14        return None;
15    }
16
17    let diff = p2 - p1;
18    let t = (diff).cross(v2).dot(cross) / denom;
19    let s = (diff).cross(v1).dot(cross) / denom;
20
21    let point1 = p1 + v1 * t;
22    let point2 = p2 + v2 * s;
23
24    if (point1 - point2).length_squared() < f64::EPSILON {
25        Some(point1)
26    } else {
27        None
28    }
29}
30
31/// A rectangle in 2D space
32#[derive(Debug, Clone, Copy)]
33pub struct Rect {
34    min: DVec2,
35    max: DVec2,
36}
37
38impl Rect {
39    /// Get the union of two rectangle
40    pub fn union(&self, other: &Self) -> Self {
41        let min = self.min.min(other.min);
42        let max = self.max.max(other.max);
43        Self { min, max }
44    }
45    /// Get the intersection of two rectangle
46    pub fn intersection(&self, other: &Self) -> Self {
47        let min = self.min.max(other.min);
48        let max = self.max.min(other.max);
49        Self { min, max }
50    }
51    /// Get the center of the rectangle
52    pub fn center(&self) -> DVec2 {
53        (self.min + self.max) / 2.0
54    }
55
56    /// Get the point of the rectangle
57    /// ```text
58    /// (-1,-1)-----(0,-1)-----(1,-1)
59    ///    |          |          |
60    /// (-1, 0)-----(0, 0)-----(1, 0)
61    ///    |          |          |
62    /// (-1, 1)-----(0, 1)-----(1, 1)
63    /// ```
64    pub fn point(&self, edge: IVec2) -> DVec2 {
65        let min = self.min;
66        let center = self.center();
67        let max = self.max;
68
69        let x = match edge.x {
70            -1 => min.y,
71            0 => center.y,
72            1 => max.y,
73            _ => unreachable!(),
74        };
75        let y = match edge.y {
76            -1 => min.y,
77            0 => center.y,
78            1 => max.y,
79            _ => unreachable!(),
80        };
81
82        dvec2(x, y)
83    }
84}
85
86/// Interpolate between two integers
87///
88/// return integer and the sub progress to the next integer
89pub fn interpolate_usize(a: usize, b: usize, t: f64) -> (usize, f64) {
90    assert!(b >= a);
91    let t = t.clamp(0.0, 1.0);
92    let v = b - a;
93
94    let p = v as f64 * t;
95
96    (a + p.floor() as usize, p.fract())
97}
98
99#[cfg(test)]
100mod test {
101    use super::*;
102
103    #[test]
104    fn test_interpolate_usize() {
105        for (start, end, alpha, expected, remainder) in [
106            (0, 10, 0.0, 0, 0.0),
107            (0, 10, 0.5, 5, 0.0),
108            (0, 10, 1.0, 10, 0.0),
109            (0, 1, 0.5, 0, 0.5),
110            (0, 2, 0.8, 1, 0.6),
111        ] {
112            let (value, fraction) = interpolate_usize(start, end, alpha);
113            assert_eq!(value, expected);
114            assert!((fraction - remainder).abs() < f64::EPSILON);
115        }
116    }
117
118    #[test]
119    fn test_intersection() {
120        use glam::dvec3;
121
122        // 1. 垂直相交
123        let p1 = dvec3(0.0, 0.0, 0.0);
124        let v1 = dvec3(1.0, 0.0, 0.0);
125        let p2 = dvec3(0.0, 1.0, 0.0);
126        let v2 = dvec3(0.0, -1.0, 0.0);
127        assert_eq!(intersection(p1, v1, p2, v2), Some(dvec3(0.0, 0.0, 0.0)));
128
129        // 2. 斜交
130        let p1 = dvec3(1.0, 1.0, 0.0);
131        let v1 = dvec3(1.0, 2.0, 0.0);
132        let p2 = dvec3(3.0, 1.0, 0.0);
133        let v2 = dvec3(-1.0, 2.0, 0.0);
134        assert_eq!(intersection(p1, v1, p2, v2), Some(dvec3(2.0, 3.0, 0.0)));
135
136        // 3. 重合直线(应返回 None)
137        let p1 = dvec3(0.0, 0.0, 0.0);
138        let v1 = dvec3(1.0, 1.0, 1.0);
139        let p2 = dvec3(1.0, 1.0, 1.0);
140        let v2 = dvec3(2.0, 2.0, 2.0);
141        assert!(intersection(p1, v1, p2, v2).is_none());
142
143        // 4. 平行直线(应返回 None)
144        let p1 = dvec3(0.0, 0.0, 0.0);
145        let v1 = dvec3(1.0, 1.0, 0.0);
146        let p2 = dvec3(1.0, 0.0, 0.0);
147        let v2 = dvec3(1.0, 1.0, 0.0);
148        assert!(intersection(p1, v1, p2, v2).is_none());
149
150        // 5. 异面直线(应返回 None)
151        let p1 = dvec3(0.0, 0.0, 0.0);
152        let v1 = dvec3(1.0, 0.0, 1.0);
153        let p2 = dvec3(0.0, 1.0, 0.0);
154        let v2 = dvec3(1.0, 0.0, -1.0);
155        assert!(intersection(p1, v1, p2, v2).is_none());
156    }
157}