minvect/
rect.rs

1use crate::*;
2
3#[repr(C)]
4#[derive(Debug, Clone, Copy, PartialEq, Default, PartialOrd)]
5pub struct Rect {
6    pub xy: Vec2,
7    pub wh: Vec2,
8}
9/// rect from scalars
10pub const fn rect(x: f32, y: f32, w: f32, h: f32) -> Rect {
11    Rect { xy: vec2(x,y), wh: vec2(w,h)}
12}
13/// rect from vectors
14pub const fn rectv(xy: Vec2, wh: Vec2) -> Rect {
15    Rect { xy, wh }
16}
17/// rect centered
18pub fn rectc(xy: Vec2, wh: Vec2) -> Rect {
19    Rect { xy: xy - wh/2.0, wh }
20}
21
22impl Rect {
23    pub fn i(&self) -> Vec2 { vec2(self.wh.x, 0.0) }
24    pub fn j(&self) -> Vec2 { vec2(0.0, self.wh.y) }
25    pub fn tl(&self) -> Vec2 { self.xy }
26    pub fn tr(&self) -> Vec2 { self.xy + self.i() }
27    pub fn bl(&self) -> Vec2 { self.xy + self.j() }
28    pub fn br(&self) -> Vec2 { self.xy + self.wh }
29    pub fn w(&self) -> f32 { self.wh.x }
30    pub fn h(&self) -> f32 { self.wh.y }
31    pub fn center(&self) -> Vec2 { self.xy + self.wh/2.0 }
32    pub fn contains(&self, p: Vec2) -> bool {
33        let p = p - self.xy;
34        p.x >= 0.0 && p.x <= self.wh.x &&
35        p.y >= 0.0 && p.y <= self.wh.y
36    }
37    // p uv not ndc
38    pub fn rect_to_world(&self, p: Vec2) -> Vec2 {
39        self.xy + p * self.wh
40    }
41    pub fn world_to_rect(&self, p: Vec2) -> Vec2 {
42        (p - self.xy) / self.wh
43    }
44    pub fn uniform_grid(&self, i: usize, j: usize, w: usize, h: usize) -> Rect {
45        let v = self.wh / vec2(w as f32, h as f32);
46        rectv(self.xy + vec2(i as f32, j as f32) * v, v)
47    }
48    /// returns grid rect i,j. widths and heights as weights of parent vect.
49    pub fn grid(&self, i: usize, j: usize, widths: &[f32], heights: &[f32]) -> Rect {
50        let mut acc = vec2(0.0, 0.0);
51        let sw = widths.iter().fold(0.0f32, |acc: f32, e| acc + e);
52        let sh = widths.iter().fold(0.0f32, |acc: f32, e| acc + e);
53        for x in 0..i {
54            acc.x += widths[x];
55        }
56        for y in 0..j {
57            acc.y += heights[y];
58        }
59        acc.x /= sw;
60        acc.y /= sh;
61        acc *= self.wh;
62
63        acc += self.xy;
64        
65        let wh = vec2(widths[i]/sw, heights[j]/sh) * self.wh;
66        rectv(acc, wh)
67    }
68    pub fn fit_aspect(&self, a: f32) -> Rect {
69        let a_self = self.wh.x/self.wh.y;
70        if a_self > a {
71            // parent wider
72            rect((self.wh.x - self.wh.x*(1.0/a))/2.0, 0.0, self.wh.x*1.0/a, self.wh.y)
73        } else {
74            // child wider
75            rect(0.0, (self.wh.y - self.wh.y*(1.0/a))/2.0, self.wh.x, self.wh.y/a)
76        }
77    }
78    pub fn signed_distance(&self, p: Vec2) -> f32 {
79        let p = p - self.center();
80        let p = vec2(p.x.abs(), p.y.abs());
81        let d = p - (self.wh/2.0); // i guess its / 2
82        d.max(vec2(0.0, 0.0)).norm() + d.x.max(d.y).min(0.0)
83    }
84    /// grows by a fixed amount
85    pub fn grow(&self, x: f32, y: f32) -> Self {
86        rectc(self.center(), self.wh + vec2(x,y))
87    }
88}
89
90#[test]
91fn test_rect_rtw() {
92    let r = rect(-2.0, -2.0, 4.0, 4.0);
93    let v = vec2(0.25, 0.25);
94    let p = r.rect_to_world(v);
95    assert_eq!(p, vec2(-1.0, -1.0));
96}
97
98#[test]
99fn test_rect_wtr() {
100    let r = rect(4.0, 4.0, 1.0, 2.0);
101    let v = vec2(4.5, 5.0);
102    let p = r.world_to_rect(v);
103    assert_eq!(p, vec2(0.5, 0.5));
104}
105
106#[test]
107fn test_rect_contains() {
108    let r = rect(1.0, 1.0, 2.0, 2.0);
109    
110    // Test a point inside the rectangle
111    let inside_point = vec2(1.5, 1.5);
112    assert!(r.contains(inside_point));
113    
114    // Test a point outside the rectangle
115    let outside_point = vec2(3.5, 3.5);
116    assert!(!r.contains(outside_point));
117}
118
119#[test]
120fn test_rect_uniform_grid() {
121    let r = rect(0.0, 0.0, 4.0, 4.0);
122    let i = 1;
123    let j = 0;
124    let w = 2;
125    let h = 2;
126    let grid_rect = r.uniform_grid(i, j, w, h);
127    assert_eq!(grid_rect, rect(2.0, 0.0, 2.0, 2.0));
128}
129
130#[test]
131fn test_rect_grid() {
132    let r = rect(0.0, 0.0, 4.0, 4.0);
133    let i = 1;
134    let j = 1;
135    let widths = vec![1.0, 2.0, 1.0];
136    let heights = vec![0.5, 2.5, 1.0];
137    let grid_rect = r.grid(i, j, &widths, &heights);
138    assert_eq!(grid_rect, rect(1.0, 0.5, 2.0, 5.0 / 8.0 * 4.0));
139}
140
141#[test]
142fn test_rect_fit_aspect() {
143    let r = rect(0.0, 0.0, 4.0, 4.0);
144    let aspect = 2.0;
145    let fitted_rect = r.fit_aspect(aspect);
146    assert_eq!(fitted_rect, rect(0.0, 1.0, 4.0, 2.0));
147}
148#[test]
149fn test_sdf() {
150    let p = vec2(69.0, 420.0);
151    let r = rect(0.0, 0.0,69.0, 69.0);
152    assert_eq!(r.signed_distance(p), 351.0);
153    let p = vec2(2.0, 3.0);
154    let r = rect(-2.0, -2.0,1.0, 1.0);
155    assert_eq!(r.signed_distance(p), 5.0);
156}