1use alpha2_geometry::StableHash;
4use alpha2_model::{RangeShape, RangeUnits, RunState, TextAnimator, TextSelector, TextUnit};
5use alpha2_time::MediaTime;
6
7use crate::{sample_rgba, sample_scalar, sample_vec2};
8
9#[derive(Clone, Debug, PartialEq)]
11pub struct SampledAnimator {
12 pub selector: SampledSelector,
14 pub offset: Option<[f32; 2]>,
16 pub scale: Option<[f32; 2]>,
18 pub rotation_degrees: Option<f32>,
20 pub opacity: Option<f32>,
22 pub fill: Option<[f32; 4]>,
24 pub stroke: Option<[f32; 4]>,
26 pub tracking: Option<f32>,
28}
29
30#[derive(Clone, Debug, PartialEq)]
32pub enum SampledSelector {
33 Range {
35 based_on: TextUnit,
37 units: RangeUnits,
39 start: f32,
41 end: f32,
43 offset: f32,
45 shape: RangeShape,
47 amount: f32,
49 },
50 Timing {
52 state: RunState,
54 ramp: MediaTime,
56 sweep: bool,
58 },
59 Class {
61 class: String,
63 },
64}
65
66#[must_use]
68pub fn sample_text_animators(animators: &[TextAnimator], time: MediaTime) -> Vec<SampledAnimator> {
69 animators
70 .iter()
71 .map(|animator| {
72 let properties = &animator.properties;
73 let pair = |value: alpha2_model::Vec2| [value.x, value.y];
74 let color =
75 |value: alpha2_model::Rgba| [value.red, value.green, value.blue, value.alpha];
76 SampledAnimator {
77 selector: match &animator.selector {
78 TextSelector::Range {
79 based_on,
80 units,
81 start,
82 end,
83 offset,
84 shape,
85 amount,
86 } => SampledSelector::Range {
87 based_on: *based_on,
88 units: *units,
89 start: sample_scalar(start, time),
90 end: sample_scalar(end, time),
91 offset: sample_scalar(offset, time),
92 shape: *shape,
93 amount: sample_scalar(amount, time).clamp(0.0, 1.0),
94 },
95 TextSelector::Timing { state, ramp, sweep } => SampledSelector::Timing {
96 state: *state,
97 ramp: *ramp,
98 sweep: *sweep,
99 },
100 TextSelector::Class { class } => SampledSelector::Class {
101 class: class.clone(),
102 },
103 },
104 offset: properties
105 .offset
106 .as_ref()
107 .map(|value| pair(sample_vec2(value, time))),
108 scale: properties
109 .scale
110 .as_ref()
111 .map(|value| pair(sample_vec2(value, time)).map(|axis| axis.max(0.0))),
112 rotation_degrees: properties
113 .rotation_degrees
114 .as_ref()
115 .map(|value| sample_scalar(value, time)),
116 opacity: properties
117 .opacity
118 .as_ref()
119 .map(|value| sample_scalar(value, time).clamp(0.0, 1.0)),
120 fill: properties
121 .fill
122 .as_ref()
123 .map(|value| color(sample_rgba(value, time))),
124 stroke: properties
125 .stroke
126 .as_ref()
127 .map(|value| color(sample_rgba(value, time))),
128 tracking: properties
129 .tracking
130 .as_ref()
131 .map(|value| sample_scalar(value, time)),
132 }
133 })
134 .collect()
135}
136
137pub fn hash_sampled_animators(hash: &mut StableHash, animators: &[SampledAnimator]) {
139 hash.u64(animators.len() as u64);
140 for animator in animators {
141 match &animator.selector {
142 SampledSelector::Range {
143 based_on,
144 units,
145 start,
146 end,
147 offset,
148 shape,
149 amount,
150 } => {
151 hash.u32(0);
152 hash.u32(*based_on as u32);
153 hash.u32(*units as u32);
154 hash.u32(*shape as u32);
155 for value in [start, end, offset, amount] {
156 hash.f32(*value);
157 }
158 }
159 SampledSelector::Timing { state, ramp, sweep } => {
160 hash.u32(1);
161 hash.u32(*state as u32);
162 hash.u64(ramp.ticks().cast_unsigned());
163 hash.u32(u32::from(*sweep));
164 }
165 SampledSelector::Class { class } => {
166 hash.u32(2);
167 hash.bytes(class.as_bytes());
168 }
169 }
170 let mut option = |values: Option<&[f32]>| match values {
171 None => hash.u32(0),
172 Some(values) => {
173 hash.u32(1);
174 for value in values {
175 hash.f32(*value);
176 }
177 }
178 };
179 option(animator.offset.as_ref().map(<[f32; 2]>::as_slice));
180 option(animator.scale.as_ref().map(<[f32; 2]>::as_slice));
181 option(animator.rotation_degrees.as_ref().map(std::slice::from_ref));
182 option(animator.opacity.as_ref().map(std::slice::from_ref));
183 option(animator.fill.as_ref().map(<[f32; 4]>::as_slice));
184 option(animator.stroke.as_ref().map(<[f32; 4]>::as_slice));
185 option(animator.tracking.as_ref().map(std::slice::from_ref));
186 }
187}