1use crate::{
2 GraphicsLayer, Point, Rect,
3 space::{LayerSpace, Mat4, MeshSurface, Scene3d},
4 transform::{ProjectiveTransform, quad_bounds},
5};
6
7#[doc(hidden)]
8#[inline]
9pub fn layer_scale_x(layer: &GraphicsLayer) -> f32 {
10 layer.scale * layer.scale_x
11}
12
13#[doc(hidden)]
14#[inline]
15pub fn layer_scale_y(layer: &GraphicsLayer) -> f32 {
16 layer.scale * layer.scale_y
17}
18
19pub fn layer_uniform_scale(layer: &GraphicsLayer) -> f32 {
21 layer_scale_x(layer).min(layer_scale_y(layer))
22}
23
24#[doc(hidden)]
25#[inline]
26pub fn layer_affine_is_identity(layer: &GraphicsLayer) -> bool {
27 layer.scale == 1.0
28 && layer.scale_x == 1.0
29 && layer.scale_y == 1.0
30 && layer.translation_x == 0.0
31 && layer.translation_y == 0.0
32}
33
34pub fn apply_layer_affine_to_rect(rect: Rect, layer_bounds: Rect, layer: &GraphicsLayer) -> Rect {
36 if layer_affine_is_identity(layer) {
37 return rect;
38 }
39 let scale_x = layer_scale_x(layer);
40 let scale_y = layer_scale_y(layer);
41 let (pivot_x, pivot_y) = layer_rotation_pivot(layer_bounds, layer);
42 Rect {
43 x: pivot_x + (rect.x - pivot_x) * scale_x + layer.translation_x,
44 y: pivot_y + (rect.y - pivot_y) * scale_y + layer.translation_y,
45 width: rect.width * scale_x,
46 height: rect.height * scale_y,
47 }
48}
49
50pub fn apply_layer_affine_to_point(
55 point: Point,
56 layer_bounds: Rect,
57 layer: &GraphicsLayer,
58) -> Point {
59 if layer_affine_is_identity(layer) {
60 return point;
61 }
62 let (pivot_x, pivot_y) = layer_rotation_pivot(layer_bounds, layer);
63 Point::new(
64 pivot_x + (point.x - pivot_x) * layer_scale_x(layer) + layer.translation_x,
65 pivot_y + (point.y - pivot_y) * layer_scale_y(layer) + layer.translation_y,
66 )
67}
68
69fn layer_rotation_pivot(layer_bounds: Rect, layer: &GraphicsLayer) -> (f32, f32) {
70 (
71 layer_bounds.x + layer_bounds.width * layer.transform_origin.pivot_fraction_x,
72 layer_bounds.y + layer_bounds.height * layer.transform_origin.pivot_fraction_y,
73 )
74}
75
76#[doc(hidden)]
77#[inline]
78pub fn layer_scales_or_rotates(layer: &GraphicsLayer) -> bool {
79 layer_scale_x(layer) != 1.0 || layer_scale_y(layer) != 1.0 || layer_has_rotation(layer)
80}
81
82#[inline]
83fn layer_has_rotation(layer: &GraphicsLayer) -> bool {
84 layer.rotation_x.abs() > f32::EPSILON
85 || layer.rotation_y.abs() > f32::EPSILON
86 || layer.rotation_z.abs() > f32::EPSILON
87}
88
89fn rotation_axes(layer: &GraphicsLayer) -> [[f32; 3]; 2] {
90 let (sin_x, cos_x) = layer.rotation_x.to_radians().sin_cos();
91 let (sin_y, cos_y) = layer.rotation_y.to_radians().sin_cos();
92 let (sin_z, cos_z) = layer.rotation_z.to_radians().sin_cos();
93 [
94 [cos_z * cos_y, sin_z * cos_y, -sin_y],
95 [
96 cos_z * sin_x * sin_y - sin_z * cos_x,
97 sin_z * sin_x * sin_y + cos_z * cos_x,
98 sin_x * cos_y,
99 ],
100 ]
101}
102
103fn camera_distance(layer: &GraphicsLayer) -> f32 {
104 const CAMERA_DISTANCE_SCALE: f32 = 72.0;
105 (layer.camera_distance * CAMERA_DISTANCE_SCALE).max(1.0)
106}
107
108fn apply_rotation_and_perspective(
109 point: [f32; 2],
110 pivot: (f32, f32),
111 layer: &GraphicsLayer,
112) -> [f32; 2] {
113 if !layer_has_rotation(layer) {
114 return point;
115 }
116 let (x, y) = (point[0] - pivot.0, point[1] - pivot.1);
117 let [x_axis, y_axis] = rotation_axes(layer);
118 let [turned_x, turned_y, turned_z] = [0, 1, 2].map(|axis| x * x_axis[axis] + y * y_axis[axis]);
119 let camera_distance = camera_distance(layer);
120 let perspective = camera_distance / (camera_distance - turned_z).max(1.0);
121 [
122 pivot.0 + turned_x * perspective,
123 pivot.1 + turned_y * perspective,
124 ]
125}
126
127fn apply_layer_to_point(point: [f32; 2], pivot: (f32, f32), layer: &GraphicsLayer) -> [f32; 2] {
128 let scaled = [
129 pivot.0 + (point[0] - pivot.0) * layer_scale_x(layer),
130 pivot.1 + (point[1] - pivot.1) * layer_scale_y(layer),
131 ];
132 let rotated = apply_rotation_and_perspective(scaled, pivot, layer);
133 [
134 rotated[0] + layer.translation_x,
135 rotated[1] + layer.translation_y,
136 ]
137}
138
139#[inline]
141pub fn apply_layer_to_quad(rect: Rect, layer_bounds: Rect, layer: &GraphicsLayer) -> [[f32; 2]; 4] {
142 let quad = [
143 [rect.x, rect.y],
144 [rect.x + rect.width, rect.y],
145 [rect.x, rect.y + rect.height],
146 [rect.x + rect.width, rect.y + rect.height],
147 ];
148 if layer_affine_is_identity(layer) && !layer_has_rotation(layer) {
149 return quad;
150 }
151 let pivot = layer_rotation_pivot(layer_bounds, layer);
152 quad.map(|point| apply_layer_to_point(point, pivot, layer))
153}
154
155#[inline]
157pub fn apply_layer_to_rect(rect: Rect, layer_bounds: Rect, layer: &GraphicsLayer) -> Rect {
158 let Rect {
159 x,
160 y,
161 width,
162 height,
163 } = rect;
164 if layer_affine_is_identity(layer)
168 && !layer_has_rotation(layer)
169 && width >= 0.0
170 && height >= 0.0
171 && crate::float::all_finite([x, y, width, height])
172 {
173 return Rect {
174 x,
175 y,
176 width: (x + width) - x,
177 height: (y + height) - y,
178 };
179 }
180 quad_bounds(apply_layer_to_quad(rect, layer_bounds, layer))
181}
182
183pub fn layer_transform_to_parent(
185 local_bounds: Rect,
186 placement: Point,
187 layer: &GraphicsLayer,
188) -> ProjectiveTransform {
189 if !layer_scales_or_rotates(layer) {
190 return ProjectiveTransform::translation(
191 placement.x + layer.translation_x,
192 placement.y + layer.translation_y,
193 );
194 }
195 let (pivot_x, pivot_y) = layer_rotation_pivot(local_bounds, layer);
196 let [x_axis, y_axis] = rotation_axes(layer);
197 let (scale_x, scale_y) = (layer_scale_x(layer), layer_scale_y(layer));
198 let camera_distance = camera_distance(layer);
199 let turn = ProjectiveTransform::from_homogeneous([
200 [x_axis[0] * scale_x, y_axis[0] * scale_y, 0.0],
201 [x_axis[1] * scale_x, y_axis[1] * scale_y, 0.0],
202 [
203 -x_axis[2] * scale_x / camera_distance,
204 -y_axis[2] * scale_y / camera_distance,
205 1.0,
206 ],
207 ]);
208 let placed = ProjectiveTransform::translation(-pivot_x, -pivot_y)
209 .then(turn)
210 .then(ProjectiveTransform::translation(pivot_x, pivot_y))
211 .then(ProjectiveTransform::translation(
212 placement.x + layer.translation_x,
213 placement.y + layer.translation_y,
214 ));
215 ProjectiveTransform::from_homogeneous(placed.matrix())
216}
217
218#[inline]
222pub fn layer_transform_to_window(
223 parent: ProjectiveTransform,
224 origin: Point,
225 bounds: Rect,
226 layer: &GraphicsLayer,
227) -> ProjectiveTransform {
228 if layer_affine_is_identity(layer) && !layer_has_rotation(layer) {
229 return parent;
230 }
231 let bounds = Rect {
232 x: bounds.x + origin.x,
233 y: bounds.y + origin.y,
234 ..bounds
235 };
236 layer_transform_to_parent(bounds, Point::default(), layer).then(parent)
237}
238
239pub fn layer_scene(layer: &GraphicsLayer) -> Option<&Scene3d> {
242 match layer.space.as_deref()? {
243 LayerSpace::Scene(scene) => Some(scene),
244 LayerSpace::Surface(_) | LayerSpace::Transform(_) => None,
245 }
246}
247
248pub fn layer_surface(layer: &GraphicsLayer) -> Option<&MeshSurface> {
251 match layer.space.as_deref()? {
252 LayerSpace::Surface(surface) => Some(surface),
253 LayerSpace::Scene(_) | LayerSpace::Transform(_) => None,
254 }
255}
256
257pub fn layer_transform3d(layer: &GraphicsLayer) -> Option<&Mat4> {
260 match layer.space.as_deref()? {
261 LayerSpace::Transform(transform) => Some(transform),
262 LayerSpace::Scene(_) | LayerSpace::Surface(_) => None,
263 }
264}