1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Viewport context for resolving viewport-relative units.
//!
//! Provides context information needed to resolve viewport-relative length units
//! (vw, vh, vmin, vmax) to absolute pixel values.
use astrelis_core::math::Vec2;
/// Context for resolving viewport-relative units.
///
/// Stores the viewport dimensions needed to convert viewport-relative length units
/// (vw, vh, vmin, vmax) into absolute pixel values.
///
/// # Examples
/// ```
/// use astrelis_ui::viewport_context::ViewportContext;
/// use astrelis_core::math::Vec2;
///
/// let ctx = ViewportContext::new(Vec2::new(1280.0, 720.0));
/// assert_eq!(ctx.viewport_size().x, 1280.0);
/// assert_eq!(ctx.viewport_size().y, 720.0);
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ViewportContext {
/// Viewport dimensions (width, height) in pixels
viewport_size: Vec2,
}
impl ViewportContext {
/// Create a new viewport context.
///
/// # Arguments
/// * `viewport_size` - The viewport dimensions (width, height) in pixels
///
/// # Examples
/// ```
/// use astrelis_ui::viewport_context::ViewportContext;
/// use astrelis_core::math::Vec2;
///
/// let ctx = ViewportContext::new(Vec2::new(1920.0, 1080.0));
/// ```
pub fn new(viewport_size: Vec2) -> Self {
Self { viewport_size }
}
/// Get the viewport size.
pub fn viewport_size(&self) -> Vec2 {
self.viewport_size
}
/// Get the viewport width.
pub fn width(&self) -> f32 {
self.viewport_size.x
}
/// Get the viewport height.
pub fn height(&self) -> f32 {
self.viewport_size.y
}
/// Get the smaller viewport dimension.
pub fn min_dimension(&self) -> f32 {
self.viewport_size.x.min(self.viewport_size.y)
}
/// Get the larger viewport dimension.
pub fn max_dimension(&self) -> f32 {
self.viewport_size.x.max(self.viewport_size.y)
}
/// Get the aspect ratio (width / height).
pub fn aspect_ratio(&self) -> f32 {
if self.viewport_size.y != 0.0 {
self.viewport_size.x / self.viewport_size.y
} else {
1.0
}
}
/// Create a viewport context from window dimensions.
///
/// Convenience constructor for common use cases.
pub fn from_window_size(width: f32, height: f32) -> Self {
Self::new(Vec2::new(width, height))
}
}
impl Default for ViewportContext {
/// Default viewport context with 1280x720 dimensions.
fn default() -> Self {
Self::new(Vec2::new(1280.0, 720.0))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_viewport_context_new() {
let ctx = ViewportContext::new(Vec2::new(1920.0, 1080.0));
assert_eq!(ctx.viewport_size(), Vec2::new(1920.0, 1080.0));
assert_eq!(ctx.width(), 1920.0);
assert_eq!(ctx.height(), 1080.0);
}
#[test]
fn test_viewport_context_dimensions() {
let ctx = ViewportContext::new(Vec2::new(1280.0, 720.0));
assert_eq!(ctx.min_dimension(), 720.0);
assert_eq!(ctx.max_dimension(), 1280.0);
assert!((ctx.aspect_ratio() - 1.777777).abs() < 0.001);
}
#[test]
fn test_viewport_context_square() {
let ctx = ViewportContext::new(Vec2::new(800.0, 800.0));
assert_eq!(ctx.min_dimension(), 800.0);
assert_eq!(ctx.max_dimension(), 800.0);
assert_eq!(ctx.aspect_ratio(), 1.0);
}
#[test]
fn test_viewport_context_from_window_size() {
let ctx = ViewportContext::from_window_size(1024.0, 768.0);
assert_eq!(ctx.width(), 1024.0);
assert_eq!(ctx.height(), 768.0);
}
#[test]
fn test_viewport_context_default() {
let ctx = ViewportContext::default();
assert_eq!(ctx.width(), 1280.0);
assert_eq!(ctx.height(), 720.0);
}
#[test]
fn test_aspect_ratio_zero_height() {
let ctx = ViewportContext::new(Vec2::new(1920.0, 0.0));
assert_eq!(ctx.aspect_ratio(), 1.0); // Should not panic
}
}