1use std::fmt;
4
5pub use zng_unit::*;
6
7mod alignment;
8pub use alignment::*;
9
10mod constraints;
11pub use constraints::*;
12
13mod factor;
14pub use factor::*;
15
16mod grid;
17pub use grid::*;
18
19mod length;
20pub use length::*;
21
22mod line;
23pub use line::*;
24
25mod point;
26pub use point::*;
27
28mod rect;
29pub use rect::*;
30
31mod resolution;
32pub use resolution::*;
33
34mod side_offsets;
35pub use side_offsets::*;
36
37mod size;
38pub use size::*;
39
40mod transform;
41pub use transform::*;
42
43mod vector;
44pub use vector::*;
45
46use crate::context::LayoutMask;
47
48macro_rules! impl_length_comp_conversions {
50 ($(
51 $(#[$docs:meta])*
52 fn from($($n:ident : $N:ident),+) -> $For:ty {
53 $convert:expr
54 }
55 )+) => {
56 $(
57 impl<$($N),+> From<($($N),+)> for $For
58 where
59 $($N: Into<Length>,)+
60 {
61 $(#[$docs])*
62 fn from(($($n),+) : ($($N),+)) -> Self {
63 $convert
64 }
65 }
66
67 impl<$($N),+> zng_var::IntoVar<$For> for ($($N),+)
68 where
69 $($N: Into<Length> + Clone,)+
70 {
71 $(#[$docs])*
72 fn into_var(self) -> zng_var::Var<$For> {
73 zng_var::const_var(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#[derive(Debug)]
237#[non_exhaustive]
238pub enum ParseFloatCompositeError {
239 Component(std::num::ParseFloatError),
241 MissingComponent,
243 ExtraComponent,
245 UnknownFormat,
247}
248impl fmt::Display for ParseFloatCompositeError {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 match self {
251 ParseFloatCompositeError::Component(e) => write!(f, "error parsing component, {e}"),
252 ParseFloatCompositeError::MissingComponent => write!(f, "missing component"),
253 ParseFloatCompositeError::ExtraComponent => write!(f, "extra component"),
254 ParseFloatCompositeError::UnknownFormat => write!(f, "unknown format"),
255 }
256 }
257}
258impl std::error::Error for ParseFloatCompositeError {
259 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
260 if let ParseFloatCompositeError::Component(e) = self {
261 Some(e)
262 } else {
263 None
264 }
265 }
266}
267impl From<std::num::ParseFloatError> for ParseFloatCompositeError {
268 fn from(value: std::num::ParseFloatError) -> Self {
269 ParseFloatCompositeError::Component(value)
270 }
271}
272
273#[derive(Debug)]
275#[non_exhaustive]
276pub enum ParseCompositeError {
277 FloatComponent(std::num::ParseFloatError),
279 IntComponent(std::num::ParseIntError),
281 MissingComponent,
283 ExtraComponent,
285 UnknownFormat,
287}
288impl fmt::Display for ParseCompositeError {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 match self {
291 ParseCompositeError::FloatComponent(e) => write!(f, "error parsing component, {e}"),
292 ParseCompositeError::IntComponent(e) => write!(f, "error parsing component, {e}"),
293 ParseCompositeError::MissingComponent => write!(f, "missing component"),
294 ParseCompositeError::ExtraComponent => write!(f, "extra component"),
295 ParseCompositeError::UnknownFormat => write!(f, "unknown format"),
296 }
297 }
298}
299impl std::error::Error for ParseCompositeError {
300 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
301 if let ParseCompositeError::FloatComponent(e) = self {
302 Some(e)
303 } else if let ParseCompositeError::IntComponent(e) = self {
304 Some(e)
305 } else {
306 None
307 }
308 }
309}
310impl From<std::num::ParseFloatError> for ParseCompositeError {
311 fn from(value: std::num::ParseFloatError) -> Self {
312 ParseCompositeError::FloatComponent(value)
313 }
314}
315impl From<std::num::ParseIntError> for ParseCompositeError {
316 fn from(value: std::num::ParseIntError) -> Self {
317 ParseCompositeError::IntComponent(value)
318 }
319}
320impl From<ParseFloatCompositeError> for ParseCompositeError {
321 fn from(value: ParseFloatCompositeError) -> Self {
322 match value {
323 ParseFloatCompositeError::Component(e) => ParseCompositeError::FloatComponent(e),
324 ParseFloatCompositeError::MissingComponent => ParseCompositeError::MissingComponent,
325 ParseFloatCompositeError::ExtraComponent => ParseCompositeError::ExtraComponent,
326 ParseFloatCompositeError::UnknownFormat => ParseCompositeError::UnknownFormat,
327 }
328 }
329}
330impl From<ParseIntCompositeError> for ParseCompositeError {
331 fn from(value: ParseIntCompositeError) -> Self {
332 match value {
333 ParseIntCompositeError::Component(e) => ParseCompositeError::IntComponent(e),
334 ParseIntCompositeError::MissingComponent => ParseCompositeError::MissingComponent,
335 ParseIntCompositeError::ExtraComponent => ParseCompositeError::ExtraComponent,
336 ParseIntCompositeError::UnknownFormat => ParseCompositeError::UnknownFormat,
337 _ => unreachable!(),
338 }
339 }
340}
341
342pub(crate) struct LengthCompositeParser<'a> {
343 sep: &'a [char],
344 s: &'a str,
345}
346impl<'a> LengthCompositeParser<'a> {
347 pub(crate) fn new(s: &'a str) -> Result<LengthCompositeParser<'a>, ParseCompositeError> {
348 Self::new_sep(s, &[','])
349 }
350 pub(crate) fn new_sep(s: &'a str, sep: &'a [char]) -> Result<LengthCompositeParser<'a>, ParseCompositeError> {
351 if let Some(s) = s.strip_prefix('(') {
352 if let Some(s) = s.strip_suffix(')') {
353 return Ok(Self { s, sep });
354 } else {
355 return Err(ParseCompositeError::MissingComponent);
356 }
357 }
358 Ok(Self { s, sep })
359 }
360
361 pub(crate) fn next(&mut self) -> Result<Length, ParseCompositeError> {
362 let mut depth = 0;
363 for (ci, c) in self.s.char_indices() {
364 if depth == 0
365 && let Some(sep) = self.sep.iter().find(|s| **s == c)
366 {
367 let l = &self.s[..ci];
368 self.s = &self.s[ci + sep.len_utf8()..];
369 return l.trim().parse();
370 } else if c == '(' {
371 depth += 1;
372 } else if c == ')' {
373 depth -= 1;
374 }
375 }
376 if self.s.is_empty() {
377 Err(ParseCompositeError::MissingComponent)
378 } else {
379 let l = self.s;
380 self.s = "";
381 l.trim().parse()
382 }
383 }
384
385 pub fn has_ended(&self) -> bool {
386 self.s.is_empty()
387 }
388
389 pub(crate) fn expect_last(mut self) -> Result<Length, ParseCompositeError> {
390 let c = self.next()?;
391 if !self.has_ended() {
392 Err(ParseCompositeError::ExtraComponent)
393 } else {
394 Ok(c)
395 }
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use std::f32::consts::{PI, TAU};
402
403 use zng_app_context::{AppId, LocalContext};
404
405 use crate::context::{LAYOUT, LayoutMetrics};
406
407 use super::*;
408
409 #[test]
410 pub fn zero() {
411 all_equal(0.rad(), 0.grad(), 0.deg(), 0.turn());
412 }
413
414 #[test]
415 pub fn half_circle() {
416 all_equal(PI.rad(), 200.grad(), 180.deg(), 0.5.turn())
417 }
418
419 #[test]
420 pub fn full_circle() {
421 all_equal(TAU.rad(), 400.grad(), 360.deg(), 1.turn())
422 }
423
424 #[test]
425 pub fn one_and_a_half_circle() {
426 all_equal((TAU + PI).rad(), 600.grad(), 540.deg(), 1.5.turn())
427 }
428
429 #[test]
430 pub fn modulo_rad() {
431 assert_eq!(PI.rad(), (TAU + PI).rad().modulo());
432 }
433
434 #[test]
435 pub fn modulo_grad() {
436 assert_eq!(200.grad(), 600.grad().modulo());
437 }
438
439 #[test]
440 pub fn modulo_deg() {
441 assert_eq!(180.deg(), 540.deg().modulo());
442 }
443
444 #[test]
445 pub fn modulo_turn() {
446 assert_eq!(0.5.turn(), 1.5.turn().modulo());
447 }
448
449 #[test]
450 pub fn length_expr_same_unit() {
451 let a = Length::from(200);
452 let b = Length::from(300);
453 let c = a + b;
454
455 assert_eq!(c, 500.dip());
456 }
457
458 #[test]
459 pub fn length_expr_diff_units() {
460 let a = Length::from(200);
461 let b = Length::from(10.pct());
462 let c = a + b;
463
464 assert_eq!(c, Length::Expr(Box::new(LengthExpr::Add(200.into(), 10.pct().into()))))
465 }
466
467 #[test]
468 pub fn length_expr_eval() {
469 let _app = LocalContext::start_app(AppId::new_unique());
470
471 let l = (Length::from(200) - 100.pct()).abs();
472 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(600), Px(400)), Px(0));
473 let l = LAYOUT.with_context(metrics, || l.layout_x());
474
475 assert_eq!(l.0, (200i32 - 600i32).abs());
476 }
477
478 #[test]
479 pub fn length_expr_clamp() {
480 let _app = LocalContext::start_app(AppId::new_unique());
481
482 let l = Length::from(100.pct()).clamp(100, 500);
483 assert!(matches!(l, Length::Expr(_)));
484
485 let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(200), Px(50)), Px(0));
486 LAYOUT.with_context(metrics, || {
487 let r = l.layout_x();
488 assert_eq!(r.0, 200);
489
490 let r = l.layout_y();
491 assert_eq!(r.0, 100);
492
493 LAYOUT.with_constraints(LAYOUT.constraints().with_new_max_x(Px(550)), || {
494 let r = l.layout_x();
495 assert_eq!(r.0, 500);
496 });
497 });
498 }
499
500 fn all_equal(rad: AngleRadian, grad: AngleGradian, deg: AngleDegree, turn: AngleTurn) {
501 assert_eq!(rad, AngleRadian::from(grad));
502 assert_eq!(rad, AngleRadian::from(deg));
503 assert_eq!(rad, AngleRadian::from(turn));
504
505 assert_eq!(grad, AngleGradian::from(rad));
506 assert_eq!(grad, AngleGradian::from(deg));
507 assert_eq!(grad, AngleGradian::from(turn));
508
509 assert_eq!(deg, AngleDegree::from(rad));
510 assert_eq!(deg, AngleDegree::from(grad));
511 assert_eq!(deg, AngleDegree::from(turn));
512
513 assert_eq!(turn, AngleTurn::from(rad));
514 assert_eq!(turn, AngleTurn::from(grad));
515 assert_eq!(turn, AngleTurn::from(deg));
516 }
517
518 #[test]
519 fn distance_bounds() {
520 assert_eq!(DistanceKey::MAX.distance(), Some(Px::MAX));
521 assert_eq!(DistanceKey::MIN.distance(), Some(Px(0)));
522 }
523
524 #[test]
525 fn orientation_box_above() {
526 let a = PxRect::from_size(PxSize::splat(Px(40)));
527 let mut b = a;
528 b.origin.y = -Px(82);
529 let a = a.to_box2d();
530 let b = b.to_box2d();
531
532 assert!(Orientation2D::Above.box_is(a, b));
533 assert!(!Orientation2D::Below.box_is(a, b));
534 assert!(!Orientation2D::Left.box_is(a, b));
535 assert!(!Orientation2D::Right.box_is(a, b));
536 }
537
538 #[test]
539 fn orientation_box_below() {
540 let a = PxRect::from_size(PxSize::splat(Px(40)));
541 let mut b = a;
542 b.origin.y = Px(42);
543 let a = a.to_box2d();
544 let b = b.to_box2d();
545
546 assert!(!Orientation2D::Above.box_is(a, b));
547 assert!(Orientation2D::Below.box_is(a, b));
548 assert!(!Orientation2D::Left.box_is(a, b));
549 assert!(!Orientation2D::Right.box_is(a, b));
550 }
551
552 #[test]
553 fn orientation_box_left() {
554 let a = PxRect::from_size(PxSize::splat(Px(40)));
555 let mut b = a;
556 b.origin.x = -Px(82);
557 let a = a.to_box2d();
558 let b = b.to_box2d();
559
560 assert!(!Orientation2D::Above.box_is(a, b));
561 assert!(!Orientation2D::Below.box_is(a, b));
562 assert!(Orientation2D::Left.box_is(a, b));
563 assert!(!Orientation2D::Right.box_is(a, b));
564 }
565
566 #[test]
567 fn orientation_box_right() {
568 let a = PxRect::from_size(PxSize::splat(Px(40)));
569 let mut b = a;
570 b.origin.x = Px(42);
571 let a = a.to_box2d();
572 let b = b.to_box2d();
573
574 assert!(!Orientation2D::Above.box_is(a, b));
575 assert!(!Orientation2D::Below.box_is(a, b));
576 assert!(!Orientation2D::Left.box_is(a, b));
577 assert!(Orientation2D::Right.box_is(a, b));
578 }
579
580 #[test]
581 fn length_composite_parser_2() {
582 let mut parser = LengthCompositeParser::new("(10%, 20%)").unwrap();
583 assert_eq!(parser.next().unwrap(), Length::from(10.pct()));
584 assert_eq!(parser.expect_last().unwrap(), Length::from(20.pct()));
585 }
586
587 #[test]
588 fn length_composite_parser_1() {
589 let parser = LengthCompositeParser::new("10px").unwrap();
590 assert_eq!(parser.expect_last().unwrap(), 10.px());
591 }
592}