1use crate::util::sys::f32_max;
4use crate::CompactLength;
5use crate::{style::Dimension, util::sys::f32_min};
6use core::ops::{Add, Sub};
7
8#[cfg(feature = "flexbox")]
9use crate::style::FlexDirection;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub enum AbsoluteAxis {
14    Horizontal,
16    Vertical,
18}
19
20impl AbsoluteAxis {
21    #[inline]
23    pub const fn other_axis(&self) -> Self {
24        match *self {
25            AbsoluteAxis::Horizontal => AbsoluteAxis::Vertical,
26            AbsoluteAxis::Vertical => AbsoluteAxis::Horizontal,
27        }
28    }
29}
30
31impl<T> Size<T> {
32    #[inline(always)]
33    pub fn get_abs(self, axis: AbsoluteAxis) -> T {
35        match axis {
36            AbsoluteAxis::Horizontal => self.width,
37            AbsoluteAxis::Vertical => self.height,
38        }
39    }
40}
41
42impl<T: Add> Rect<T> {
43    #[inline(always)]
44    pub fn grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output {
46        match axis {
47            AbsoluteAxis::Horizontal => self.left + self.right,
48            AbsoluteAxis::Vertical => self.top + self.bottom,
49        }
50    }
51}
52
53#[derive(Copy, Clone, Debug, PartialEq, Eq)]
56pub enum AbstractAxis {
57    Inline,
59    Block,
61}
62
63impl AbstractAxis {
64    #[inline]
66    pub fn other(&self) -> AbstractAxis {
67        match *self {
68            AbstractAxis::Inline => AbstractAxis::Block,
69            AbstractAxis::Block => AbstractAxis::Inline,
70        }
71    }
72
73    #[inline]
76    pub fn as_abs_naive(&self) -> AbsoluteAxis {
77        match self {
78            AbstractAxis::Inline => AbsoluteAxis::Horizontal,
79            AbstractAxis::Block => AbsoluteAxis::Vertical,
80        }
81    }
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
87pub(crate) struct InBothAbsAxis<T> {
88    pub horizontal: T,
90    pub vertical: T,
92}
93
94impl<T: Copy> InBothAbsAxis<T> {
95    #[cfg(feature = "grid")]
96    pub fn get(&self, axis: AbsoluteAxis) -> T {
98        match axis {
99            AbsoluteAxis::Horizontal => self.horizontal,
100            AbsoluteAxis::Vertical => self.vertical,
101        }
102    }
103}
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108pub struct Rect<T> {
109    pub left: T,
115    pub right: T,
121    pub top: T,
124    pub bottom: T,
127}
128
129impl<U, T: Add<U>> Add<Rect<U>> for Rect<T> {
130    type Output = Rect<T::Output>;
131
132    fn add(self, rhs: Rect<U>) -> Self::Output {
133        Rect {
134            left: self.left + rhs.left,
135            right: self.right + rhs.right,
136            top: self.top + rhs.top,
137            bottom: self.bottom + rhs.bottom,
138        }
139    }
140}
141
142impl<T> Rect<T> {
143    #[cfg(any(feature = "flexbox", feature = "block_layout"))]
149    pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
150    where
151        F: Fn(T, U) -> R,
152        U: Copy,
153    {
154        Rect {
155            left: f(self.left, size.width),
156            right: f(self.right, size.width),
157            top: f(self.top, size.height),
158            bottom: f(self.bottom, size.height),
159        }
160    }
161
162    pub fn map<R, F>(self, f: F) -> Rect<R>
166    where
167        F: Fn(T) -> R,
168    {
169        Rect { left: f(self.left), right: f(self.right), top: f(self.top), bottom: f(self.bottom) }
170    }
171
172    pub fn horizontal_components(self) -> Line<T> {
174        Line { start: self.left, end: self.right }
175    }
176
177    pub fn vertical_components(self) -> Line<T> {
179        Line { start: self.top, end: self.bottom }
180    }
181}
182
183impl<T, U> Rect<T>
184where
185    T: Add<Output = U> + Copy + Clone,
186{
187    #[inline(always)]
193    pub(crate) fn horizontal_axis_sum(&self) -> U {
194        self.left + self.right
195    }
196
197    #[inline(always)]
203    pub(crate) fn vertical_axis_sum(&self) -> U {
204        self.top + self.bottom
205    }
206
207    #[inline(always)]
211    #[allow(dead_code)] pub(crate) fn sum_axes(&self) -> Size<U> {
213        Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() }
214    }
215
216    #[cfg(feature = "flexbox")]
223    pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U {
224        if direction.is_row() {
225            self.horizontal_axis_sum()
226        } else {
227            self.vertical_axis_sum()
228        }
229    }
230
231    #[cfg(feature = "flexbox")]
236    pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U {
237        if direction.is_row() {
238            self.vertical_axis_sum()
239        } else {
240            self.horizontal_axis_sum()
241        }
242    }
243}
244
245impl<T> Rect<T>
246where
247    T: Copy + Clone,
248{
249    #[cfg(feature = "flexbox")]
251    pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
252        if direction.is_row() {
253            self.left
254        } else {
255            self.top
256        }
257    }
258
259    #[cfg(feature = "flexbox")]
261    pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
262        if direction.is_row() {
263            self.right
264        } else {
265            self.bottom
266        }
267    }
268
269    #[cfg(feature = "flexbox")]
271    pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
272        if direction.is_row() {
273            self.top
274        } else {
275            self.left
276        }
277    }
278
279    #[cfg(feature = "flexbox")]
281    pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
282        if direction.is_row() {
283            self.bottom
284        } else {
285            self.right
286        }
287    }
288}
289
290impl Rect<f32> {
291    pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
293
294    #[must_use]
296    pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
297        Self { left: start, right: end, top, bottom }
298    }
299}
300
301#[derive(Debug, Copy, Clone, PartialEq, Eq)]
303#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
304#[cfg_attr(feature = "serde", serde(default))]
305pub struct Line<T> {
306    pub start: T,
308    pub end: T,
310}
311
312impl<T> Line<T> {
313    pub fn map<R, F>(self, f: F) -> Line<R>
317    where
318        F: Fn(T) -> R,
319    {
320        Line { start: f(self.start), end: f(self.end) }
321    }
322}
323
324impl Line<bool> {
325    pub const TRUE: Self = Line { start: true, end: true };
327    pub const FALSE: Self = Line { start: false, end: false };
329}
330
331impl<T: Add + Copy> Line<T> {
332    pub fn sum(&self) -> <T as Add>::Output {
334        self.start + self.end
335    }
336}
337
338#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
340#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
341pub struct Size<T> {
342    pub width: T,
344    pub height: T,
346}
347
348impl<U, T: Add<U>> Add<Size<U>> for Size<T> {
350    type Output = Size<<T as Add<U>>::Output>;
351
352    fn add(self, rhs: Size<U>) -> Self::Output {
353        Size { width: self.width + rhs.width, height: self.height + rhs.height }
354    }
355}
356
357impl<U, T: Sub<U>> Sub<Size<U>> for Size<T> {
359    type Output = Size<<T as Sub<U>>::Output>;
360
361    fn sub(self, rhs: Size<U>) -> Self::Output {
362        Size { width: self.width - rhs.width, height: self.height - rhs.height }
363    }
364}
365
366#[allow(dead_code)]
369impl<T> Size<T> {
370    pub fn map<R, F>(self, f: F) -> Size<R>
374    where
375        F: Fn(T) -> R,
376    {
377        Size { width: f(self.width), height: f(self.height) }
378    }
379
380    pub fn map_width<F>(self, f: F) -> Size<T>
382    where
383        F: Fn(T) -> T,
384    {
385        Size { width: f(self.width), height: self.height }
386    }
387
388    pub fn map_height<F>(self, f: F) -> Size<T>
390    where
391        F: Fn(T) -> T,
392    {
393        Size { width: self.width, height: f(self.height) }
394    }
395
396    pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
399    where
400        Func: Fn(T, Other) -> Ret,
401    {
402        Size { width: f(self.width, other.width), height: f(self.height, other.height) }
403    }
404
405    #[cfg(feature = "flexbox")]
409    pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
410        if direction.is_row() {
411            self.width = value
412        } else {
413            self.height = value
414        }
415    }
416
417    #[cfg(feature = "flexbox")]
421    pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
422        if direction.is_row() {
423            self.height = value
424        } else {
425            self.width = value
426        }
427    }
428
429    #[cfg(feature = "flexbox")]
433    pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self {
434        let mut new = self;
435        if direction.is_row() {
436            new.width = value
437        } else {
438            new.height = value
439        }
440        new
441    }
442
443    #[cfg(feature = "flexbox")]
447    pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self {
448        let mut new = self;
449        if direction.is_row() {
450            new.height = value
451        } else {
452            new.width = value
453        }
454        new
455    }
456
457    #[cfg(feature = "flexbox")]
461    pub(crate) fn map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
462        let mut new = self;
463        if direction.is_row() {
464            new.width = mapper(new.width);
465        } else {
466            new.height = mapper(new.height);
467        }
468        new
469    }
470
471    #[cfg(feature = "flexbox")]
475    pub(crate) fn map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
476        let mut new = self;
477        if direction.is_row() {
478            new.height = mapper(new.height);
479        } else {
480            new.width = mapper(new.width);
481        }
482        new
483    }
484
485    #[cfg(feature = "flexbox")]
489    pub(crate) fn main(self, direction: FlexDirection) -> T {
490        if direction.is_row() {
491            self.width
492        } else {
493            self.height
494        }
495    }
496
497    #[cfg(feature = "flexbox")]
501    pub(crate) fn cross(self, direction: FlexDirection) -> T {
502        if direction.is_row() {
503            self.height
504        } else {
505            self.width
506        }
507    }
508
509    #[cfg(feature = "grid")]
512    pub(crate) fn get(self, axis: AbstractAxis) -> T {
513        match axis {
514            AbstractAxis::Inline => self.width,
515            AbstractAxis::Block => self.height,
516        }
517    }
518
519    #[cfg(feature = "grid")]
522    pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) {
523        match axis {
524            AbstractAxis::Inline => self.width = value,
525            AbstractAxis::Block => self.height = value,
526        }
527    }
528}
529
530impl Size<f32> {
531    pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
533
534    #[inline(always)]
536    pub fn f32_max(self, rhs: Size<f32>) -> Size<f32> {
537        Size { width: f32_max(self.width, rhs.width), height: f32_max(self.height, rhs.height) }
538    }
539
540    #[inline(always)]
542    pub fn f32_min(self, rhs: Size<f32>) -> Size<f32> {
543        Size { width: f32_min(self.width, rhs.width), height: f32_min(self.height, rhs.height) }
544    }
545
546    #[inline(always)]
548    pub fn has_non_zero_area(self) -> bool {
549        self.width > 0.0 && self.height > 0.0
550    }
551}
552
553impl Size<Option<f32>> {
554    pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
556
557    #[must_use]
559    pub const fn new(width: f32, height: f32) -> Self {
560        Size { width: Some(width), height: Some(height) }
561    }
562
563    #[cfg(feature = "flexbox")]
565    pub fn from_cross(direction: FlexDirection, value: Option<f32>) -> Self {
566        let mut new = Self::NONE;
567        if direction.is_row() {
568            new.height = value
569        } else {
570            new.width = value
571        }
572        new
573    }
574
575    pub fn maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>> {
581        match aspect_ratio {
582            Some(ratio) => match (self.width, self.height) {
583                (Some(width), None) => Size { width: Some(width), height: Some(width / ratio) },
584                (None, Some(height)) => Size { width: Some(height * ratio), height: Some(height) },
585                _ => self,
586            },
587            None => self,
588        }
589    }
590}
591
592impl<T> Size<Option<T>> {
593    pub fn unwrap_or(self, alt: Size<T>) -> Size<T> {
595        Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
596    }
597
598    pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> {
600        Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
601    }
602
603    #[inline(always)]
605    pub fn both_axis_defined(&self) -> bool {
606        self.width.is_some() && self.height.is_some()
607    }
608}
609
610impl Size<Dimension> {
611    #[must_use]
613    pub const fn from_lengths(width: f32, height: f32) -> Self {
614        Size { width: Dimension(CompactLength::length(width)), height: Dimension(CompactLength::length(height)) }
615    }
616
617    #[must_use]
619    pub const fn from_percent(width: f32, height: f32) -> Self {
620        Size { width: Dimension(CompactLength::percent(width)), height: Dimension(CompactLength::percent(height)) }
621    }
622}
623
624#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
628#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
629pub struct Point<T> {
630    pub x: T,
632    pub y: T,
634}
635
636impl Point<f32> {
637    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
639}
640
641impl Point<Option<f32>> {
642    pub const NONE: Self = Self { x: None, y: None };
644}
645
646impl<U, T: Add<U>> Add<Point<U>> for Point<T> {
648    type Output = Point<<T as Add<U>>::Output>;
649
650    fn add(self, rhs: Point<U>) -> Self::Output {
651        Point { x: self.x + rhs.x, y: self.y + rhs.y }
652    }
653}
654
655impl<T> Point<T> {
656    pub fn map<R, F>(self, f: F) -> Point<R>
660    where
661        F: Fn(T) -> R,
662    {
663        Point { x: f(self.x), y: f(self.y) }
664    }
665
666    #[cfg(feature = "grid")]
669    pub fn get(self, axis: AbstractAxis) -> T {
670        match axis {
671            AbstractAxis::Inline => self.x,
672            AbstractAxis::Block => self.y,
673        }
674    }
675
676    pub fn transpose(self) -> Point<T> {
678        Point { x: self.y, y: self.x }
679    }
680
681    #[cfg(feature = "grid")]
684    pub fn set(&mut self, axis: AbstractAxis, value: T) {
685        match axis {
686            AbstractAxis::Inline => self.x = value,
687            AbstractAxis::Block => self.y = value,
688        }
689    }
690
691    #[cfg(feature = "flexbox")]
695    pub(crate) fn main(self, direction: FlexDirection) -> T {
696        if direction.is_row() {
697            self.x
698        } else {
699            self.y
700        }
701    }
702
703    #[cfg(feature = "flexbox")]
707    pub(crate) fn cross(self, direction: FlexDirection) -> T {
708        if direction.is_row() {
709            self.y
710        } else {
711            self.x
712        }
713    }
714}
715
716impl<T> From<Point<T>> for Size<T> {
717    fn from(value: Point<T>) -> Self {
718        Size { width: value.x, height: value.y }
719    }
720}
721
722#[derive(Debug, Copy, Clone, PartialEq, Eq)]
724#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
725pub struct MinMax<Min, Max> {
726    pub min: Min,
728    pub max: Max,
730}