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        let test = |(x, t): (usize, f64), (expected_x, expected_t): (usize, f64)| {
106            assert_eq!(x, expected_x);
107            assert!((t - expected_t).abs() < f64::EPSILON);
108        };
109
110        test(interpolate_usize(0, 10, 0.0), (0, 0.0));
111        test(interpolate_usize(0, 10, 0.5), (5, 0.0));
112        test(interpolate_usize(0, 10, 1.0), (10, 0.0));
113
114        test(interpolate_usize(0, 1, 0.0), (0, 0.0));
115        test(interpolate_usize(0, 1, 0.5), (0, 0.5));
116        test(interpolate_usize(0, 1, 1.0), (1, 0.0));
117
118        test(interpolate_usize(0, 2, 0.0), (0, 0.0));
119        test(interpolate_usize(0, 2, 0.2), (0, 0.4));
120        test(interpolate_usize(0, 2, 0.4), (0, 0.8));
121        test(interpolate_usize(0, 2, 0.6), (1, 0.2));
122        test(interpolate_usize(0, 2, 0.8), (1, 0.6));
123        test(interpolate_usize(0, 2, 1.0), (2, 0.0));
124    }
125
126    #[test]
127    fn test_intersection() {
128        use glam::dvec3;
129
130        // 1. 垂直相交
131        let p1 = dvec3(0.0, 0.0, 0.0);
132        let v1 = dvec3(1.0, 0.0, 0.0);
133        let p2 = dvec3(0.0, 1.0, 0.0);
134        let v2 = dvec3(0.0, -1.0, 0.0);
135        assert_eq!(intersection(p1, v1, p2, v2), Some(dvec3(0.0, 0.0, 0.0)));
136
137        // 2. 斜交
138        let p1 = dvec3(1.0, 1.0, 0.0);
139        let v1 = dvec3(1.0, 2.0, 0.0);
140        let p2 = dvec3(3.0, 1.0, 0.0);
141        let v2 = dvec3(-1.0, 2.0, 0.0);
142        assert_eq!(intersection(p1, v1, p2, v2), Some(dvec3(2.0, 3.0, 0.0)));
143
144        // 3. 重合直线(应返回 None)
145        let p1 = dvec3(0.0, 0.0, 0.0);
146        let v1 = dvec3(1.0, 1.0, 1.0);
147        let p2 = dvec3(1.0, 1.0, 1.0);
148        let v2 = dvec3(2.0, 2.0, 2.0);
149        assert!(intersection(p1, v1, p2, v2).is_none());
150
151        // 4. 平行直线(应返回 None)
152        let p1 = dvec3(0.0, 0.0, 0.0);
153        let v1 = dvec3(1.0, 1.0, 0.0);
154        let p2 = dvec3(1.0, 0.0, 0.0);
155        let v2 = dvec3(1.0, 1.0, 0.0);
156        assert!(intersection(p1, v1, p2, v2).is_none());
157
158        // 5. 异面直线(应返回 None)
159        let p1 = dvec3(0.0, 0.0, 0.0);
160        let v1 = dvec3(1.0, 0.0, 1.0);
161        let p2 = dvec3(0.0, 1.0, 0.0);
162        let v2 = dvec3(1.0, 0.0, -1.0);
163        assert!(intersection(p1, v1, p2, v2).is_none());
164    }
165}