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 $(#[$docs])*
70 fn into_var(self) -> zng_var::Var<$For> {
71 zng_var::const_var(self.into())
72 }
73 }
74 )+
75 };
76}
77use impl_length_comp_conversions;
78
79pub trait Layout2d {
83 type Px: Default;
85
86 fn layout(&self) -> Self::Px {
90 self.layout_dft(Default::default())
91 }
92
93 fn layout_dft(&self, default: Self::Px) -> Self::Px;
97
98 fn affect_mask(&self) -> LayoutMask;
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
106pub enum LayoutAxis {
107 X,
109 Y,
111 Z,
113}
114
115pub trait Layout1d {
119 fn layout(&self, axis: LayoutAxis) -> Px {
123 self.layout_dft(axis, Px(0))
124 }
125
126 fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px;
130
131 fn layout_x(&self) -> Px {
135 self.layout(LayoutAxis::X)
136 }
137
138 fn layout_y(&self) -> Px {
142 self.layout(LayoutAxis::Y)
143 }
144
145 fn layout_z(&self) -> Px {
149 self.layout(LayoutAxis::Z)
150 }
151
152 fn layout_dft_x(&self, default: Px) -> Px {
156 self.layout_dft(LayoutAxis::X, default)
157 }
158
159 fn layout_dft_y(&self, default: Px) -> Px {
163 self.layout_dft(LayoutAxis::Y, default)
164 }
165
166 fn layout_dft_z(&self, default: Px) -> Px {
170 self.layout_dft(LayoutAxis::Z, default)
171 }
172
173 fn layout_f32(&self, axis: LayoutAxis) -> f32 {
177 self.layout_f32_dft(axis, 0.0)
178 }
179
180 fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32;
184
185 fn layout_f32_x(&self) -> f32 {
189 self.layout_f32(LayoutAxis::X)
190 }
191
192 fn layout_f32_y(&self) -> f32 {
196 self.layout_f32(LayoutAxis::Y)
197 }
198
199 fn layout_f32_z(&self) -> f32 {
203 self.layout_f32(LayoutAxis::Z)
204 }
205
206 fn layout_f32_dft_x(&self, default: f32) -> f32 {
210 self.layout_f32_dft(LayoutAxis::X, default)
211 }
212
213 fn layout_f32_dft_y(&self, default: f32) -> f32 {
217 self.layout_f32_dft(LayoutAxis::Y, default)
218 }
219
220 fn layout_f32_dft_z(&self, default: f32) -> f32 {
224 self.layout_f32_dft(LayoutAxis::Z, default)
225 }
226
227 fn affect_mask(&self) -> LayoutMask;
231}
232
233#[cfg(test)]
234mod tests {
235 use std::f32::consts::{PI, TAU};
236
237 use zng_app_context::{AppId, LocalContext};
238
239 use crate::context::{LAYOUT, LayoutMetrics};
240
241 use super::*;
242
243 #[test]
244 pub fn zero() {
245 all_equal(0.rad(), 0.grad(), 0.deg(), 0.turn());
246 }
247
248 #[test]
249 pub fn half_circle() {
250 all_equal(PI.rad(), 200.grad(), 180.deg(), 0.5.turn())
251 }
252
253 #[test]
254 pub fn full_circle() {
255 all_equal(TAU.rad(), 400.grad(), 360.deg(), 1.turn())
256 }
257
258 #[test]
259 pub fn one_and_a_half_circle() {
260 all_equal((TAU + PI).rad(), 600.grad(), 540.deg(), 1.5.turn())
261 }
262
263 #[test]
264 pub fn modulo_rad() {
265 assert_eq!(PI.rad(), (TAU + PI).rad().modulo());
266 }
267
268 #[test]
269 pub fn modulo_grad() {
270 assert_eq!(200.grad(), 600.grad().modulo());
271 }
272
273 #[test]
274 pub fn modulo_deg() {
275 assert_eq!(180.deg(), 540.deg().modulo());
276 }
277
278 #[test]
279 pub fn modulo_turn() {
280 assert_eq!(0.5.turn(), 1.5.turn().modulo());
281 }
282
283 #[test]
284 pub fn length_expr_same_unit() {
285 let a = Length::from(200);
286 let b = Length::from(300);
287 let c = a + b;
288
289 assert_eq!(c, 500.dip());
290 }
291
292 #[test]
293 pub fn length_expr_diff_units() {
294 let a = Length::from(200);
295 let b = Length::from(10.pct());
296 let c = a + b;
297
298 assert_eq!(c, Length::Expr(Box::new(LengthExpr::Add(200.into(), 10.pct().into()))))
299 }
300
301 #[test]
302 pub fn length_expr_eval() {
303 let _app = LocalContext::start_app(AppId::new_unique());
304
305 let l = (Length::from(200) - 100.pct()).abs();
306 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(600), Px(400)), Px(0));
307 let l = LAYOUT.with_context(metrics, || l.layout_x());
308
309 assert_eq!(l.0, (200i32 - 600i32).abs());
310 }
311
312 #[test]
313 pub fn length_expr_clamp() {
314 let _app = LocalContext::start_app(AppId::new_unique());
315
316 let l = Length::from(100.pct()).clamp(100, 500);
317 assert!(matches!(l, Length::Expr(_)));
318
319 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(200), Px(50)), Px(0));
320 LAYOUT.with_context(metrics, || {
321 let r = l.layout_x();
322 assert_eq!(r.0, 200);
323
324 let r = l.layout_y();
325 assert_eq!(r.0, 100);
326
327 LAYOUT.with_constraints(LAYOUT.constraints().with_new_max_x(Px(550)), || {
328 let r = l.layout_x();
329 assert_eq!(r.0, 500);
330 });
331 });
332 }
333
334 fn all_equal(rad: AngleRadian, grad: AngleGradian, deg: AngleDegree, turn: AngleTurn) {
335 assert_eq!(rad, AngleRadian::from(grad));
336 assert_eq!(rad, AngleRadian::from(deg));
337 assert_eq!(rad, AngleRadian::from(turn));
338
339 assert_eq!(grad, AngleGradian::from(rad));
340 assert_eq!(grad, AngleGradian::from(deg));
341 assert_eq!(grad, AngleGradian::from(turn));
342
343 assert_eq!(deg, AngleDegree::from(rad));
344 assert_eq!(deg, AngleDegree::from(grad));
345 assert_eq!(deg, AngleDegree::from(turn));
346
347 assert_eq!(turn, AngleTurn::from(rad));
348 assert_eq!(turn, AngleTurn::from(grad));
349 assert_eq!(turn, AngleTurn::from(deg));
350 }
351
352 #[test]
353 fn distance_bounds() {
354 assert_eq!(DistanceKey::MAX.distance(), Some(Px::MAX));
355 assert_eq!(DistanceKey::MIN.distance(), Some(Px(0)));
356 }
357
358 #[test]
359 fn orientation_box_above() {
360 let a = PxRect::from_size(PxSize::splat(Px(40)));
361 let mut b = a;
362 b.origin.y = -Px(82);
363 let a = a.to_box2d();
364 let b = b.to_box2d();
365
366 assert!(Orientation2D::Above.box_is(a, b));
367 assert!(!Orientation2D::Below.box_is(a, b));
368 assert!(!Orientation2D::Left.box_is(a, b));
369 assert!(!Orientation2D::Right.box_is(a, b));
370 }
371
372 #[test]
373 fn orientation_box_below() {
374 let a = PxRect::from_size(PxSize::splat(Px(40)));
375 let mut b = a;
376 b.origin.y = Px(42);
377 let a = a.to_box2d();
378 let b = b.to_box2d();
379
380 assert!(!Orientation2D::Above.box_is(a, b));
381 assert!(Orientation2D::Below.box_is(a, b));
382 assert!(!Orientation2D::Left.box_is(a, b));
383 assert!(!Orientation2D::Right.box_is(a, b));
384 }
385
386 #[test]
387 fn orientation_box_left() {
388 let a = PxRect::from_size(PxSize::splat(Px(40)));
389 let mut b = a;
390 b.origin.x = -Px(82);
391 let a = a.to_box2d();
392 let b = b.to_box2d();
393
394 assert!(!Orientation2D::Above.box_is(a, b));
395 assert!(!Orientation2D::Below.box_is(a, b));
396 assert!(Orientation2D::Left.box_is(a, b));
397 assert!(!Orientation2D::Right.box_is(a, b));
398 }
399
400 #[test]
401 fn orientation_box_right() {
402 let a = PxRect::from_size(PxSize::splat(Px(40)));
403 let mut b = a;
404 b.origin.x = Px(42);
405 let a = a.to_box2d();
406 let b = b.to_box2d();
407
408 assert!(!Orientation2D::Above.box_is(a, b));
409 assert!(!Orientation2D::Below.box_is(a, b));
410 assert!(!Orientation2D::Left.box_is(a, b));
411 assert!(Orientation2D::Right.box_is(a, b));
412 }
413}