载入中...
搜索中...
未找到
ControlAnim.cpp
浏览该文件的文档.
2
4#include "common/Exception.h"
5
6#include <algorithm>
7
8namespace eve::animation {
9
10ControlAnim::ControlAnim(float frequencyHz, float dampingZeta, float response)
11 : frequencyHz_(frequencyHz), dampingZeta_(dampingZeta), response_(response) {
12 refreshCoeffs();
13}
14
15void ControlAnim::setFrequency(float frequencyHz) {
16 frequencyHz_ = frequencyHz;
17 refreshCoeffs();
18}
19
20void ControlAnim::setDamping(float dampingZeta) {
21 dampingZeta_ = dampingZeta;
22 refreshCoeffs();
23}
24
25void ControlAnim::setResponse(float response) {
26 response_ = response;
27 refreshCoeffs();
28}
29
30void ControlAnim::setIntegrator(const std::string &kind) {
31 if (kind == "secondOrder") {
32 integrator_ = Integrator::SecondOrder;
33 } else if (kind == "spring") {
34 integrator_ = Integrator::Spring;
35 } else if (kind == "pd") {
36 integrator_ = Integrator::Pd;
37 } else {
38 throw Exception("ControlAnim::setIntegrator: unknown kind '%s' (expected secondOrder|spring|pd)",
39 kind.c_str());
40 }
41}
42
43std::string ControlAnim::getIntegrator() const {
44 switch (integrator_) {
45 case Integrator::SecondOrder: return "secondOrder";
46 case Integrator::Spring: return "spring";
47 case Integrator::Pd: return "pd";
48 }
49 return "secondOrder";
50}
51
52void ControlAnim::refreshCoeffs() {
53 coeffs_ = makeSecondOrderCoeffs(frequencyHz_, dampingZeta_, response_);
54 pdGainsFromOmegaZeta(hzToOmega(frequencyHz_), dampingZeta_, kp_, kd_);
55}
56
57ControlAnim::Channel &ControlAnim::ensure(const std::string &name) {
58 auto it = channels_.find(name);
59 if (it != channels_.end()) return it->second;
60 Channel ch;
61 channels_.emplace(name, ch);
62 order_.push_back(name);
63 return channels_[name];
64}
65
66void ControlAnim::set(const std::string &name, float value) {
67 Channel &ch = ensure(name);
68 ch.y = value;
69 ch.yd = 0.f;
70 ch.x = value;
71 ch.xp = value;
72 ch.xdExplicit = 0.f;
73 ch.hasXd = false;
74 ch.hasPrevX = true;
75}
76
77void ControlAnim::setTarget(const std::string &name, float value) {
78 Channel &ch = ensure(name);
79 if (!ch.hasPrevX) {
80 ch.y = value;
81 ch.yd = 0.f;
82 ch.xp = value;
83 ch.hasPrevX = true;
84 }
85 ch.x = value;
86}
87
88void ControlAnim::setTargetVelocity(const std::string &name, float velocity) {
89 Channel &ch = ensure(name);
90 ch.xdExplicit = velocity;
91 ch.hasXd = true;
92}
93
94void ControlAnim::impulse(const std::string &name, float deltaVelocity) {
95 Channel &ch = ensure(name);
96 ch.yd += deltaVelocity;
97}
98
99bool ControlAnim::has(const std::string &name) const {
100 return channels_.find(name) != channels_.end();
101}
102
103float ControlAnim::get(const std::string &name) const {
104 auto it = channels_.find(name);
105 return it == channels_.end() ? 0.f : it->second.y;
106}
107
108float ControlAnim::getVelocity(const std::string &name) const {
109 auto it = channels_.find(name);
110 return it == channels_.end() ? 0.f : it->second.yd;
111}
112
113float ControlAnim::getTarget(const std::string &name) const {
114 auto it = channels_.find(name);
115 return it == channels_.end() ? 0.f : it->second.x;
116}
117
119 channels_.clear();
120 order_.clear();
121}
122
123void ControlAnim::remove(const std::string &name) {
124 channels_.erase(name);
125 order_.erase(std::remove(order_.begin(), order_.end(), name), order_.end());
126}
127
128std::string ControlAnim::getPropertyName(int index) const {
129 if (index < 0 || index >= static_cast<int>(order_.size())) return {};
130 return order_[static_cast<size_t>(index)];
131}
132
133void ControlAnim::updateUnchecked(float dt) {
134 if (dt <= 0.f) return;
135 const float omega = hzToOmega(frequencyHz_);
136
137 for (auto &kv : channels_) {
138 Channel &ch = kv.second;
139 float xd = 0.f;
140 if (ch.hasXd) {
141 xd = ch.xdExplicit;
142 } else if (ch.hasPrevX && dt > 1e-8f) {
143 xd = (ch.x - ch.xp) / dt;
144 }
145 ch.xp = ch.x;
146 ch.hasPrevX = true;
147
148 switch (integrator_) {
149 case Integrator::SecondOrder:
150 stepSecondOrder(dt, ch.x, xd, coeffs_, ch.y, ch.yd);
151 break;
152 case Integrator::Spring:
153 stepDampedSpring(dt, ch.x, omega, dampingZeta_, ch.y, ch.yd);
154 break;
155 case Integrator::Pd:
156 stepPd(dt, ch.x, xd, kp_, kd_, ch.y, ch.yd);
157 break;
158 }
159 }
160}
161
163 auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "ControlAnim");
164 if (!seconds) return eve::Result<void>::failure(seconds.status());
165 updateUnchecked(std::move(seconds).takeValue());
166 lastTick_ = step.tick;
167 hasLastTick_ = true;
169}
170
171void ControlAnim::update(float dt) {
172 auto step = detail::legacyStep(dt, hasLastTick_, lastTick_, "ControlAnim");
173 if (!step) {
174 step.ignore("legacy ControlAnim update");
175 return;
176 }
177 advance(std::move(step).takeValue()).ignore("legacy ControlAnim update");
178}
179
180} // namespace eve::animation
double value
TokenKind kind
std::string name
float step
Definition TreeMesh.cpp:314
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
float getVelocity(const std::string &name) const
Returns the velocity.
void remove(const std::string &name)
Removes .
ControlAnim(float frequencyHz=3.f, float dampingZeta=1.f, float response=1.f)
Control anim.
void set(const std::string &name, float value)
Create/update a channel: current value snaps to value, velocity cleared.
void setTarget(const std::string &name, float value)
Move the tracking target (does not snap state). Creates channel if missing.
eve::Result< void > advance(const eve::SimulationStep &step)
Advance all channels by one scheduler-owned deterministic step.
float get(const std::string &name) const
Returns the value.
void setDamping(float dampingZeta)
Sets the damping.
void setFrequency(float frequencyHz)
Sets the frequency.
bool has(const std::string &name) const
True when active.
float getTarget(const std::string &name) const
Returns the target.
void setResponse(float response)
Sets the response.
std::string getIntegrator() const
Returns the integrator.
std::string getPropertyName(int index) const
Returns the property name.
void setIntegrator(const std::string &kind)
Integrator: "secondOrder" | "spring" | "pd". Invalid → exception.
void update(float dt)
Legacy seconds facade; explicitly forwards to advance().
void setTargetVelocity(const std::string &name, float velocity)
Optional explicit target velocity (used by secondOrder / pd).
void impulse(const std::string &name, float deltaVelocity)
Inject an instantaneous velocity impulse (procedural “hit”).
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.
void stepPd(float dt, float target, float targetVel, float kp, float kd, float &y, float &yd)
Semi-implicit Euler PD step (unit mass): a = Kp (target − y) + Kd (targetVel − yd),...
void stepSecondOrder(float dt, float x, float xd, const SecondOrderCoeffs &c, float &y, float &yd)
Stable semi-implicit Euler step for a second-order tracker. Estimates input velocity from consecutive...
void pdGainsFromOmegaZeta(float omega, float zeta, float &kp, float &kd)
Unit-mass PD gains from natural frequency ω (rad/s) and damping ratio ζ.
SecondOrderCoeffs makeSecondOrderCoeffs(float frequencyHz, float dampingZeta, float response)
Build second-order coefficients from frequency (Hz), damping ζ, response r.
float hzToOmega(float frequencyHz)
ω (rad/s) from frequency in Hz.
void stepDampedSpring(float dt, float target, float angularFrequency, float dampingRatio, float &pos, float &vel)
Advance position/velocity relative to a set-point using closed-form spring step.
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158