12#define M_PI 3.14159265358979323846
20 if (owner_) owner_->unregisterTween(
this);
23Tween::Track &Tween::ensureTrack(
const std::string &
name) {
24 auto it = tracks_.find(
name);
25 if (it != tracks_.end())
return it->second;
26 order_.push_back(
name);
30const Tween::Track *Tween::findTrack(
const std::string &
name)
const {
31 auto it = tracks_.find(
name);
32 if (it == tracks_.end())
return nullptr;
37 Track &
tr = ensureTrack(
name);
45 Track &
tr = ensureTrack(
name);
47 tr.endMode = EndMode::Absolute;
53 Track &
tr = ensureTrack(
name);
55 tr.endMode = EndMode::Delta;
61 Track &
tr = ensureTrack(
name);
65 if (!
tr.resolved)
tr.current = radians;
69 Track &
tr = ensureTrack(
name);
71 tr.endMode = EndMode::Absolute;
77 Track &
tr = ensureTrack(
name);
78 tr.delta = deltaRadians;
79 tr.endMode = EndMode::Delta;
87 const Track *
tr = findTrack(
name);
88 if (!
tr)
throw Exception(
"Tween.get: unknown property '%s'",
name.c_str());
93 const Track *
tr = findTrack(
name);
94 if (!
tr)
throw Exception(
"Tween.getFrom: unknown property '%s'",
name.c_str());
99 const Track *
tr = findTrack(
name);
100 if (!
tr)
throw Exception(
"Tween.getTo: unknown property '%s'",
name.c_str());
101 if (
tr->endMode == EndMode::Delta && !
tr->resolved)
return tr->from +
tr->delta;
106 const Track *
tr = findTrack(
name);
107 if (!
tr)
throw Exception(
"Tween.getDelta: unknown property '%s'",
name.c_str());
108 if (
tr->endMode == EndMode::Delta)
return tr->delta;
109 return tr->to -
tr->from;
113 if (seconds < 0.f)
throw Exception(
"Tween.setDuration: duration must be >= 0");
118 if (seconds < 0.f)
throw Exception(
"Tween.setDelay: delay must be >= 0");
124 (void)ease(0.5f,
kind);
125 ease_ =
kind.empty() ?
"linear" :
kind;
134float Tween::clamp01(
float t) {
return std::clamp(
t, 0.f, 1.f); }
136float Tween::lerpAngle(
float a,
float b,
float t) {
138 while (diff >
float(
M_PI)) diff -= float(2.0 *
M_PI);
139 while (diff < -
float(
M_PI)) diff += float(2.0 *
M_PI);
143float Tween::ease(
float t,
const std::string &
kind) {
145 if (
kind ==
"linear" ||
kind.empty())
return t;
146 if (
kind ==
"inQuad")
return t *
t;
147 if (
kind ==
"outQuad")
return 1.f - (1.f -
t) * (1.f -
t);
148 if (
kind ==
"inOutQuad")
149 return t < 0.5f ? 2.f *
t *
t : 1.f - std::pow(-2.f *
t + 2.f, 2.f) * 0.5f;
150 if (
kind ==
"inCubic")
return t *
t *
t;
151 if (
kind ==
"outCubic")
return 1.f - std::pow(1.f -
t, 3.f);
152 if (
kind ==
"inOutCubic")
153 return t < 0.5f ? 4.f *
t *
t *
t : 1.f - std::pow(-2.f *
t + 2.f, 3.f) * 0.5f;
154 if (
kind ==
"inSine")
return 1.f - std::cos(
t *
float(
M_PI) * 0.5f);
155 if (
kind ==
"outSine")
return std::sin(
t *
float(
M_PI) * 0.5f);
156 if (
kind ==
"inOutSine")
return -(std::cos(
float(
M_PI) *
t) - 1.f) * 0.5f;
157 if (
kind ==
"inExpo")
return t <= 0.f ? 0.f : std::pow(2.f, 10.f *
t - 10.f);
158 if (
kind ==
"outExpo")
return t >= 1.f ? 1.f : 1.f - std::pow(2.f, -10.f *
t);
159 if (
kind ==
"inOutExpo") {
160 if (
t <= 0.f)
return 0.f;
161 if (
t >= 1.f)
return 1.f;
162 return t < 0.5f ? std::pow(2.f, 20.f *
t - 10.f) * 0.5f
163 : (2.f - std::pow(2.f, -20.f *
t + 10.f)) * 0.5f;
165 throw Exception(
"Tween.setEase: unknown kind '%s'",
kind.c_str());
168void Tween::resolveTracks() {
169 for (
auto &kv : tracks_) {
170 Track &
tr = kv.second;
172 tr.from =
tr.current;
175 if (
tr.endMode == EndMode::Delta)
tr.to =
tr.from +
tr.delta;
176 tr.current =
tr.from;
181void Tween::applyProgress(
float linearT) {
182 float t = reverse_ ? (1.f - clamp01(linearT)) : clamp01(linearT);
183 float e = ease(
t, ease_);
184 for (
auto &kv : tracks_) {
185 Track &
tr = kv.second;
187 tr.current = lerpAngle(
tr.from,
tr.to, e);
189 tr.current =
tr.from + (
tr.to -
tr.from) * e;
194 if (duration_ <= 0.f)
return state_ == State::Finished ? 1.f : 0.f;
195 return clamp01(elapsed_ / duration_);
200 if (reverse_)
t = 1.f -
t;
201 return ease(
t, ease_);
210 state_ = delay_ > 0.f ? State::Delayed : State::Running;
211 if (state_ == State::Running) applyProgress(0.f);
215 if (state_ == State::Running || state_ == State::Delayed) state_ = State::Paused;
219 if (state_ != State::Paused)
return;
220 if (delayLeft_ > 0.f)
221 state_ = State::Delayed;
223 state_ = State::Running;
227 state_ = State::Stopped;
236 state_ = State::Idle;
237 for (
auto &kv : tracks_) {
238 Track &
tr = kv.second;
240 if (
tr.hasFrom)
tr.current =
tr.from;
244void Tween::finishCycle() {
248 const bool infinite = repeat_ < 0;
249 if (!infinite && played_ >= repeat_) {
250 state_ = State::Finished;
254 if (yoyo_) reverse_ = !reverse_;
258 state_ = State::Running;
261bool Tween::updateUnchecked(
float dt) {
262 if (dt < 0.f) dt = 0.f;
264 if (state_ == State::Paused)
return true;
265 if (state_ != State::Delayed && state_ != State::Running)
return false;
267 if (state_ == State::Delayed) {
269 if (delayLeft_ > 0.f)
return true;
272 state_ = State::Running;
274 if (dt <= 0.f)
return true;
277 if (duration_ <= 0.f) {
284 while (state_ == State::Running && elapsed_ >= duration_) {
285 float over = elapsed_ - duration_;
287 if (state_ != State::Running)
break;
291 if (state_ == State::Running) applyProgress(
getProgress());
298 updateUnchecked(std::move(seconds).takeValue());
299 lastTick_ =
step.tick;
307 step.ignore(
"legacy Tween update");
310 auto result =
advance(std::move(
step).takeValue());
311 result.ignore(
"legacy Tween update");
316 const Track *
tr = findTrack(
name);
317 if (!
tr)
throw Exception(
"Tween.evaluate: unknown property '%s'",
name.c_str());
320 float to =
tr->endMode == EndMode::Delta && !
tr->resolved ?
tr->from +
tr->delta :
tr->to;
321 if (!
tr->hasFrom && !
tr->resolved)
from =
tr->current;
323 float e = ease(clamp01(
t), ease_);
324 if (
tr->isAngle)
return lerpAngle(
from,
to, e);
329 if (index < 0 || index >=
static_cast<int>(order_.size()))
330 throw Exception(
"Tween.getPropertyName: index %d out of range (count=%d)",
index,
331 static_cast<int>(order_.size()));
332 return order_[
static_cast<size_t>(
index)];
HexCoordinates to
Cell the unit walks towards on this segment.
EVENGINE_API_FOUNDATION public API.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
void setFrom(const std::string &name, float value)
Sets the from.
void setDelay(float seconds)
Sets the delay.
void setDeltaAngle(const std::string &name, float deltaRadians)
Sets the delta angle.
void setToAngle(const std::string &name, float radians)
Sets the to angle.
void setEase(const std::string &kind)
Sets the ease.
float getEasedProgress() const
Eased progress used for interpolation, [0,1].
bool update(float dt)
Advance by dt seconds; legacy facade that forwards to advance().
void setFromAngle(const std::string &name, float radians)
Sets the from angle.
Tween(float duration=1.f)
Tween.
float get(const std::string &name) const
Returns the value.
bool isActive() const
True when active.
float getProgress() const
Linear progress in the current cycle, [0,1].
void setDuration(float seconds)
Sets the duration.
std::string getPropertyName(int index) const
Returns the property name.
float evaluate(const std::string &name, float t) const
Sample property at linear t in [0,1] using current from/to (no state change).
float getFrom(const std::string &name) const
Returns the from.
eve::Result< void > advance(const eve::SimulationStep &step)
Advance by one scheduler-owned deterministic step.
void setRepeat(int count)
Play count: 1 = once (default), N = N cycles, -1 = infinite. Values < -1 are clamped to -1.
void setDelta(const std::string &name, float delta)
Relative change: end = start + delta (resolved when start() runs).
float getTo(const std::string &name) const
Returns the to.
void setTo(const std::string &name, float value)
Sets the to.
float getDelta(const std::string &name) const
Returns the delta.
bool has(const std::string &name) const
True when active.
eve::Result< eve::SimulationStep > legacyStep(float seconds, bool hasLastTick, eve::SimulationTick lastTick, const char *owner)
Convert a legacy seconds call to the next local scheduler step.
eve::Result< float > secondsForStep(const eve::SimulationStep &step, bool hasLastTick, eve::SimulationTick lastTick, const char *owner)
Validate a scheduler step before an animation object mutates state.
One deterministic fixed-step emitted by SimulationClock.