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
20impl From<macroquad::prelude::Vec2> for Vector2 {
21 fn from(value: macroquad::prelude::Vec2) -> Self {
22 Self::new(value.x, value.y)
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Default)]
27#[repr(C)]
28pub struct Dimensions {
29 pub width: f32,
30 pub height: f32,
31}
32
33impl Dimensions {
34 pub fn new(width: f32, height: f32) -> Self {
35 Self { width, height }
36 }
37}
38
39impl From<(f32, f32)> for Dimensions {
40 fn from(value: (f32, f32)) -> Self {
41 Self::new(value.0, value.1)
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Default)]
47#[repr(C)]
48pub struct BoundingBox {
49 pub x: f32,
50 pub y: f32,
51 pub width: f32,
52 pub height: f32,
53}
54
55impl BoundingBox {
56 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
57 Self {
58 x,
59 y,
60 width,
61 height,
62 }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq)]
68pub enum AngleType {
69 Zero,
71 Right90,
73 Straight180,
75 Right270,
77 Arbitrary(f32),
79}
80
81pub fn classify_angle(radians: f32) -> AngleType {
84 let normalized = radians.rem_euclid(std::f32::consts::TAU);
85 const EPS: f32 = 0.001;
86 if normalized < EPS || (std::f32::consts::TAU - normalized) < EPS {
87 AngleType::Zero
88 } else if (normalized - std::f32::consts::FRAC_PI_2).abs() < EPS {
89 AngleType::Right90
90 } else if (normalized - std::f32::consts::PI).abs() < EPS {
91 AngleType::Straight180
92 } else if (normalized - 3.0 * std::f32::consts::FRAC_PI_2).abs() < EPS {
93 AngleType::Right270
94 } else {
95 AngleType::Arbitrary(normalized)
96 }
97}
98
99use crate::layout::CornerRadius;
100
101pub fn compute_rotated_aabb(
110 width: f32,
111 height: f32,
112 corner_radius: &CornerRadius,
113 rotation_radians: f32,
114) -> (f32, f32) {
115 let angle = classify_angle(rotation_radians);
116 match angle {
117 AngleType::Zero => (width, height),
118 AngleType::Straight180 => (width, height),
119 AngleType::Right90 | AngleType::Right270 => (height, width),
120 AngleType::Arbitrary(theta) => {
121 let r = corner_radius
122 .top_left
123 .max(corner_radius.top_right)
124 .max(corner_radius.bottom_left)
125 .max(corner_radius.bottom_right)
126 .min(width / 2.0)
127 .min(height / 2.0);
128
129 let cos_t = theta.cos().abs();
130 let sin_t = theta.sin().abs();
131 let inner_w = (width - 2.0 * r).max(0.0);
132 let inner_h = (height - 2.0 * r).max(0.0);
133
134 let eff_w = inner_w * cos_t + inner_h * sin_t + 2.0 * r;
135 let eff_h = inner_w * sin_t + inner_h * cos_t + 2.0 * r;
136 (eff_w, eff_h)
137 }
138 }
139}