1pub use zng_unit::*;
4
5mod alignment;
6pub use alignment::*;
7
8mod constraints;
9pub use constraints::*;
10
11mod factor;
12pub use factor::*;
13
14mod grid;
15pub use grid::*;
16
17mod length;
18pub use length::*;
19
20mod line;
21pub use line::*;
22
23mod point;
24pub use point::*;
25
26mod rect;
27pub use rect::*;
28
29mod resolution;
30pub use resolution::*;
31
32mod side_offsets;
33pub use side_offsets::*;
34
35mod size;
36pub use size::*;
37
38mod transform;
39pub use transform::*;
40
41mod vector;
42pub use vector::*;
43
44use crate::context::LayoutMask;
45
46macro_rules! impl_length_comp_conversions {
48 ($(
49 $(#[$docs:meta])*
50 fn from($($n:ident : $N:ident),+) -> $For:ty {
51 $convert:expr
52 }
53 )+) => {
54 $(
55 impl<$($N),+> From<($($N),+)> for $For
56 where
57 $($N: Into<Length>,)+
58 {
59 $(#[$docs])*
60 fn from(($($n),+) : ($($N),+)) -> Self {
61 $convert
62 }
63 }
64
65 impl<$($N),+> zng_var::IntoVar<$For> for ($($N),+)
66 where
67 $($N: Into<Length> + Clone,)+
68 {
69 type Var = zng_var::LocalVar<$For>;
70
71 $(#[$docs])*
72 fn into_var(self) -> Self::Var {
73 zng_var::LocalVar(self.into())
74 }
75 }
76 )+
77 };
78}
79use impl_length_comp_conversions;
80
81pub trait Layout2d {
85 type Px: Default;
87
88 fn layout(&self) -> Self::Px {
92 self.layout_dft(Default::default())
93 }
94
95 fn layout_dft(&self, default: Self::Px) -> Self::Px;
99
100 fn affect_mask(&self) -> LayoutMask;
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
108pub enum LayoutAxis {
109 X,
111 Y,
113 Z,
115}
116
117pub trait Layout1d {
121 fn layout(&self, axis: LayoutAxis) -> Px {
125 self.layout_dft(axis, Px(0))
126 }
127
128 fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px;
132
133 fn layout_x(&self) -> Px {
137 self.layout(LayoutAxis::X)
138 }
139
140 fn layout_y(&self) -> Px {
144 self.layout(LayoutAxis::Y)
145 }
146
147 fn layout_z(&self) -> Px {
151 self.layout(LayoutAxis::Z)
152 }
153
154 fn layout_dft_x(&self, default: Px) -> Px {
158 self.layout_dft(LayoutAxis::X, default)
159 }
160
161 fn layout_dft_y(&self, default: Px) -> Px {
165 self.layout_dft(LayoutAxis::Y, default)
166 }
167
168 fn layout_dft_z(&self, default: Px) -> Px {
172 self.layout_dft(LayoutAxis::Z, default)
173 }
174
175 fn layout_f32(&self, axis: LayoutAxis) -> f32 {
179 self.layout_f32_dft(axis, 0.0)
180 }
181
182 fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32;
186
187 fn layout_f32_x(&self) -> f32 {
191 self.layout_f32(LayoutAxis::X)
192 }
193
194 fn layout_f32_y(&self) -> f32 {
198 self.layout_f32(LayoutAxis::Y)
199 }
200
201 fn layout_f32_z(&self) -> f32 {
205 self.layout_f32(LayoutAxis::Z)
206 }
207
208 fn layout_f32_dft_x(&self, default: f32) -> f32 {
212 self.layout_f32_dft(LayoutAxis::X, default)
213 }
214
215 fn layout_f32_dft_y(&self, default: f32) -> f32 {
219 self.layout_f32_dft(LayoutAxis::Y, default)
220 }
221
222 fn layout_f32_dft_z(&self, default: f32) -> f32 {
226 self.layout_f32_dft(LayoutAxis::Z, default)
227 }
228
229 fn affect_mask(&self) -> LayoutMask;
233}
234
235#[cfg(test)]
236mod tests {
237 use std::f32::consts::{PI, TAU};
238
239 use zng_app_context::{AppId, LocalContext};
240
241 use crate::context::{LAYOUT, LayoutMetrics};
242
243 use super::*;
244
245 #[test]
246 pub fn zero() {
247 all_equal(0.rad(), 0.grad(), 0.deg(), 0.turn());
248 }
249
250 #[test]
251 pub fn half_circle() {
252 all_equal(PI.rad(), 200.grad(), 180.deg(), 0.5.turn())
253 }
254
255 #[test]
256 pub fn full_circle() {
257 all_equal(TAU.rad(), 400.grad(), 360.deg(), 1.turn())
258 }
259
260 #[test]
261 pub fn one_and_a_half_circle() {
262 all_equal((TAU + PI).rad(), 600.grad(), 540.deg(), 1.5.turn())
263 }
264
265 #[test]
266 pub fn modulo_rad() {
267 assert_eq!(PI.rad(), (TAU + PI).rad().modulo());
268 }
269
270 #[test]
271 pub fn modulo_grad() {
272 assert_eq!(200.grad(), 600.grad().modulo());
273 }
274
275 #[test]
276 pub fn modulo_deg() {
277 assert_eq!(180.deg(), 540.deg().modulo());
278 }
279
280 #[test]
281 pub fn modulo_turn() {
282 assert_eq!(0.5.turn(), 1.5.turn().modulo());
283 }
284
285 #[test]
286 pub fn length_expr_same_unit() {
287 let a = Length::from(200);
288 let b = Length::from(300);
289 let c = a + b;
290
291 assert_eq!(c, 500.dip());
292 }
293
294 #[test]
295 pub fn length_expr_diff_units() {
296 let a = Length::from(200);
297 let b = Length::from(10.pct());
298 let c = a + b;
299
300 assert_eq!(c, Length::Expr(Box::new(LengthExpr::Add(200.into(), 10.pct().into()))))
301 }
302
303 #[test]
304 pub fn length_expr_eval() {
305 let _app = LocalContext::start_app(AppId::new_unique());
306
307 let l = (Length::from(200) - 100.pct()).abs();
308 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(600), Px(400)), Px(0));
309 let l = LAYOUT.with_context(metrics, || l.layout_x());
310
311 assert_eq!(l.0, (200i32 - 600i32).abs());
312 }
313
314 #[test]
315 pub fn length_expr_clamp() {
316 let _app = LocalContext::start_app(AppId::new_unique());
317
318 let l = Length::from(100.pct()).clamp(100, 500);
319 assert!(matches!(l, Length::Expr(_)));
320
321 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(200), Px(50)), Px(0));
322 LAYOUT.with_context(metrics, || {
323 let r = l.layout_x();
324 assert_eq!(r.0, 200);
325
326 let r = l.layout_y();
327 assert_eq!(r.0, 100);
328
329 LAYOUT.with_constraints(LAYOUT.constraints().with_new_max_x(Px(550)), || {
330 let r = l.layout_x();
331 assert_eq!(r.0, 500);
332 });
333 });
334 }
335
336 fn all_equal(rad: AngleRadian, grad: AngleGradian, deg: AngleDegree, turn: AngleTurn) {
337 assert_eq!(rad, AngleRadian::from(grad));
338 assert_eq!(rad, AngleRadian::from(deg));
339 assert_eq!(rad, AngleRadian::from(turn));
340
341 assert_eq!(grad, AngleGradian::from(rad));
342 assert_eq!(grad, AngleGradian::from(deg));
343 assert_eq!(grad, AngleGradian::from(turn));
344
345 assert_eq!(deg, AngleDegree::from(rad));
346 assert_eq!(deg, AngleDegree::from(grad));
347 assert_eq!(deg, AngleDegree::from(turn));
348
349 assert_eq!(turn, AngleTurn::from(rad));
350 assert_eq!(turn, AngleTurn::from(grad));
351 assert_eq!(turn, AngleTurn::from(deg));
352 }
353
354 #[test]
355 fn distance_bounds() {
356 assert_eq!(DistanceKey::MAX.distance(), Some(Px::MAX));
357 assert_eq!(DistanceKey::MIN.distance(), Some(Px(0)));
358 }
359
360 #[test]
361 fn orientation_box_above() {
362 let a = PxRect::from_size(PxSize::splat(Px(40)));
363 let mut b = a;
364 b.origin.y = -Px(82);
365 let a = a.to_box2d();
366 let b = b.to_box2d();
367
368 assert!(Orientation2D::Above.box_is(a, b));
369 assert!(!Orientation2D::Below.box_is(a, b));
370 assert!(!Orientation2D::Left.box_is(a, b));
371 assert!(!Orientation2D::Right.box_is(a, b));
372 }
373
374 #[test]
375 fn orientation_box_below() {
376 let a = PxRect::from_size(PxSize::splat(Px(40)));
377 let mut b = a;
378 b.origin.y = Px(42);
379 let a = a.to_box2d();
380 let b = b.to_box2d();
381
382 assert!(!Orientation2D::Above.box_is(a, b));
383 assert!(Orientation2D::Below.box_is(a, b));
384 assert!(!Orientation2D::Left.box_is(a, b));
385 assert!(!Orientation2D::Right.box_is(a, b));
386 }
387
388 #[test]
389 fn orientation_box_left() {
390 let a = PxRect::from_size(PxSize::splat(Px(40)));
391 let mut b = a;
392 b.origin.x = -Px(82);
393 let a = a.to_box2d();
394 let b = b.to_box2d();
395
396 assert!(!Orientation2D::Above.box_is(a, b));
397 assert!(!Orientation2D::Below.box_is(a, b));
398 assert!(Orientation2D::Left.box_is(a, b));
399 assert!(!Orientation2D::Right.box_is(a, b));
400 }
401
402 #[test]
403 fn orientation_box_right() {
404 let a = PxRect::from_size(PxSize::splat(Px(40)));
405 let mut b = a;
406 b.origin.x = Px(42);
407 let a = a.to_box2d();
408 let b = b.to_box2d();
409
410 assert!(!Orientation2D::Above.box_is(a, b));
411 assert!(!Orientation2D::Below.box_is(a, b));
412 assert!(!Orientation2D::Left.box_is(a, b));
413 assert!(Orientation2D::Right.box_is(a, b));
414 }
415}