载入中...
搜索中...
未找到
Tween.cpp
浏览该文件的文档.
1#include "animation/Tween.h"
3
4#include "common/Exception.h"
5
6#include <algorithm>
7#include <cmath>
8
9#ifndef M_PI
10#define M_PI 3.14159265358979323846
11#endif
12
13namespace eve::animation {
14
15Tween::Tween(float duration) { setDuration(duration); }
16
18 if (owner_) owner_->unregisterTween(this);
19}
20
21Tween::Track &Tween::ensureTrack(const std::string &name) {
22 auto it = tracks_.find(name);
23 if (it != tracks_.end()) return it->second;
24 order_.push_back(name);
25 return tracks_[name];
26}
27
28const Tween::Track *Tween::findTrack(const std::string &name) const {
29 auto it = tracks_.find(name);
30 if (it == tracks_.end()) return nullptr;
31 return &it->second;
32}
33
34void Tween::setFrom(const std::string &name, float value) {
35 Track &tr = ensureTrack(name);
36 tr.from = value;
37 tr.hasFrom = true;
38 tr.isAngle = false;
39 if (!tr.resolved) tr.current = value;
40}
41
42void Tween::setTo(const std::string &name, float value) {
43 Track &tr = ensureTrack(name);
44 tr.to = value;
45 tr.endMode = EndMode::Absolute;
46 tr.isAngle = false;
47 tr.resolved = false;
48}
49
50void Tween::setDelta(const std::string &name, float delta) {
51 Track &tr = ensureTrack(name);
52 tr.delta = delta;
53 tr.endMode = EndMode::Delta;
54 tr.isAngle = false;
55 tr.resolved = false;
56}
57
58void Tween::setFromAngle(const std::string &name, float radians) {
59 Track &tr = ensureTrack(name);
60 tr.from = radians;
61 tr.hasFrom = true;
62 tr.isAngle = true;
63 if (!tr.resolved) tr.current = radians;
64}
65
66void Tween::setToAngle(const std::string &name, float radians) {
67 Track &tr = ensureTrack(name);
68 tr.to = radians;
69 tr.endMode = EndMode::Absolute;
70 tr.isAngle = true;
71 tr.resolved = false;
72}
73
74void Tween::setDeltaAngle(const std::string &name, float deltaRadians) {
75 Track &tr = ensureTrack(name);
76 tr.delta = deltaRadians;
77 tr.endMode = EndMode::Delta;
78 tr.isAngle = true;
79 tr.resolved = false;
80}
81
82bool Tween::has(const std::string &name) const { return findTrack(name) != nullptr; }
83
84float Tween::get(const std::string &name) const {
85 const Track *tr = findTrack(name);
86 if (!tr) throw Exception("Tween.get: unknown property '%s'", name.c_str());
87 return tr->current;
88}
89
90float Tween::getFrom(const std::string &name) const {
91 const Track *tr = findTrack(name);
92 if (!tr) throw Exception("Tween.getFrom: unknown property '%s'", name.c_str());
93 return tr->from;
94}
95
96float Tween::getTo(const std::string &name) const {
97 const Track *tr = findTrack(name);
98 if (!tr) throw Exception("Tween.getTo: unknown property '%s'", name.c_str());
99 if (tr->endMode == EndMode::Delta && !tr->resolved) return tr->from + tr->delta;
100 return tr->to;
101}
102
103float Tween::getDelta(const std::string &name) const {
104 const Track *tr = findTrack(name);
105 if (!tr) throw Exception("Tween.getDelta: unknown property '%s'", name.c_str());
106 if (tr->endMode == EndMode::Delta) return tr->delta;
107 return tr->to - tr->from;
108}
109
110void Tween::setDuration(float seconds) {
111 if (seconds < 0.f) throw Exception("Tween.setDuration: duration must be >= 0");
112 duration_ = seconds;
113}
114
115void Tween::setDelay(float seconds) {
116 if (seconds < 0.f) throw Exception("Tween.setDelay: delay must be >= 0");
117 delay_ = seconds;
118}
119
120void Tween::setEase(const std::string &kind) {
121 // Validate against known kinds (same set as Math.ease).
122 (void)ease(0.5f, kind);
123 ease_ = kind.empty() ? "linear" : kind;
124}
125
126void Tween::setRepeat(int count) {
127 if (count < -1) count = -1;
128 if (count == 0) count = 1;
129 repeat_ = count;
130}
131
132float Tween::clamp01(float t) { return std::clamp(t, 0.f, 1.f); }
133
134float Tween::lerpAngle(float a, float b, float t) {
135 float diff = b - a;
136 while (diff > float(M_PI)) diff -= float(2.0 * M_PI);
137 while (diff < -float(M_PI)) diff += float(2.0 * M_PI);
138 return a + diff * t;
139}
140
141float Tween::ease(float t, const std::string &kind) {
142 t = clamp01(t);
143 if (kind == "linear" || kind.empty()) return t;
144 if (kind == "inQuad") return t * t;
145 if (kind == "outQuad") return 1.f - (1.f - t) * (1.f - t);
146 if (kind == "inOutQuad")
147 return t < 0.5f ? 2.f * t * t : 1.f - std::pow(-2.f * t + 2.f, 2.f) * 0.5f;
148 if (kind == "inCubic") return t * t * t;
149 if (kind == "outCubic") return 1.f - std::pow(1.f - t, 3.f);
150 if (kind == "inOutCubic")
151 return t < 0.5f ? 4.f * t * t * t : 1.f - std::pow(-2.f * t + 2.f, 3.f) * 0.5f;
152 if (kind == "inSine") return 1.f - std::cos(t * float(M_PI) * 0.5f);
153 if (kind == "outSine") return std::sin(t * float(M_PI) * 0.5f);
154 if (kind == "inOutSine") return -(std::cos(float(M_PI) * t) - 1.f) * 0.5f;
155 if (kind == "inExpo") return t <= 0.f ? 0.f : std::pow(2.f, 10.f * t - 10.f);
156 if (kind == "outExpo") return t >= 1.f ? 1.f : 1.f - std::pow(2.f, -10.f * t);
157 if (kind == "inOutExpo") {
158 if (t <= 0.f) return 0.f;
159 if (t >= 1.f) return 1.f;
160 return t < 0.5f ? std::pow(2.f, 20.f * t - 10.f) * 0.5f
161 : (2.f - std::pow(2.f, -20.f * t + 10.f)) * 0.5f;
162 }
163 throw Exception("Tween.setEase: unknown kind '%s'", kind.c_str());
164}
165
166void Tween::resolveTracks() {
167 for (auto &kv : tracks_) {
168 Track &tr = kv.second;
169 if (!tr.hasFrom) {
170 tr.from = tr.current;
171 tr.hasFrom = true;
172 }
173 if (tr.endMode == EndMode::Delta) tr.to = tr.from + tr.delta;
174 tr.current = tr.from;
175 tr.resolved = true;
176 }
177}
178
179void Tween::applyProgress(float linearT) {
180 float t = reverse_ ? (1.f - clamp01(linearT)) : clamp01(linearT);
181 float e = ease(t, ease_);
182 for (auto &kv : tracks_) {
183 Track &tr = kv.second;
184 if (tr.isAngle)
185 tr.current = lerpAngle(tr.from, tr.to, e);
186 else
187 tr.current = tr.from + (tr.to - tr.from) * e;
188 }
189}
190
191float Tween::getProgress() const {
192 if (duration_ <= 0.f) return state_ == State::Finished ? 1.f : 0.f;
193 return clamp01(elapsed_ / duration_);
194}
195
197 float t = getProgress();
198 if (reverse_) t = 1.f - t;
199 return ease(t, ease_);
200}
201
203 resolveTracks();
204 elapsed_ = 0.f;
205 played_ = 0;
206 reverse_ = false;
207 delayLeft_ = delay_;
208 state_ = delay_ > 0.f ? State::Delayed : State::Running;
209 if (state_ == State::Running) applyProgress(0.f);
210}
211
213 if (state_ == State::Running || state_ == State::Delayed) state_ = State::Paused;
214}
215
217 if (state_ != State::Paused) return;
218 if (delayLeft_ > 0.f)
219 state_ = State::Delayed;
220 else
221 state_ = State::Running;
222}
223
225 state_ = State::Stopped;
226 delayLeft_ = 0.f;
227}
228
230 elapsed_ = 0.f;
231 played_ = 0;
232 reverse_ = false;
233 delayLeft_ = delay_;
234 state_ = State::Idle;
235 for (auto &kv : tracks_) {
236 Track &tr = kv.second;
237 tr.resolved = false;
238 if (tr.hasFrom) tr.current = tr.from;
239 }
240}
241
242void Tween::finishCycle() {
243 ++played_;
244 applyProgress(1.f);
245
246 const bool infinite = repeat_ < 0;
247 if (!infinite && played_ >= repeat_) {
248 state_ = State::Finished;
249 return;
250 }
251
252 if (yoyo_) reverse_ = !reverse_;
253 elapsed_ = 0.f;
254 // Snap to the start of the next cycle orientation.
255 applyProgress(0.f);
256 state_ = State::Running;
257}
258
259bool Tween::update(float dt) {
260 if (dt < 0.f) dt = 0.f;
261
262 if (state_ == State::Paused) return true;
263 if (state_ != State::Delayed && state_ != State::Running) return false;
264
265 if (state_ == State::Delayed) {
266 delayLeft_ -= dt;
267 if (delayLeft_ > 0.f) return true;
268 dt = -delayLeft_;
269 delayLeft_ = 0.f;
270 state_ = State::Running;
271 applyProgress(0.f);
272 if (dt <= 0.f) return true;
273 }
274
275 if (duration_ <= 0.f) {
276 applyProgress(1.f);
277 finishCycle();
278 return isActive();
279 }
280
281 elapsed_ += dt;
282 while (state_ == State::Running && elapsed_ >= duration_) {
283 float over = elapsed_ - duration_;
284 finishCycle();
285 if (state_ != State::Running) break;
286 elapsed_ = over;
287 }
288
289 if (state_ == State::Running) applyProgress(getProgress());
290 return isActive();
291}
292
293float Tween::evaluate(const std::string &name, float t) const {
294 const Track *tr = findTrack(name);
295 if (!tr) throw Exception("Tween.evaluate: unknown property '%s'", name.c_str());
296
297 float from = tr->from;
298 float to = tr->endMode == EndMode::Delta && !tr->resolved ? tr->from + tr->delta : tr->to;
299 if (!tr->hasFrom && !tr->resolved) from = tr->current;
300
301 float e = ease(clamp01(t), ease_);
302 if (tr->isAngle) return lerpAngle(from, to, e);
303 return from + (to - from) * e;
304}
305
306std::string Tween::getPropertyName(int index) const {
307 if (index < 0 || index >= static_cast<int>(order_.size()))
308 throw Exception("Tween.getPropertyName: index %d out of range (count=%d)", index,
309 static_cast<int>(order_.size()));
310 return order_[static_cast<size_t>(index)];
311}
312
313} // namespace eve::animation
Tok kind
std::string value
uint32_t a
uint32_t b
float tr
const char * name
Definition RockMesh.cpp:21
#define M_PI
Definition SpineAnim.cpp:16
void setFrom(const std::string &name, float value)
Definition Tween.cpp:34
void setDelay(float seconds)
Definition Tween.cpp:115
void setDeltaAngle(const std::string &name, float deltaRadians)
Definition Tween.cpp:74
void setToAngle(const std::string &name, float radians)
Definition Tween.cpp:66
void setEase(const std::string &kind)
Definition Tween.cpp:120
float getEasedProgress() const
Eased progress used for interpolation, [0,1].
Definition Tween.cpp:196
bool update(float dt)
Advance by dt seconds. Returns true while the tween is still active (delayed / running / paused).
Definition Tween.cpp:259
void setFromAngle(const std::string &name, float radians)
Definition Tween.cpp:58
Tween(float duration=1.f)
Definition Tween.cpp:15
float get(const std::string &name) const
Definition Tween.cpp:84
bool isActive() const
Definition Tween.h:71
float getProgress() const
Linear progress in the current cycle, [0,1].
Definition Tween.cpp:191
void setDuration(float seconds)
Definition Tween.cpp:110
std::string getPropertyName(int index) const
Definition Tween.cpp:306
float evaluate(const std::string &name, float t) const
Sample property at linear t in [0,1] using current from/to (no state change).
Definition Tween.cpp:293
float getFrom(const std::string &name) const
Definition Tween.cpp:90
void setRepeat(int count)
Play count: 1 = once (default), N = N cycles, -1 = infinite. Values < -1 are clamped to -1.
Definition Tween.cpp:126
void setDelta(const std::string &name, float delta)
Relative change: end = start + delta (resolved when start() runs).
Definition Tween.cpp:50
float getTo(const std::string &name) const
Definition Tween.cpp:96
void setTo(const std::string &name, float value)
Definition Tween.cpp:42
float getDelta(const std::string &name) const
Definition Tween.cpp:103
bool has(const std::string &name) const
Definition Tween.cpp:82