concinnity_core/gfx/anim_graph/
blend.rs1use alloc::vec::Vec;
9
10#[derive(Debug, Clone)]
14pub struct ClipPlay {
15 pub clip: usize,
17 pub duration_secs: f32,
19}
20
21#[derive(Debug, Clone)]
25pub struct Blend1D {
26 pub param: usize,
28 pub thresholds: Vec<f32>,
30 pub plays: Vec<ClipPlay>,
32 pub sync: bool,
34}
35
36#[derive(Debug, Clone)]
41pub struct Blend2D {
42 pub(crate) param_x: usize,
43 pub(crate) param_y: usize,
44 pub x_values: Vec<f32>,
46 pub y_values: Vec<f32>,
48 pub plays: Vec<ClipPlay>,
50 pub sync: bool,
52}
53
54#[derive(Debug, Clone)]
56pub enum StatePlay {
57 Clip(ClipPlay),
59 Blend1D(Blend1D),
61 Blend2D(Blend2D),
63}
64
65impl StatePlay {
66 pub fn sync(&self) -> bool {
69 match self {
70 StatePlay::Clip(_) => false,
71 StatePlay::Blend1D(b) => b.sync,
72 StatePlay::Blend2D(b) => b.sync,
73 }
74 }
75
76 pub fn members(&self) -> &[ClipPlay] {
78 match self {
79 StatePlay::Clip(play) => core::slice::from_ref(play),
80 StatePlay::Blend1D(b) => &b.plays,
81 StatePlay::Blend2D(b) => &b.plays,
82 }
83 }
84
85 pub(crate) fn members_mut(&mut self) -> &mut [ClipPlay] {
86 match self {
87 StatePlay::Clip(play) => core::slice::from_mut(play),
88 StatePlay::Blend1D(b) => &mut b.plays,
89 StatePlay::Blend2D(b) => &mut b.plays,
90 }
91 }
92
93 pub fn weights(&self, params: &[f32]) -> Vec<f32> {
96 let mut out = Vec::new();
97 self.weights_into(params, &mut out);
98 out
99 }
100
101 pub fn weights_into(&self, params: &[f32], out: &mut Vec<f32>) {
105 let at = |i: usize| params.get(i).copied().unwrap_or(0.0);
106 out.clear();
107 match self {
108 StatePlay::Clip(_) => out.push(1.0),
109 StatePlay::Blend1D(b) => blend1d_weights_into(&b.thresholds, at(b.param), out),
110 StatePlay::Blend2D(b) => {
111 blend2d_weights_into(&b.x_values, &b.y_values, at(b.param_x), at(b.param_y), out)
112 }
113 }
114 }
115
116 pub fn effective_duration(&self, weights: &[f32]) -> f32 {
120 let members = self.members();
121 let mut total_w = 0.0f32;
122 let mut acc = 0.0f32;
123 for (member, &w) in members.iter().zip(weights) {
124 let w = w.max(0.0);
125 acc += w * member.duration_secs;
126 total_w += w;
127 }
128 if total_w <= 1e-6 {
129 members.first().map(|m| m.duration_secs).unwrap_or(0.0)
130 } else {
131 acc / total_w
132 }
133 }
134}
135
136pub fn blend1d_weights(thresholds: &[f32], x: f32) -> Vec<f32> {
140 let mut weights = Vec::new();
141 blend1d_weights_into(thresholds, x, &mut weights);
142 weights
143}
144
145pub(super) fn blend1d_weights_into(thresholds: &[f32], x: f32, weights: &mut Vec<f32>) {
147 weights.clear();
148 weights.resize(thresholds.len(), 0.0);
149 let Some((&first, &last)) = thresholds.first().zip(thresholds.last()) else {
150 return;
151 };
152 if x <= first {
153 weights[0] = 1.0;
154 return;
155 }
156 if x >= last {
157 *weights.last_mut().expect("non-empty") = 1.0;
158 return;
159 }
160 for i in 0..thresholds.len() - 1 {
161 let (a, b) = (thresholds[i], thresholds[i + 1]);
162 if x >= a && x <= b {
163 let f = (x - a) / (b - a).max(1e-6);
164 weights[i] = 1.0 - f;
165 weights[i + 1] = f;
166 break;
167 }
168 }
169}
170
171pub fn blend2d_weights(x_values: &[f32], y_values: &[f32], x: f32, y: f32) -> Vec<f32> {
175 let mut weights = Vec::new();
176 blend2d_weights_into(x_values, y_values, x, y, &mut weights);
177 weights
178}
179
180pub(super) fn blend2d_weights_into(
182 x_values: &[f32],
183 y_values: &[f32],
184 x: f32,
185 y: f32,
186 weights: &mut Vec<f32>,
187) {
188 weights.clear();
189 weights.resize(x_values.len() * y_values.len(), 0.0);
190 if x_values.is_empty() || y_values.is_empty() {
191 return;
192 }
193 let (ix, fx) = axis_segment(x_values, x);
194 let (iy, fy) = axis_segment(y_values, y);
195 let nx = x_values.len();
196 let ix1 = (ix + 1).min(nx - 1);
197 let iy1 = (iy + 1).min(y_values.len() - 1);
198 weights[iy * nx + ix] += (1.0 - fx) * (1.0 - fy);
199 weights[iy * nx + ix1] += fx * (1.0 - fy);
200 weights[iy1 * nx + ix] += (1.0 - fx) * fy;
201 weights[iy1 * nx + ix1] += fx * fy;
202}
203
204fn axis_segment(values: &[f32], v: f32) -> (usize, f32) {
207 if v <= values[0] || values.len() == 1 {
208 return (0, 0.0);
209 }
210 if v >= values[values.len() - 1] {
211 return (values.len() - 1, 0.0);
212 }
213 for i in 0..values.len() - 1 {
214 let (a, b) = (values[i], values[i + 1]);
215 if v >= a && v <= b {
216 return (i, (v - a) / (b - a).max(1e-6));
217 }
218 }
219 (values.len() - 1, 0.0)
220}