1use std::ops::Mul;
2
3#[allow(unused)]
4use pax_runtime_api::math::Generic;
5use pax_runtime_api::math::{Point2, Space, TransformParts};
6use pax_runtime_api::{Interpolatable, Percent, Property, Rotation, Window};
7
8use crate::api::math::{Transform2, Vector2};
9use crate::api::{Axis, Size, Transform2D};
10use crate::node_interface::NodeLocal;
11
12pub fn compute_tab(
15 layout_properties: Property<LayoutProperties>,
16 extra_transform: Property<Option<Transform2D>>,
17 container_transform_and_bounds: Property<TransformAndBounds<NodeLocal, Window>>,
18) -> Property<TransformAndBounds<NodeLocal, Window>> {
19 let deps = [
24 layout_properties.untyped(),
25 container_transform_and_bounds.untyped(),
26 extra_transform.untyped(),
27 ];
28
29 Property::computed(
30 move || {
31 let container_t_and_b = container_transform_and_bounds.get();
32 layout_properties.read(|layout_properties| {
33 let transform_and_bounds =
34 calculate_transform_and_bounds(&layout_properties, container_t_and_b.clone());
35 let extra_transform = extra_transform.get();
36 if let Some(transform) = extra_transform {
37 transform.apply(transform_and_bounds)
38 } else {
39 transform_and_bounds
40 }
41 })
42 },
43 &deps,
44 )
45}
46
47pub fn calculate_transform_and_bounds(
48 LayoutProperties {
49 width,
50 height,
51 anchor_x,
52 anchor_y,
53 x,
54 y,
55 rotate,
56 scale_x,
57 scale_y,
58 skew_x,
59 skew_y,
60 }: &LayoutProperties,
61 TransformAndBounds {
62 transform: container_transform,
63 bounds: container_bounds,
64 }: TransformAndBounds<NodeLocal, Window>,
65) -> TransformAndBounds<NodeLocal, Window> {
66 let x = x.unwrap_or(Size::ZERO());
67 let y = y.unwrap_or(Size::ZERO());
68 let width = width
69 .map(|v| v.evaluate(container_bounds, Axis::X))
70 .unwrap_or(container_bounds.0);
71 let height = height
72 .map(|v| v.evaluate(container_bounds, Axis::Y))
73 .unwrap_or(container_bounds.1);
74 let origin = Vector2::new(
75 x.evaluate(container_bounds, Axis::X),
76 y.evaluate(container_bounds, Axis::Y),
77 );
78
79 let bounds = (width, height);
80
81 let anchor_x = anchor_x.unwrap_or_else(|| match x {
85 Size::Pixels(_) => Size::ZERO(),
86 Size::Combined(_, per) | Size::Percent(per) => {
87 if per.to_float() > 100.0 {
88 Size::default()
89 } else if per.to_float() < 0.0 {
90 Size::ZERO()
91 } else {
92 Size::Percent(per)
93 }
94 }
95 });
96
97 let anchor_y = anchor_y.unwrap_or_else(|| match y {
98 Size::Pixels(_) => Size::ZERO(),
99 Size::Combined(_, per) | Size::Percent(per) => {
100 if per.to_float() > 100.0 {
101 Size::default()
102 } else if per.to_float() < 0.0 {
103 Size::ZERO()
104 } else {
105 Size::Percent(per)
106 }
107 }
108 });
109
110 let anchor_transform = Transform2::translate(Vector2::new(
111 -anchor_x.evaluate(bounds, Axis::X),
112 -anchor_y.evaluate(bounds, Axis::Y),
113 ));
114
115 let scale = Vector2::new(
116 scale_x
117 .as_ref()
118 .map(|s| s.0.to_float() / 100.0)
119 .unwrap_or(1.0),
120 scale_y
121 .as_ref()
122 .map(|s| s.0.to_float() / 100.0)
123 .unwrap_or(1.0),
124 );
125
126 let skew = Vector2::new(
127 skew_x.map(|s| s.get_as_radians()).unwrap_or(0.0),
128 skew_y.map(|s| s.get_as_radians()).unwrap_or(0.0),
129 );
130
131 let rotation = rotate.map(|s| s.get_as_radians()).unwrap_or(0.0);
132
133 let parts = TransformParts {
134 origin,
135 scale,
136 skew,
137 rotation,
138 };
139
140 let combined_transform: Transform2<NodeLocal, NodeLocal> = parts.into();
141
142 TransformAndBounds {
143 transform: container_transform * combined_transform * anchor_transform,
144 bounds,
145 }
146}
147
148impl<F: Space, T: Space> Interpolatable for TransformAndBounds<F, T> {
151 fn interpolate(&self, other: &Self, t: f64) -> Self {
152 TransformAndBounds {
153 transform: self.transform.interpolate(&other.transform, t),
154 bounds: (
155 self.bounds.0 + (other.bounds.0 - self.bounds.0) * t,
156 self.bounds.1 + (other.bounds.1 - self.bounds.1) * t,
157 ),
158 }
159 }
160}
161
162#[derive(PartialEq)]
163pub struct TransformAndBounds<F, T = F> {
164 pub transform: Transform2<F, T>,
165 pub bounds: (f64, f64),
166}
167
168impl<F: Space, T: Space> std::fmt::Debug for TransformAndBounds<F, T> {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 f.debug_struct("TransformAndBounds")
171 .field("transform", &self.transform)
172 .field("bounds", &self.bounds)
173 .finish()
174 }
175}
176
177impl PartialEq for TransformAndBounds<NodeLocal, Window> {
178 fn eq(&self, other: &Self) -> bool {
179 self.transform == other.transform && self.bounds == other.bounds
180 }
181}
182
183impl<F: Space, T: Space> Default for TransformAndBounds<F, T> {
184 fn default() -> Self {
185 Self {
186 transform: Default::default(),
187 bounds: (100.0, 100.0),
188 }
189 }
190}
191
192impl<F, T> Clone for TransformAndBounds<F, T> {
193 fn clone(&self) -> Self {
194 Self {
195 transform: self.transform.clone(),
196 bounds: self.bounds.clone(),
197 }
198 }
199}
200
201impl<F, T> Copy for TransformAndBounds<F, T> {}
202
203impl<W1: Space, W2: Space, W3: Space> Mul<TransformAndBounds<W1, W2>>
204 for TransformAndBounds<W2, W3>
205{
206 type Output = TransformAndBounds<W1, W3>;
207
208 fn mul(self, rhs: TransformAndBounds<W1, W2>) -> Self::Output {
217 let s_s = Transform2::scale_sep(Vector2::new(self.bounds.0, self.bounds.1));
218 let r_s = Transform2::scale_sep(Vector2::new(rhs.bounds.0, rhs.bounds.1));
219
220 let s_t = self.transform * s_s;
221 let r_t = rhs.transform * r_s;
222 let res = s_t * r_t * s_s.inverse() * r_s.inverse();
223
224 TransformAndBounds {
225 transform: res,
226 bounds: (self.bounds.0 * rhs.bounds.0, self.bounds.1 * rhs.bounds.1),
227 }
228 }
229}
230
231impl<F: Space, T: Space> TransformAndBounds<F, T> {
232 pub fn center(&self) -> Point2<T> {
233 let (o, u, v) = self.transform.decompose();
234 let u = u * self.bounds.0;
235 let v = v * self.bounds.1;
236 o + v / 2.0 + u / 2.0
237 }
238
239 pub fn corners(&self) -> [Point2<T>; 4] {
240 let (o, u, v) = self.transform.decompose();
241 let u = u * self.bounds.0;
242 let v = v * self.bounds.1;
243 [o, o + v, o + u + v, o + u]
244 }
245
246 pub fn contains_point(&self, point: Point2<T>) -> bool {
247 self.as_transform().contains_point(point)
248 }
249
250 pub fn as_pure_size(self) -> Self {
251 let mut parts: TransformParts = self.transform.into();
252 let bounds_x = std::mem::replace(&mut parts.scale.x, 1.0);
253 let bounds_y = std::mem::replace(&mut parts.scale.y, 1.0);
254 TransformAndBounds {
255 transform: parts.into(),
256 bounds: (self.bounds.0 * bounds_x, self.bounds.1 * bounds_y),
257 }
258 }
259 pub fn as_pure_scale(self) -> Self {
260 TransformAndBounds {
261 transform: self.transform
262 * Transform2::scale_sep(Vector2::new(self.bounds.0, self.bounds.1)),
263 bounds: (1.0, 1.0),
264 }
265 }
266
267 pub fn cast_spaces<A: Space, B: Space>(self) -> TransformAndBounds<A, B> {
268 TransformAndBounds {
269 transform: self.transform.cast_spaces(),
270 bounds: self.bounds,
271 }
272 }
273
274 pub fn as_transform(&self) -> Transform2<F, T> {
275 self.transform * Transform2::scale_sep(Vector2::new(self.bounds.0, self.bounds.1))
276 }
277}
278
279#[test]
280fn test_transform_and_bounds_mult() {
281 let dvx = 0.6;
282 let dvy = 0.3;
283
284 let t_and_b_with_scale = TransformAndBounds::<Generic> {
285 transform: Transform2::new([1.5, 1.1, 2.3, 3.2, 1.2, 1.0])
286 * Transform2::scale_sep(Vector2::<Generic>::new(dvx, dvy)),
287 bounds: (2.1, 1.2),
288 };
289
290 let t_and_b_with_size = TransformAndBounds::<Generic> {
291 transform: Transform2::new([1.5, 1.1, 2.3, 3.2, 1.2, 1.0]),
292 bounds: (2.1 * dvx, 1.2 * dvy),
293 };
294 let some_other_transform = TransformAndBounds::<Generic> {
295 transform: Transform2::new([1.1, 1.2, 5.3, 9.2, 1.0, 2.0]),
296 bounds: (1.9, 4.5),
297 };
298
299 let res_scale = t_and_b_with_scale * some_other_transform;
300 let res_size = t_and_b_with_size * some_other_transform;
301
302 let t_scale = res_scale.transform
303 * Transform2::<Generic>::scale_sep(Vector2::new(res_scale.bounds.0, res_scale.bounds.1));
304 let t_scale_c = t_scale.coeffs();
305 let t_size = res_size.transform
306 * Transform2::<Generic>::scale_sep(Vector2::new(res_size.bounds.0, res_size.bounds.1));
307 let t_size_c = t_size.coeffs();
308 let diff_sum = t_scale_c
309 .iter()
310 .zip(t_size_c)
311 .map(|(a, b)| (a - b).abs())
312 .sum::<f64>();
313 assert!(diff_sum < 1e-4);
314
315 let res_scale_other_way = some_other_transform * t_and_b_with_scale;
316 let res_size_other_way = some_other_transform * t_and_b_with_size;
317
318 let t_scale = res_scale_other_way.transform
319 * Transform2::<Generic>::scale_sep(Vector2::new(
320 res_scale_other_way.bounds.0,
321 res_scale_other_way.bounds.1,
322 ));
323 let t_scale_c = t_scale.coeffs();
324 let t_size = res_size_other_way.transform
325 * Transform2::<Generic>::scale_sep(Vector2::new(
326 res_size_other_way.bounds.0,
327 res_size_other_way.bounds.1,
328 ));
329 let t_size_c = t_size.coeffs();
330 let diff_sum = t_scale_c
331 .iter()
332 .zip(t_size_c)
333 .map(|(a, b)| (a - b).abs())
334 .sum::<f64>();
335 assert!(diff_sum < 1e-4);
336}
337
338impl Interpolatable for LayoutProperties {}
339
340#[derive(Debug, Default, Clone)]
341pub struct LayoutProperties {
342 pub x: Option<Size>,
343 pub y: Option<Size>,
344 pub width: Option<Size>,
345 pub height: Option<Size>,
346 pub rotate: Option<Rotation>,
347 pub scale_x: Option<Percent>,
348 pub scale_y: Option<Percent>,
349 pub anchor_x: Option<Size>,
350 pub anchor_y: Option<Size>,
351 pub skew_x: Option<Rotation>,
352 pub skew_y: Option<Rotation>,
353}
354
355impl LayoutProperties {
356 pub fn fill() -> Self {
357 Self {
358 x: Some(Size::ZERO()),
359 y: Some(Size::ZERO()),
360 width: Some(Size::default()),
361 height: Some(Size::default()),
362 rotate: Some(Rotation::ZERO()),
363 scale_x: Some(Percent(100.into())),
364 scale_y: Some(Percent(100.into())),
365 anchor_x: None,
366 anchor_y: None,
367 skew_x: Some(Rotation::ZERO()),
368 skew_y: Some(Rotation::ZERO()),
369 }
370 }
371}
372
373impl<F: Space, T: Space> TransformAndBounds<F, T> {
374 pub fn inverse(&self) -> TransformAndBounds<T, F> {
375 let t_inv = self.transform.inverse();
376 let b_inv = (1.0 / self.bounds.0, 1.0 / self.bounds.1);
377 TransformAndBounds {
378 transform: t_inv,
379 bounds: b_inv,
380 }
381 }
382
383 pub fn intersects(&self, other: &Self) -> bool {
385 let corners_self = self.corners();
386 let corners_other = other.corners();
387
388 for i in 0..2 {
389 let axis = (corners_self[i] - corners_self[(i + 1) % 4]).normal();
390
391 let self_projections: Vec<_> = corners_self
392 .iter()
393 .map(|&p| p.to_vector().project_onto(axis).length())
394 .collect();
395 let other_projections: Vec<_> = corners_other
396 .iter()
397 .map(|&p| p.to_vector().project_onto(axis).length())
398 .collect();
399
400 let (min_self, max_self) = min_max_projections(&self_projections);
401 let (min_other, max_other) = min_max_projections(&other_projections);
402
403 if max_self < min_other || max_other < min_self {
405 return false;
407 }
408 }
409 true
410 }
411}
412
413fn min_max_projections(projections: &[f64]) -> (f64, f64) {
414 let min_projection = *projections
415 .iter()
416 .min_by(|a, b| a.partial_cmp(b).unwrap())
417 .unwrap();
418 let max_projection = *projections
419 .iter()
420 .max_by(|a, b| a.partial_cmp(b).unwrap())
421 .unwrap();
422 (min_projection, max_projection)
423}
424
425pub trait ComputableTransform<F, T> {
426 fn apply(&self, bounds: TransformAndBounds<F, T>) -> TransformAndBounds<F, T>;
427}
428
429impl ComputableTransform<NodeLocal, Window> for Transform2D {
430 fn apply(
431 &self,
432 bounds: TransformAndBounds<NodeLocal, Window>,
433 ) -> TransformAndBounds<NodeLocal, Window> {
434 let layout_properties = LayoutProperties {
435 x: self.translate.map(|v| v[0]),
436 y: self.translate.map(|v| v[1]),
437 width: Some(Size::Pixels(bounds.bounds.0.into())),
438 height: Some(Size::Pixels(bounds.bounds.1.into())),
439 rotate: self.rotate.clone(),
440 scale_x: self
441 .scale
442 .as_ref()
443 .map(|v| Percent((100.0 * v[0].clone().expect_percent()).into())),
444 scale_y: self
445 .scale
446 .as_ref()
447 .map(|v| Percent((100.0 * v[1].clone().expect_percent()).into())),
448 anchor_x: self.anchor.map(|v| v[0]),
449 anchor_y: self.anchor.map(|v| v[1]),
450 skew_x: self.skew.as_ref().map(|v| v[0].clone()),
451 skew_y: self.skew.as_ref().map(|v| v[1].clone()),
452 };
453
454 let curr = calculate_transform_and_bounds(&layout_properties, bounds.clone());
455 match &self.previous {
456 Some(previous) => (*previous).apply(curr),
457 None => curr,
458 }
459 }
460}