argui_animation/
composition.rs1use 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}