Animation/AnimationGate.cs
namespace Goo.Animation;
/// <summary>Fuses the two rebuild triggers (state <see cref="Invalidate"/> and in-motion animations) into one per-frame <see cref="Tick"/>; one extra rebuild fires on the motion-to-settled edge.</summary>
public sealed class AnimationGate
{
readonly AnimationSet _anims = new();
bool _dirty = true; // mount dirty: the first Tick rebuilds
bool _wasMoving;
/// <summary>Register an animator tick (returns true while still in motion). Register once, where the animator is created.</summary>
public void Add(TickFn tick) => _anims.Add(tick);
/// <summary>Mark dirty for exactly one upcoming Tick. Call from event handlers that mutate state.</summary>
public void Invalidate() => _dirty = true;
/// <summary>Drop all registered animator ticks.</summary>
public void Clear() => _anims.Clear();
/// <summary>Ticks every registered animator; returns true if a rebuild is needed this frame.</summary>
public bool Tick(float dt)
{
bool moving = _anims.UpdateAll(dt);
bool settledEdge = _wasMoving && !moving; // render the final settled value once
_wasMoving = moving;
bool needsRebuild = _dirty || moving || settledEdge;
_dirty = false;
return needsRebuild;
}
}