Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui/src/ui_transform.rs
6849 views
1
use crate::Val;
2
use bevy_derive::Deref;
3
use bevy_ecs::component::Component;
4
use bevy_ecs::prelude::ReflectComponent;
5
use bevy_math::Affine2;
6
use bevy_math::Mat2;
7
use bevy_math::Rot2;
8
use bevy_math::Vec2;
9
use bevy_reflect::prelude::*;
10
use core::ops::Mul;
11
12
/// A pair of [`Val`]s used to represent a 2-dimensional size or offset.
13
#[derive(Debug, PartialEq, Clone, Copy, Reflect)]
14
#[reflect(Default, PartialEq, Debug, Clone)]
15
#[cfg_attr(
16
feature = "serialize",
17
derive(serde::Serialize, serde::Deserialize),
18
reflect(Serialize, Deserialize)
19
)]
20
pub struct Val2 {
21
/// Translate the node along the x-axis.
22
/// `Val::Percent` values are resolved based on the computed width of the Ui Node.
23
/// `Val::Auto` is resolved to `0.`.
24
pub x: Val,
25
/// Translate the node along the y-axis.
26
/// `Val::Percent` values are resolved based on the computed height of the UI Node.
27
/// `Val::Auto` is resolved to `0.`.
28
pub y: Val,
29
}
30
31
impl Val2 {
32
pub const ZERO: Self = Self {
33
x: Val::ZERO,
34
y: Val::ZERO,
35
};
36
37
/// Creates a new [`Val2`] where both components are in logical pixels
38
pub const fn px(x: f32, y: f32) -> Self {
39
Self {
40
x: Val::Px(x),
41
y: Val::Px(y),
42
}
43
}
44
45
/// Creates a new [`Val2`] where both components are percentage values
46
pub const fn percent(x: f32, y: f32) -> Self {
47
Self {
48
x: Val::Percent(x),
49
y: Val::Percent(y),
50
}
51
}
52
53
/// Creates a new [`Val2`]
54
pub const fn new(x: Val, y: Val) -> Self {
55
Self { x, y }
56
}
57
58
/// Resolves this [`Val2`] from the given `scale_factor`, `parent_size`,
59
/// and `viewport_size`.
60
///
61
/// Component values of [`Val::Auto`] are resolved to 0.
62
pub fn resolve(&self, scale_factor: f32, base_size: Vec2, viewport_size: Vec2) -> Vec2 {
63
Vec2::new(
64
self.x
65
.resolve(scale_factor, base_size.x, viewport_size)
66
.unwrap_or(0.),
67
self.y
68
.resolve(scale_factor, base_size.y, viewport_size)
69
.unwrap_or(0.),
70
)
71
}
72
}
73
74
impl Default for Val2 {
75
fn default() -> Self {
76
Self::ZERO
77
}
78
}
79
80
/// Relative 2D transform for UI nodes
81
///
82
/// [`UiGlobalTransform`] is automatically inserted whenever [`UiTransform`] is inserted.
83
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
84
#[reflect(Component, Default, PartialEq, Debug, Clone)]
85
#[cfg_attr(
86
feature = "serialize",
87
derive(serde::Serialize, serde::Deserialize),
88
reflect(Serialize, Deserialize)
89
)]
90
#[require(UiGlobalTransform)]
91
pub struct UiTransform {
92
/// Translate the node.
93
pub translation: Val2,
94
/// Scale the node. A negative value reflects the node in that axis.
95
pub scale: Vec2,
96
/// Rotate the node clockwise.
97
pub rotation: Rot2,
98
}
99
100
impl UiTransform {
101
pub const IDENTITY: Self = Self {
102
translation: Val2::ZERO,
103
scale: Vec2::ONE,
104
rotation: Rot2::IDENTITY,
105
};
106
107
/// Creates a UI transform representing a rotation.
108
pub const fn from_rotation(rotation: Rot2) -> Self {
109
Self {
110
rotation,
111
..Self::IDENTITY
112
}
113
}
114
115
/// Creates a UI transform representing a responsive translation.
116
pub const fn from_translation(translation: Val2) -> Self {
117
Self {
118
translation,
119
..Self::IDENTITY
120
}
121
}
122
123
/// Creates a UI transform representing a scaling.
124
pub const fn from_scale(scale: Vec2) -> Self {
125
Self {
126
scale,
127
..Self::IDENTITY
128
}
129
}
130
131
/// Resolves the translation from the given `scale_factor`, `base_value`, and `target_size`
132
/// and returns a 2d affine transform from the resolved translation, and the `UiTransform`'s rotation, and scale.
133
pub fn compute_affine(&self, scale_factor: f32, base_size: Vec2, target_size: Vec2) -> Affine2 {
134
Affine2::from_mat2_translation(
135
Mat2::from(self.rotation) * Mat2::from_diagonal(self.scale),
136
self.translation
137
.resolve(scale_factor, base_size, target_size),
138
)
139
}
140
}
141
142
impl Default for UiTransform {
143
fn default() -> Self {
144
Self::IDENTITY
145
}
146
}
147
148
/// Absolute 2D transform for UI nodes
149
///
150
/// [`UiGlobalTransform`]s are updated from [`UiTransform`] and [`Node`](crate::ui_node::Node)
151
/// in [`ui_layout_system`](crate::layout::ui_layout_system)
152
#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect, Deref)]
153
#[reflect(Component, Default, PartialEq, Debug, Clone)]
154
#[cfg_attr(
155
feature = "serialize",
156
derive(serde::Serialize, serde::Deserialize),
157
reflect(Serialize, Deserialize)
158
)]
159
pub struct UiGlobalTransform(Affine2);
160
161
impl Default for UiGlobalTransform {
162
fn default() -> Self {
163
Self(Affine2::IDENTITY)
164
}
165
}
166
167
impl UiGlobalTransform {
168
/// If the transform is invertible returns its inverse.
169
/// Otherwise returns `None`.
170
#[inline]
171
pub fn try_inverse(&self) -> Option<Affine2> {
172
(self.matrix2.determinant() != 0.).then_some(self.inverse())
173
}
174
175
/// Creates a `UiGlobalTransform` from the given 2D translation.
176
#[inline]
177
pub fn from_translation(translation: Vec2) -> Self {
178
Self(Affine2::from_translation(translation))
179
}
180
181
/// Creates a `UiGlobalTransform` from the given 2D translation.
182
#[inline]
183
pub fn from_xy(x: f32, y: f32) -> Self {
184
Self::from_translation(Vec2::new(x, y))
185
}
186
187
/// Creates a `UiGlobalTransform` from the given rotation.
188
#[inline]
189
pub fn from_rotation(rotation: Rot2) -> Self {
190
Self(Affine2::from_mat2(rotation.into()))
191
}
192
193
/// Creates a `UiGlobalTransform` from the given scaling.
194
#[inline]
195
pub fn from_scale(scale: Vec2) -> Self {
196
Self(Affine2::from_scale(scale))
197
}
198
199
/// Extracts scale, angle and translation from self.
200
/// The transform is expected to be non-degenerate and without shearing, or the output will be invalid.
201
#[inline]
202
pub fn to_scale_angle_translation(&self) -> (Vec2, f32, Vec2) {
203
self.0.to_scale_angle_translation()
204
}
205
206
/// Returns the transform as an [`Affine2`]
207
#[inline]
208
pub fn affine(&self) -> Affine2 {
209
self.0
210
}
211
}
212
213
impl From<Affine2> for UiGlobalTransform {
214
fn from(value: Affine2) -> Self {
215
Self(value)
216
}
217
}
218
219
impl From<UiGlobalTransform> for Affine2 {
220
fn from(value: UiGlobalTransform) -> Self {
221
value.0
222
}
223
}
224
225
impl From<&UiGlobalTransform> for Affine2 {
226
fn from(value: &UiGlobalTransform) -> Self {
227
value.0
228
}
229
}
230
231
impl Mul for UiGlobalTransform {
232
type Output = Self;
233
234
#[inline]
235
fn mul(self, value: Self) -> Self::Output {
236
Self(self.0 * value.0)
237
}
238
}
239
240
impl Mul<Affine2> for UiGlobalTransform {
241
type Output = Affine2;
242
243
#[inline]
244
fn mul(self, affine2: Affine2) -> Self::Output {
245
self.0 * affine2
246
}
247
}
248
249
impl Mul<UiGlobalTransform> for Affine2 {
250
type Output = Affine2;
251
252
#[inline]
253
fn mul(self, transform: UiGlobalTransform) -> Self::Output {
254
self * transform.0
255
}
256
}
257
258
impl Mul<Vec2> for UiGlobalTransform {
259
type Output = Vec2;
260
261
#[inline]
262
fn mul(self, value: Vec2) -> Vec2 {
263
self.transform_point2(value)
264
}
265
}
266
267