1#[derive(Debug, Clone, Copy, PartialEq, Default)]
2#[repr(C)]
3pub struct Vector2 {
4 pub x: f32,
5 pub y: f32,
6}
7
8impl Vector2 {
9 pub fn new(x: f32, y: f32) -> Self {
10 Self { x, y }
11 }
12}
13
14impl From<(f32, f32)> for Vector2 {
15 fn from(value: (f32, f32)) -> Self {
16 Self::new(value.0, value.1)
17 }
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Default)]
21#[repr(C)]
22pub struct Dimensions {
23 pub width: f32,
24 pub height: f32,
25}
26
27impl Dimensions {
28 pub fn new(width: f32, height: f32) -> Self {
29 Self { width, height }
30 }
31}
32
33impl From<(f32, f32)> for Dimensions {
34 fn from(value: (f32, f32)) -> Self {
35 Self::new(value.0, value.1)
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Default)]
41#[repr(C)]
42pub struct BoundingBox {
43 pub x: f32,
44 pub y: f32,
45 pub width: f32,
46 pub height: f32,
47}
48
49impl BoundingBox {
50 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
51 Self {
52 x,
53 y,
54 width,
55 height,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq)]
62pub enum AngleType {
63 Zero,
65 Right90,
67 Straight180,
69 Right270,
71 Arbitrary(f32),
73}
74
75pub fn classify_angle(radians: f32) -> AngleType {
78 let normalized = radians.rem_euclid(std::f32::consts::TAU);
79 const EPS: f32 = 0.001;
80 if normalized < EPS || (std::f32::consts::TAU - normalized) < EPS {
81 AngleType::Zero
82 } else if (normalized - std::f32::consts::FRAC_PI_2).abs() < EPS {
83 AngleType::Right90
84 } else if (normalized - std::f32::consts::PI).abs() < EPS {
85 AngleType::Straight180
86 } else if (normalized - 3.0 * std::f32::consts::FRAC_PI_2).abs() < EPS {
87 AngleType::Right270
88 } else {
89 AngleType::Arbitrary(normalized)
90 }
91}
92
93use crate::layout::CornerRadius;
94
95pub fn compute_rotated_aabb(
104 width: f32,
105 height: f32,
106 corner_radius: &CornerRadius,
107 rotation_radians: f32,
108) -> (f32, f32) {
109 let angle = classify_angle(rotation_radians);
110 match angle {
111 AngleType::Zero => (width, height),
112 AngleType::Straight180 => (width, height),
113 AngleType::Right90 | AngleType::Right270 => (height, width),
114 AngleType::Arbitrary(theta) => {
115 let r = corner_radius
116 .top_left
117 .max(corner_radius.top_right)
118 .max(corner_radius.bottom_left)
119 .max(corner_radius.bottom_right)
120 .min(width / 2.0)
121 .min(height / 2.0);
122
123 let cos_t = theta.cos().abs();
124 let sin_t = theta.sin().abs();
125 let inner_w = (width - 2.0 * r).max(0.0);
126 let inner_h = (height - 2.0 * r).max(0.0);
127
128 let eff_w = inner_w * cos_t + inner_h * sin_t + 2.0 * r;
129 let eff_h = inner_w * sin_t + inner_h * cos_t + 2.0 * r;
130 (eff_w, eff_h)
131 }
132 }
133}