Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
animator_set.hpp
Go to the documentation of this file.
1#pragma once
2
3// AnimatorSetBuilder — compose animations in sequence or parallel groups.
4
5#include <algorithm>
8#include <vector>
9#include <memory>
10#include <functional>
11#include <string>
12#include <utility>
13
14namespace pulp::view {
15
18public:
19 virtual ~AnimationStep() = default;
20 virtual float advance(float dt) = 0;
21 virtual bool finished() const = 0;
22 virtual void reset() = 0;
23};
24
26class TweenStep : public AnimationStep {
27public:
28 TweenStep(Tween tween, std::function<void(float)> apply)
29 : tween_(tween), apply_(std::move(apply)) {}
30
31 float advance(float dt) override {
32 float v = tween_.advance(dt);
33 if (apply_) apply_(v);
34 return v;
35 }
36 bool finished() const override { return tween_.finished(); }
37 void reset() override { tween_.reset(); }
38
39private:
40 Tween tween_;
41 std::function<void(float)> apply_;
42};
43
46public:
47 void add(std::unique_ptr<AnimationStep> step) {
48 steps_.push_back(std::move(step));
49 }
50
51 float advance(float dt) override {
52 for (auto& s : steps_)
53 if (!s->finished())
54 s->advance(dt);
55 return 0;
56 }
57
58 bool finished() const override {
59 for (auto& s : steps_)
60 if (!s->finished()) return false;
61 return true;
62 }
63
64 void reset() override {
65 for (auto& s : steps_)
66 s->reset();
67 }
68
69private:
70 std::vector<std::unique_ptr<AnimationStep>> steps_;
71};
72
75public:
80 AnimatorSetBuilder& name(std::string n) {
81 name_ = std::move(n);
82 return *this;
83 }
84
85 const std::string& configured_name() const noexcept { return name_; }
86
88 AnimatorSetBuilder& then(float from, float to, float duration,
89 std::function<void(float)> apply,
91 steps_.push_back(std::make_unique<TweenStep>(
92 Tween(from, to, duration, ease), std::move(apply)));
93 return *this;
94 }
95
97 AnimatorSetBuilder& delay(float seconds) {
98 steps_.push_back(std::make_unique<TweenStep>(
99 Tween(0, 0, seconds), nullptr));
100 return *this;
101 }
102
105 parallel_ = std::make_unique<ParallelStep>();
106 return *this;
107 }
108
110 AnimatorSetBuilder& with(float from, float to, float duration,
111 std::function<void(float)> apply,
113 if (parallel_) {
114 parallel_->add(std::make_unique<TweenStep>(
115 Tween(from, to, duration, ease), std::move(apply)));
116 }
117 return *this;
118 }
119
122 if (parallel_) {
123 steps_.push_back(std::move(parallel_));
124 }
125 return *this;
126 }
127
129 std::vector<std::unique_ptr<AnimationStep>> build() {
130 return std::move(steps_);
131 }
132
134 class Runner {
135 public:
136 explicit Runner(std::vector<std::unique_ptr<AnimationStep>> steps,
137 std::string name = {})
138 : steps_(std::move(steps)) {
139 if (!name.empty()) {
140 provenance_.source_kind = "animator-set";
141 provenance_.source_id = std::move(name);
142 }
143 }
144
145 bool advance(float dt) {
146 while (current_ < static_cast<int>(steps_.size()) && dt > 0) {
147 steps_[current_]->advance(dt);
148 if (steps_[current_]->finished()) {
149 ++current_;
150 // Start the next step on the same frame; this runner does
151 // not track partial-frame leftover time per step.
152 } else {
153 break;
154 }
155 }
156 return current_ >= static_cast<int>(steps_.size());
157 }
158
159 bool finished() const {
160 return current_ >= static_cast<int>(steps_.size());
161 }
162
163 void reset() {
164 current_ = 0;
165 for (auto& s : steps_)
166 s->reset();
167 }
168
172 void publish(std::string view_name,
173 std::string metric_name,
174 double value,
175 motion::PublishOptions opts = {}) const {
176 if (provenance_.is_set() && !opts.provenance.is_set()) {
177 opts.provenance = provenance_;
178 }
179 motion::publish_value(std::move(view_name), std::move(metric_name),
180 value, opts);
181 }
182
183 const motion::Provenance& motion_provenance() const noexcept {
184 return provenance_;
185 }
186
187 private:
188 std::vector<std::unique_ptr<AnimationStep>> steps_;
189 int current_ = 0;
190 motion::Provenance provenance_;
191 };
192
194 return Runner(std::move(steps_), name_);
195 }
196
197private:
198 std::vector<std::unique_ptr<AnimationStep>> steps_;
199 std::unique_ptr<ParallelStep> parallel_;
200 std::string name_;
201};
202
203// ── Additional easing functions ─────────────────────────────────────────
204
205namespace easing {
207 inline float cubic_bezier(float t, float x1, float y1, float x2, float y2) {
208 // Simple Newton-Raphson approximation
209 float cx = 3.0f * x1;
210 float bx = 3.0f * (x2 - x1) - cx;
211 float ax = 1.0f - cx - bx;
212
213 float cy = 3.0f * y1;
214 float by = 3.0f * (y2 - y1) - cy;
215 float ay = 1.0f - cy - by;
216
217 // Solve for t given x using Newton's method
218 float guess = t;
219 for (int i = 0; i < 8; ++i) {
220 float x = ((ax * guess + bx) * guess + cx) * guess - t;
221 float dx = (3.0f * ax * guess + 2.0f * bx) * guess + cx;
222 if (std::abs(dx) < 1e-7f) break;
223 guess -= x / dx;
224 }
225
226 return ((ay * guess + by) * guess + cy) * guess;
227 }
228
230 inline float spring(float t, float stiffness = 100.0f, float damping = 10.0f) {
231 float omega = std::sqrt(stiffness);
232 float zeta = damping / (2.0f * omega);
233
234 if (zeta < 1.0f) {
235 // Underdamped — oscillates
236 float omega_d = omega * std::sqrt(1.0f - zeta * zeta);
237 return 1.0f - std::exp(-zeta * omega * t) *
238 (std::cos(omega_d * t) + (zeta * omega / omega_d) * std::sin(omega_d * t));
239 }
240
241 // Critically damped or overdamped
242 return 1.0f - (1.0f + omega * t) * std::exp(-omega * t);
243 }
244
246 inline float ease_in_out_back(float t) {
247 float s = 1.70158f * 1.525f;
248 if (t < 0.5f) {
249 return (2.0f * t * t * ((s + 1.0f) * 2.0f * t - s)) * 0.5f;
250 }
251 float u = 2.0f * t - 2.0f;
252 return (u * u * ((s + 1.0f) * u + s) + 2.0f) * 0.5f;
253 }
254}
255
256// ── 3D Math (Quaternion, Vector3D) ──────────────────────────────────────
257
258struct Vector3D {
259 float x = 0, y = 0, z = 0;
260
261 Vector3D operator+(const Vector3D& o) const { return {x + o.x, y + o.y, z + o.z}; }
262 Vector3D operator-(const Vector3D& o) const { return {x - o.x, y - o.y, z - o.z}; }
263 Vector3D operator*(float s) const { return {x * s, y * s, z * s}; }
264
265 float dot(const Vector3D& o) const { return x * o.x + y * o.y + z * o.z; }
266 Vector3D cross(const Vector3D& o) const {
267 return {y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x};
268 }
269 float length() const { return std::sqrt(x * x + y * y + z * z); }
271 float len = length();
272 return len > 0 ? Vector3D{x / len, y / len, z / len} : Vector3D{};
273 }
274};
275
277 float w = 1, x = 0, y = 0, z = 0;
278
279 static Quaternion identity() { return {1, 0, 0, 0}; }
280
281 static Quaternion from_axis_angle(const Vector3D& axis, float radians) {
282 float half = radians * 0.5f;
283 float s = std::sin(half);
284 auto n = axis.normalized();
285 return {std::cos(half), n.x * s, n.y * s, n.z * s};
286 }
287
289 return {
290 w * q.w - x * q.x - y * q.y - z * q.z,
291 w * q.x + x * q.w + y * q.z - z * q.y,
292 w * q.y - x * q.z + y * q.w + z * q.x,
293 w * q.z + x * q.y - y * q.x + z * q.w
294 };
295 }
296
297 Quaternion conjugate() const { return {w, -x, -y, -z}; }
298
299 float length() const { return std::sqrt(w * w + x * x + y * y + z * z); }
300
302 float len = length();
303 return len > 0 ? Quaternion{w / len, x / len, y / len, z / len} : identity();
304 }
305
307 static Quaternion slerp(const Quaternion& a, const Quaternion& b, float t) {
308 float dot = a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
309 Quaternion b2 = dot < 0 ? Quaternion{-b.w, -b.x, -b.y, -b.z} : b;
310 dot = std::abs(dot);
311
312 if (dot > 0.9995f) {
313 // Linear interpolation for very close quaternions
314 return Quaternion{
315 a.w + t * (b2.w - a.w),
316 a.x + t * (b2.x - a.x),
317 a.y + t * (b2.y - a.y),
318 a.z + t * (b2.z - a.z)
319 }.normalized();
320 }
321
322 float theta = std::acos(dot);
323 float sin_theta = std::sin(theta);
324 float wa = std::sin((1.0f - t) * theta) / sin_theta;
325 float wb = std::sin(t * theta) / sin_theta;
326
327 return Quaternion{
328 wa * a.w + wb * b2.w,
329 wa * a.x + wb * b2.x,
330 wa * a.y + wb * b2.y,
331 wa * a.z + wb * b2.z
332 };
333 }
334
336 Vector3D rotate(const Vector3D& v) const {
337 Quaternion p{0, v.x, v.y, v.z};
338 Quaternion result = (*this) * p * conjugate();
339 return {result.x, result.y, result.z};
340 }
341};
342
345public:
347 void begin_drag(float x, float y) {
348 dragging_ = true;
349 drag_start_ = project_to_sphere(x, y);
350 start_orientation_ = orientation_;
351 }
352
354 void update_drag(float x, float y) {
355 if (!dragging_) return;
356
357 Vector3D current = project_to_sphere(x, y);
358 Vector3D axis = drag_start_.cross(current);
359 float dot = drag_start_.dot(current);
360
361 if (axis.length() > 1e-6f) {
362 float angle = std::acos(std::clamp(dot, -1.0f, 1.0f));
363 Quaternion rotation = Quaternion::from_axis_angle(axis.normalized(), angle);
364 orientation_ = rotation * start_orientation_;
365 }
366 }
367
369 void end_drag() { dragging_ = false; }
370
372 const Quaternion& orientation() const { return orientation_; }
373
375 void reset() {
376 orientation_ = Quaternion::identity();
377 dragging_ = false;
378 drag_start_ = {};
379 start_orientation_ = Quaternion::identity();
380 }
381
382private:
383 Quaternion orientation_ = Quaternion::identity();
384 Quaternion start_orientation_;
385 Vector3D drag_start_;
386 bool dragging_ = false;
387
388 static Vector3D project_to_sphere(float x, float y) {
389 float d = x * x + y * y;
390 if (d < 1.0f)
391 return {x, y, std::sqrt(1.0f - d)};
392 float scale = 1.0f / std::sqrt(d);
393 return {x * scale, y * scale, 0};
394 }
395};
396
397} // namespace pulp::view
A step in an animation sequence — either a tween or a parallel group.
Definition animator_set.hpp:17
virtual void reset()=0
virtual ~AnimationStep()=default
virtual bool finished() const =0
virtual float advance(float dt)=0
Run the built animation. Advances through sequence, returns true when all done.
Definition animator_set.hpp:134
const motion::Provenance & motion_provenance() const noexcept
Definition animator_set.hpp:183
bool finished() const
Definition animator_set.hpp:159
void publish(std::string view_name, std::string metric_name, double value, motion::PublishOptions opts={}) const
Publish the runner's current scalar through the motion publish channel, stamping the animator-set nam...
Definition animator_set.hpp:172
bool advance(float dt)
Definition animator_set.hpp:145
void reset()
Definition animator_set.hpp:163
Runner(std::vector< std::unique_ptr< AnimationStep > > steps, std::string name={})
Definition animator_set.hpp:136
Builder for composing animation sequences and parallel groups.
Definition animator_set.hpp:74
Runner build_runner()
Definition animator_set.hpp:193
std::vector< std::unique_ptr< AnimationStep > > build()
Build and return the step sequence.
Definition animator_set.hpp:129
AnimatorSetBuilder & with(float from, float to, float duration, std::function< void(float)> apply, EasingFunction ease=easing::linear)
Add a tween to the current parallel group.
Definition animator_set.hpp:110
AnimatorSetBuilder & end_parallel()
End parallel group and add it to the sequence.
Definition animator_set.hpp:121
AnimatorSetBuilder & then(float from, float to, float duration, std::function< void(float)> apply, EasingFunction ease=easing::linear)
Add a tween that runs in sequence.
Definition animator_set.hpp:88
AnimatorSetBuilder & begin_parallel()
Start a parallel group — all tweens added until end_parallel() run together.
Definition animator_set.hpp:104
AnimatorSetBuilder & delay(float seconds)
Add a delay (tween from 0 to 0).
Definition animator_set.hpp:97
AnimatorSetBuilder & name(std::string n)
Assign a logical name to this animator set.
Definition animator_set.hpp:80
const std::string & configured_name() const noexcept
Definition animator_set.hpp:85
Draggable 3D orientation — arcball rotation from mouse drag.
Definition animator_set.hpp:344
void begin_drag(float x, float y)
Begin a drag at the given 2D screen position (normalized -1 to 1).
Definition animator_set.hpp:347
const Quaternion & orientation() const
Current orientation quaternion.
Definition animator_set.hpp:372
void reset()
Reset to identity (also cancels any active drag).
Definition animator_set.hpp:375
void end_drag()
End the drag.
Definition animator_set.hpp:369
void update_drag(float x, float y)
Update during drag.
Definition animator_set.hpp:354
Parallel group — all steps run simultaneously.
Definition animator_set.hpp:45
bool finished() const override
Definition animator_set.hpp:58
void add(std::unique_ptr< AnimationStep > step)
Definition animator_set.hpp:47
void reset() override
Definition animator_set.hpp:64
float advance(float dt) override
Definition animator_set.hpp:51
void reset() override
Definition animator_set.hpp:37
float advance(float dt) override
Definition animator_set.hpp:31
bool finished() const override
Definition animator_set.hpp:36
TweenStep(Tween tween, std::function< void(float)> apply)
Definition animator_set.hpp:28
Definition animation.hpp:72
Agent-first motion observability: sample view geometry, scroll state, and arbitrary scalar values at ...
float spring(float t, float stiffness=100.0f, float damping=10.0f)
Spring physics easing (damped oscillation).
Definition animator_set.hpp:230
float linear(float t)
Definition animation.hpp:26
float cubic_bezier(float t, float x1, float y1, float x2, float y2)
Cubic bezier easing (simplified — approximates CSS cubic-bezier).
Definition animator_set.hpp:207
float ease_in_out_back(float t)
Ease-in-out back (slight overshoot).
Definition animator_set.hpp:246
void publish_value(std::string view_name, std::string metric_name, double value, PublishOptions opts={})
Single-component publish.
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
@ s
Definition input_events.hpp:41
@ a
Definition input_events.hpp:39
@ y
Definition input_events.hpp:42
@ q
Definition input_events.hpp:41
@ u
Definition input_events.hpp:41
@ n
Definition input_events.hpp:40
@ d
Definition input_events.hpp:39
@ p
Definition input_events.hpp:40
@ i
Definition input_events.hpp:40
@ b
Definition input_events.hpp:39
@ x
Definition input_events.hpp:41
@ v
Definition input_events.hpp:41
@ o
Definition input_events.hpp:40
@ t
Definition input_events.hpp:41
@ scale
Definition css_animation.hpp:126
float(*)(float) EasingFunction
Definition animation.hpp:51
STL namespace.
Definition animator_set.hpp:276
Vector3D rotate(const Vector3D &v) const
Rotate a vector by this quaternion.
Definition animator_set.hpp:336
Quaternion conjugate() const
Definition animator_set.hpp:297
static Quaternion slerp(const Quaternion &a, const Quaternion &b, float t)
Spherical linear interpolation.
Definition animator_set.hpp:307
float y
Definition animator_set.hpp:277
static Quaternion identity()
Definition animator_set.hpp:279
float length() const
Definition animator_set.hpp:299
Quaternion normalized() const
Definition animator_set.hpp:301
float z
Definition animator_set.hpp:277
static Quaternion from_axis_angle(const Vector3D &axis, float radians)
Definition animator_set.hpp:281
float x
Definition animator_set.hpp:277
Quaternion operator*(const Quaternion &q) const
Definition animator_set.hpp:288
float w
Definition animator_set.hpp:277
Definition animator_set.hpp:258
Vector3D normalized() const
Definition animator_set.hpp:270
Vector3D operator-(const Vector3D &o) const
Definition animator_set.hpp:262
Vector3D cross(const Vector3D &o) const
Definition animator_set.hpp:266
float dot(const Vector3D &o) const
Definition animator_set.hpp:265
Vector3D operator+(const Vector3D &o) const
Definition animator_set.hpp:261
float x
Definition animator_set.hpp:259
float length() const
Definition animator_set.hpp:269
Vector3D operator*(float s) const
Definition animator_set.hpp:263
float y
Definition animator_set.hpp:259
float z
Definition animator_set.hpp:259
Definition motion.hpp:77
bool is_set() const noexcept
Definition motion.hpp:85
Definition motion.hpp:181