Skip to main content

argui_animation/
composition.rs

1use argui_core::{Color, Point, Rect, Size, Transform2D};
2
3#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4pub enum Composition {
5    #[default]
6    Replace,
7    Add,
8    Accumulate,
9}
10
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct Contribution<T> {
13    pub value: T,
14    pub composition: Composition,
15    pub priority: i32,
16    pub order: u64,
17    pub completed_iterations: u64,
18}
19
20impl<T> Contribution<T> {
21    #[must_use]
22    pub const fn replace(value: T, priority: i32, order: u64) -> Self {
23        Self {
24            value,
25            composition: Composition::Replace,
26            priority,
27            order,
28            completed_iterations: 0,
29        }
30    }
31}
32
33pub trait Compose: Copy {
34    fn add(self, contribution: Self) -> Self;
35
36    fn scale(self, factor: f32) -> Self;
37}
38
39#[must_use]
40pub fn compose<T: Compose>(base: T, contributions: &mut [Contribution<T>]) -> T {
41    contributions.sort_by_key(|item| (item.priority, item.order));
42    contributions
43        .iter()
44        .fold(base, |value, contribution| match contribution.composition {
45            Composition::Replace => contribution.value,
46            Composition::Add => value.add(contribution.value),
47            Composition::Accumulate => value.add(
48                contribution
49                    .value
50                    .scale(contribution.completed_iterations as f32 + 1.0),
51            ),
52        })
53}
54
55impl Compose for f32 {
56    fn add(self, contribution: Self) -> Self {
57        self + contribution
58    }
59
60    fn scale(self, factor: f32) -> Self {
61        self * factor
62    }
63}
64
65impl Compose for f64 {
66    fn add(self, contribution: Self) -> Self {
67        self + contribution
68    }
69
70    fn scale(self, factor: f32) -> Self {
71        self * f64::from(factor)
72    }
73}
74
75impl<const N: usize> Compose for [f32; N] {
76    fn add(mut self, contribution: Self) -> Self {
77        for (value, contribution) in self.iter_mut().zip(contribution) {
78            *value += contribution;
79        }
80        self
81    }
82
83    fn scale(mut self, factor: f32) -> Self {
84        for value in &mut self {
85            *value *= factor;
86        }
87        self
88    }
89}
90
91impl Compose for Color {
92    fn add(self, contribution: Self) -> Self {
93        let left = self.to_linear_rgba();
94        let right = contribution.to_linear_rgba();
95        Self::linear_rgba(
96            left[0] + right[0],
97            left[1] + right[1],
98            left[2] + right[2],
99            left[3] + right[3],
100        )
101    }
102
103    fn scale(self, factor: f32) -> Self {
104        let value = self.to_linear_rgba();
105        Self::linear_rgba(
106            value[0] * factor,
107            value[1] * factor,
108            value[2] * factor,
109            value[3] * factor,
110        )
111    }
112}
113
114impl Compose for Point {
115    fn add(self, contribution: Self) -> Self {
116        Self::new(self.x + contribution.x, self.y + contribution.y)
117    }
118
119    fn scale(self, factor: f32) -> Self {
120        Self::new(self.x * factor, self.y * factor)
121    }
122}
123
124impl Compose for Size {
125    fn add(self, contribution: Self) -> Self {
126        Self::new(
127            self.width + contribution.width,
128            self.height + contribution.height,
129        )
130    }
131
132    fn scale(self, factor: f32) -> Self {
133        Self::new(self.width * factor, self.height * factor)
134    }
135}
136
137impl Compose for Rect {
138    fn add(self, contribution: Self) -> Self {
139        Self::new(
140            self.origin.add(contribution.origin),
141            self.size.add(contribution.size),
142        )
143    }
144
145    fn scale(self, factor: f32) -> Self {
146        Self::new(self.origin.scale(factor), self.size.scale(factor))
147    }
148}
149
150impl Compose for Transform2D {
151    fn add(self, contribution: Self) -> Self {
152        Self {
153            translation: self.translation.add(contribution.translation),
154            scale: self.scale.add(contribution.scale),
155            rotation: self.rotation + contribution.rotation,
156            skew: self.skew.add(contribution.skew),
157        }
158    }
159
160    fn scale(self, factor: f32) -> Self {
161        Self {
162            translation: self.translation.scale(factor),
163            scale: self.scale.scale(factor),
164            rotation: self.rotation * factor,
165            skew: self.skew.scale(factor),
166        }
167    }
168}