Loading...
Searching...
No Matches
Easetanbul.hpp
1#pragma once
2
3/* SPDX-License-Identifier: GPL-3.0-or-later */
4
5#include <ossia/editor/curve/curve_segment/easing.hpp>
6#include <ossia/network/value/value.hpp>
7
8#include <halp/audio.hpp>
9#include <halp/controls.hpp>
10#include <halp/meta.hpp>
11
12#include <cmath>
13
14namespace ao
15{
24{
25public:
26 halp_meta(name, "Easetanbul")
27 halp_meta(c_name, "easetanbul")
28 halp_meta(category, "Control/Mappings")
29 halp_meta(description, "Easing function power tool")
30 halp_meta(manual_url, "https://ossia.io/score-docs/processes/easetanbul.html")
31 halp_meta(author, "Jean-Michaƫl Celerier")
32 halp_meta(uuid, "8abb3061-7101-48d4-81b1-6b19208098bf")
33
34 enum Mode
35 {
36 Linear,
37 QuadraticIn,
38 QuadraticOut,
39 QuadraticInOut,
40 CubicIn,
41 CubicOut,
42 CubicInOut,
43 QuarticIn,
44 QuarticOut,
45 QuarticInOut,
46 QuinticIn,
47 QuinticOut,
48 QuinticInOut,
49 SineIn,
50 SineOut,
51 SineInOut,
52 CircularIn,
53 CircularOut,
54 CircularInOut,
55 ExponentialIn,
56 ExponentialOut,
57 ExponentialInOut,
58 ElasticIn,
59 ElasticOut,
60 ElasticInOut,
61 BackIn,
62 BackOut,
63 BackInOut,
64 BounceIn,
65 BounceOut,
66 BounceInOut,
67 PerlinInOut,
68 Kinematic,
69 PID
70 };
71
72 // Kinematic:
73 // https://jcgt.org/published/0011/03/02/paper.pdf
74
75 // PID:
76 /* https://news.ycombinator.com/item?id=44584830
77 * function sprung_response(t,pos,vel,k,c,m)
78 local decay = c/2/m
79 local omega = math.sqrt(k/m)
80 local resid = decay*decay-omega*omega
81 local scale = math.sqrt(math.abs(resid))
82 local T1,T0 = t , 1
83 if resid<0 then
84 T1,T0 = math.sin( scale*t)/scale , math.cos( scale*t)
85 elseif resid>0 then
86 T1,T0 = math.sinh(scale*t)/scale , math.cosh(scale*t)
87 end
88 local dissipation = math.exp(-decay*t)
89 local evolved_pos = dissipation*( pos*(T0+T1*decay) + vel*( T1 ) )
90 local evolved_vel = dissipation*( pos*(-T1*omega^2) + vel*(T0-T1*decay) )
91 return evolved_pos , evolved_vel
92 end
93*/
94 struct
95 {
96 struct : halp::val_port<"Input", ossia::value>
97 {
98 void update(Easetanbul& self)
99 {
100 self.m_previous = self.m_current;
101 self.m_current = value;
102 self.m_running = true;
103 self.m_elapsed = 0;
104 }
105 } value;
106 struct : halp::enum_t<Mode, "Mode">
107 {
108 enum widget
109 {
110 combobox
111 };
112 } ease;
113 struct : halp::time_chooser<"Delay", halp::range{0.001, 30., 0.2}>
114 {
115 void update(Easetanbul& self) { self.m_duration = value * self.rate; }
116 } delay;
117 // retrigger on each value vs interpolate ?
118
119 } inputs;
120
121 struct
122 {
123 halp::val_port<"Output", ossia::value> output;
124 } outputs;
125
126 double rate = 48000.;
127 void prepare(halp::setup info) noexcept { rate = info.rate; }
128
129 double ease01(double value)
130 {
131 using ease_type = Mode;
132 switch(inputs.ease)
133 {
134 default:
135 case ease_type::Linear:
136 return ossia::easing::linear{}(value);
137 case ease_type::BackIn:
138 return ossia::easing::backIn{}(value);
139 case ease_type::BackOut:
140 return ossia::easing::backOut{}(value);
141 case ease_type::BackInOut:
142 return ossia::easing::backInOut{}(value);
143 case ease_type::BounceIn:
144 return ossia::easing::bounceIn{}(value);
145 case ease_type::BounceOut:
146 return ossia::easing::bounceOut{}(value);
147 case ease_type::BounceInOut:
148 return ossia::easing::bounceInOut{}(value);
149 case ease_type::QuadraticIn:
150 return ossia::easing::quadraticIn{}(value);
151 case ease_type::QuadraticOut:
152 return ossia::easing::quadraticOut{}(value);
153 case ease_type::QuadraticInOut:
154 return ossia::easing::quadraticInOut{}(value);
155 case ease_type::CubicIn:
156 return ossia::easing::cubicIn{}(value);
157 case ease_type::CubicOut:
158 return ossia::easing::cubicOut{}(value);
159 case ease_type::CubicInOut:
160 return ossia::easing::cubicInOut{}(value);
161 case ease_type::QuarticIn:
162 return ossia::easing::quarticIn{}(value);
163 case ease_type::QuarticOut:
164 return ossia::easing::quarticOut{}(value);
165 case ease_type::QuarticInOut:
166 return ossia::easing::quarticInOut{}(value);
167 case ease_type::QuinticIn:
168 return ossia::easing::quinticIn{}(value);
169 case ease_type::QuinticOut:
170 return ossia::easing::quinticOut{}(value);
171 case ease_type::QuinticInOut:
172 return ossia::easing::quinticInOut{}(value);
173 case ease_type::SineIn:
174 return ossia::easing::sineIn{}(value);
175 case ease_type::SineOut:
176 return ossia::easing::sineOut{}(value);
177 case ease_type::SineInOut:
178 return ossia::easing::sineInOut{}(value);
179 case ease_type::CircularIn:
180 return ossia::easing::circularIn{}(value);
181 case ease_type::CircularOut:
182 return ossia::easing::circularOut{}(value);
183 case ease_type::CircularInOut:
184 return ossia::easing::circularInOut{}(value);
185 case ease_type::ExponentialIn:
186 return ossia::easing::exponentialIn{}(value);
187 case ease_type::ExponentialOut:
188 return ossia::easing::exponentialOut{}(value);
189 case ease_type::ExponentialInOut:
190 return ossia::easing::exponentialInOut{}(value);
191 case ease_type::ElasticIn:
192 return ossia::easing::elasticIn{}(value);
193 case ease_type::ElasticOut:
194 return ossia::easing::elasticOut{}(value);
195 case ease_type::ElasticInOut:
196 return ossia::easing::elasticInOut{}(value);
197 case ease_type::PerlinInOut:
198 return ossia::easing::perlinInOut{}(value);
199 }
200 }
201
202 template <typename T>
203 static constexpr T ease_scalar(T v0, T v1, float t)
204 {
205 return ossia::easing::ease{}(v0, v1, t);
206 }
207
208 template <std::size_t N>
209 static constexpr std::array<float, N>
210 ease_vector(const std::array<float, N>& v0, const std::array<float, N>& v1, float t)
211 {
212 std::array<float, N> res;
213 for(int i = 0; i < N; i++)
214 res[i] = ossia::easing::ease{}(v0[i], v1[i], t);
215 return res;
216 }
217
218 static std::vector<ossia::value> ease_vector(
219 const std::vector<ossia::value>& v0, const std::vector<ossia::value>& v1, float t)
220 {
221 const int max_size = std::min(v0.size(), v1.size());
222
223 std::vector<ossia::value> result;
224 result.reserve(max_size);
225
226 for(int elem_idx = 0; elem_idx < max_size; ++elem_idx)
227 result.push_back(ease_values(v0[elem_idx], v1[elem_idx], t));
228
229 return result;
230 }
231
232 static ossia::value
233 ease_values(const ossia::value& v0, const ossia::value& v1, float weights)
234 {
235 if(!v0.valid())
236 return v1;
237 if(!v1.valid())
238 return v0;
239 const auto first_type = v0.get_type();
240 const auto second_type = v1.get_type();
241 const bool same_type = first_type == second_type;
242
243 if(!same_type)
244 return v0;
245
246 switch(first_type)
247 {
248 case ossia::val_type::FLOAT: {
249 return ease_scalar(*v0.target<float>(), *v1.target<float>(), weights);
250 }
251 case ossia::val_type::INT: {
252 return static_cast<int>(
253 std::round(ease_scalar(*v0.target<int>(), *v1.target<int>(), weights)));
254 }
255 case ossia::val_type::BOOL: {
256 return ease_scalar(*v0.target<bool>(), *v1.target<bool>(), weights) >= 0.5f;
257 }
258 case ossia::val_type::VEC2F: {
259 return ease_vector(
260 *v0.target<ossia::vec2f>(), *v1.target<ossia::vec2f>(), weights);
261 }
262 case ossia::val_type::VEC3F: {
263 return ease_vector(
264 *v0.target<ossia::vec3f>(), *v1.target<ossia::vec3f>(), weights);
265 }
266 case ossia::val_type::VEC4F: {
267 return ease_vector(
268 *v0.target<ossia::vec4f>(), *v1.target<ossia::vec4f>(), weights);
269 }
270 case ossia::val_type::LIST: {
271 return ease_vector(
272 *v0.target<std::vector<ossia::value>>(),
273 *v1.target<std::vector<ossia::value>>(), weights);
274 }
275 default:
276 return v0;
277 }
278 }
279
280 void operator()(int frames) noexcept
281 {
282 double deltaTime = frames;
283 if(!m_running)
284 {
285 outputs.output = m_current;
286 return;
287 }
288
289 m_elapsed += deltaTime;
290
291 if(m_elapsed >= m_duration)
292 {
293 outputs.output = m_current;
294 m_running = false;
295 }
296 else
297 {
298 double t = m_elapsed / m_duration;
299 double easedT = ease01(t);
300 outputs.output = ease_values(m_previous, m_current, easedT);
301 }
302 }
303
304 ossia::value m_previous{};
305 ossia::value m_current{};
306 double m_duration{96000.};
307 double m_elapsed{};
308 bool m_running{};
309};
310}
A collection of easing behaviours.
Definition Easetanbul.hpp:24