13#include <PhosphorAnimation/phosphoranimation_export.h>
16#include <QLoggingCategory>
35Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcAnimatedValue, PHOSPHORANIMATION_EXPORT)
63 qCWarning(lcAnimatedValue) <<
"start() rejected: null clock";
68 qCWarning(lcAnimatedValue) <<
"start() rejected: non-finite from/to";
72 m_from = std::move(from);
74 m_spec = std::move(spec);
79 m_state.duration = 1.0;
81 m_lastTickTime.reset();
82 m_loggedStatelessDegrade =
false;
83 m_loggedNegativeDt =
false;
84 m_loggedTransformDegrade =
false;
85 m_loggedEpochMismatch =
false;
90 m_isAnimating =
false;
97 m_spec.clock->requestFrame();
106 qCWarning(lcAnimatedValue) <<
"retarget() rejected: no stored spec (never started)";
110 qCWarning(lcAnimatedValue) <<
"retarget() rejected: non-finite newTo";
114 const T newFrom = m_current;
118 const auto curve = effectiveCurve();
119 const bool stateful = curve && curve->isStateful();
121 qreal newVelocity = 0.0;
124 if constexpr (std::same_as<T, QTransform>) {
129 if (!pureTranslate) {
130 if (!m_loggedTransformDegrade) {
131 qCDebug(lcAnimatedValue) <<
"QTransform PreserveVelocity degrading to PreservePosition: "
132 <<
"non-translate components present (Frobenius metric mixes units)";
133 m_loggedTransformDegrade =
true;
146 newVelocity = (m_state.velocity * oldDistance) / newDistance;
147 }
else if (!stateful && !m_loggedStatelessDegrade) {
148 qCDebug(lcAnimatedValue) <<
"PreserveVelocity degrading to PreservePosition on stateless curve"
149 << (curve ? curve->typeId() : QStringLiteral(
"null"));
150 m_loggedStatelessDegrade =
true;
162 m_to = std::move(newTo);
165 m_state.velocity = newVelocity;
167 m_state.startValue = 0.0;
169 m_lastTickTime.reset();
171 if (qFuzzyIsNull(newDistance)) {
174 m_state.velocity = 0.0;
175 m_isAnimating =
false;
180 m_isAnimating =
true;
181 m_isComplete =
false;
182 m_spec.clock->requestFrame();
189 return retarget(std::move(newTo), m_spec.retargetPolicy);
200 m_isAnimating =
false;
201 m_isComplete =
false;
205 template<ColorSpace OtherSpace>
209 template<ColorSpace OtherSpace>
215 if (!m_isAnimating) {
220 m_state.velocity = 0.0;
221 m_isAnimating =
false;
223 if (m_spec.onValueChanged) {
224 m_spec.onValueChanged(m_current);
227 if (m_isComplete && m_spec.onComplete) {
236 if (!m_isAnimating || !m_spec.clock) {
240 const auto now = m_spec.clock->now();
244 m_lastTickTime = now;
246 if (m_spec.onValueChanged) {
247 m_spec.onValueChanged(m_current);
250 m_spec.clock->requestFrame();
255 const auto elapsed = now - *m_startTime;
256 const qreal dtSeconds = std::chrono::duration<qreal>(now - *m_lastTickTime).count();
257 m_lastTickTime = now;
259 if (dtSeconds < 0.0) {
260 if (!m_loggedNegativeDt) {
261 qCWarning(lcAnimatedValue) <<
"negative dt from clock (" << dtSeconds
262 <<
"s) — treating as zero-step. Monotonicity contract violated.";
263 m_loggedNegativeDt =
true;
265 m_spec.clock->requestFrame();
269 const auto curve = effectiveCurve();
271 bool complete =
false;
273 if (curve->isStateful()) {
274 curve->step(dtSeconds, m_state, 1.0);
276 if (m_state.value >= 1.0 && qAbs(m_state.velocity) <= 1.0e-6) {
278 }
else if (elapsed > kSafetyCap) {
279 qCWarning(lcAnimatedValue) <<
"stateful curve exceeded safety cap; forcing completion";
283 const qreal durationMs = m_spec.profile.effectiveDuration();
284 const qreal elapsedMs = std::chrono::duration<qreal, std::milli>(elapsed).count();
285 if (!std::isfinite(durationMs) || durationMs <= 0.0 || elapsedMs >= durationMs) {
289 const qreal t = elapsedMs / durationMs;
290 m_state.value = curve->evaluate(t);
297 m_state.velocity = 0.0;
298 m_isAnimating =
false;
300 if (m_spec.onValueChanged) {
301 m_spec.onValueChanged(m_current);
304 if (m_isComplete && m_spec.onComplete) {
308 m_current = lerpStateValue();
309 if (m_spec.onValueChanged) {
310 m_spec.onValueChanged(m_current);
313 m_spec.clock->requestFrame();
324 return m_state.velocity;
332 return m_isAnimating;
354 QRectF bounds() const
355 requires detail::PositionalGeometric<T>;
357 QRectF boundsAt(QPointF anchor) const
358 requires detail::SizeGeometric<T>;
360 std::pair<QSizeF, QSizeF> sweptSize() const
361 requires detail::SizeGeometric<T>;
364 requires std::same_as<T, QRectF>;
366 std::pair<T, T> sweptRange() const
367 requires detail::ScalarValue<T>;
369 static constexpr std::chrono::seconds safetyCap() noexcept
375 static constexpr std::chrono::seconds kSafetyCap{60};
376 static constexpr int kOvershootSamples = 50;
378 T lerpStateValue()
const
383 return Interpolate<T>::lerp(m_from, m_to, m_state.value);
387 std::shared_ptr<const Curve> effectiveCurve()
const
390 return m_cachedCurve;
395 QRectF boundsImpl() const
396 requires detail::PositionalGeometric<T>;
398 std::pair<QSizeF, QSizeF> sweptSizeImpl() const
399 requires detail::SizeGeometric<T>;
401 template<typename Sampler>
402 void sampleOvershoots(qreal& minX, qreal& minY, qreal& maxX, qreal& maxY, const Sampler& sampleAt) const;
404 std::pair<T, T> sweptRangeImpl() const;
410 MotionSpec<T> m_spec;
411 std::shared_ptr<const Curve> m_cachedCurve;
412 std::optional<std::chrono::nanoseconds> m_startTime;
413 std::optional<std::chrono::nanoseconds> m_lastTickTime;
414 bool m_isAnimating =
false;
415 bool m_isComplete =
false;
416 bool m_loggedStatelessDegrade =
false;
417 bool m_loggedNegativeDt =
false;
418 bool m_loggedTransformDegrade =
false;
419 bool m_loggedEpochMismatch =
false;
Unified motion primitive: one value of type T transitioning from start to target over time,...
Definition AnimatedValue.h:44
const CurveState & state() const
Definition AnimatedValue.h:326
AnimatedValue(const AnimatedValue &)=delete
bool retarget(T newTo, RetargetPolicy policy)
Redirect in-flight animation to a new target.
Definition AnimatedValue.h:103
T value() const
Definition AnimatedValue.h:318
T to() const
Definition AnimatedValue.h:346
void cancel()
Stop animating; leave value() at its current position.
Definition AnimatedValue.h:198
AnimatedValue & operator=(const AnimatedValue &)=delete
bool isAnimating() const
Definition AnimatedValue.h:330
bool isComplete() const
Definition AnimatedValue.h:334
T from() const
Definition AnimatedValue.h:342
qreal velocity() const
Definition AnimatedValue.h:322
void finish()
Snap to target immediately, fire onValueChanged + onComplete.
Definition AnimatedValue.h:213
bool retarget(T newTo)
Convenience overload using m_spec.retargetPolicy.
Definition AnimatedValue.h:187
AnimatedValue(AnimatedValue &&) noexcept=default
void advance()
Advance animation by one paint tick.
Definition AnimatedValue.h:234
const MotionSpec< T > & spec() const
Definition AnimatedValue.h:338
Polymorphic base for all animation curves.
Definition Curve.h:44
Abstract clock interface for the motion runtime.
Definition IMotionClock.h:18
QColor lerpColorOkLab(const QColor &from, const QColor &to, qreal t)
Definition Interpolate.h:191
bool isPureTranslate(const QTransform &t)
True if the 2x2 linear part is identity (only translation present).
Definition Interpolate.h:306
Definition AnimatedValue.h:31
constexpr qreal kRectSizeEpsilonPx
Sub-pixel epsilon for rect size-change detection.
Definition Interpolate.h:26
PHOSPHORANIMATION_EXPORT std::shared_ptr< const Curve > defaultFallbackCurve()
RetargetPolicy
How an in-flight AnimatedValue<T> reshapes on retarget().
Definition RetargetPolicy.h:9
@ ResetVelocity
Zero velocity on retarget; motion restarts from rest toward the new target.
@ PreserveVelocity
Carry velocity across the segment boundary, re-scaled to the new distance. Default.
@ PreservePosition
Position-continuous only; velocity treatment delegated to the curve's natural behaviour.
ColorSpace
Interpolation space selector for AnimatedValue<QColor, ...>.
Definition Interpolate.h:29
@ Linear
sRGB -> linear lerp -> sRGB (radiometrically correct)
@ OkLab
sRGB -> OkLab lerp -> sRGB (perceptually uniform)
Mutable state for stateful curve progression (springs carry position+velocity across frames; stateles...
Definition Curve.h:19
qreal startValue
Start of current segment — stateless step() lerps from here to target.
Definition Curve.h:26
Type-specific linear interpolation and path-distance for AnimatedValue<T>.
Definition Interpolate.h:36
Runtime call-site bundle for starting an AnimatedValue<T>.
Definition MotionSpec.h:24