#nullable enable
using T3.Core.Animation;
namespace T3.Editor.Gui.Windows.TimeLine;
///
/// Sorted list of TimeWarp handle U positions plus a per-frame visibility buffer.
/// Owns only the handle data — drag mechanics live in ,
/// and drawing / hit-testing live in .
///
internal sealed class TimeWarpHandleSet
{
public int Count => _handles.Count;
public double this[int index] => _handles[index];
public IReadOnlyList VisibleIndices => _visible;
public bool HasVisible => _visible.Count > 0;
/// Recompute the set of handle indices that fall inside the given range.
public void RebuildVisible(TimeRange range)
{
_visible.Clear();
for (var i = 0; i < _handles.Count; i++)
{
var u = _handles[i];
if (u >= range.Start && u <= range.End)
_visible.Add(i);
}
}
/// Insert a handle at ; no-op if outside or exactly on the range edges.
public void Insert(double u, TimeRange range)
{
if (u <= range.Start || u >= range.End)
return;
_handles.Add(u);
_handles.Sort();
}
public void RemoveAt(int index)
{
if (index < 0 || index >= _handles.Count)
return;
_handles.RemoveAt(index);
}
public void Clear() => _handles.Clear();
/// Write a new U value to the handle at without re-sorting.
public void SetValueUnsorted(int index, double u) => _handles[index] = u;
public void RestoreFromSnapshot(IReadOnlyList snapshot)
{
_handles.Clear();
for (var i = 0; i < snapshot.Count; i++)
_handles.Add(snapshot[i]);
}
public void CopyTo(List buffer)
{
buffer.Clear();
for (var i = 0; i < _handles.Count; i++)
buffer.Add(_handles[i]);
}
/// Previous and next neighbour U for a given handle, bounded by the selection range.
public (double prev, double next) GetNeighbors(int handleIndex, TimeRange range)
{
var u = _handles[handleIndex];
double prev = range.Start;
double next = range.End;
for (var i = 0; i < _handles.Count; i++)
{
if (i == handleIndex) continue;
var other = _handles[i];
if (other < u && other > prev) prev = other;
else if (other > u && other < next) next = other;
}
return (prev, next);
}
public double GetFirstVisibleU()
{
var best = double.PositiveInfinity;
for (var vi = 0; vi < _visible.Count; vi++)
{
var u = _handles[_visible[vi]];
if (u < best) best = u;
}
return best;
}
public double GetLastVisibleU()
{
var best = double.NegativeInfinity;
for (var vi = 0; vi < _visible.Count; vi++)
{
var u = _handles[_visible[vi]];
if (u > best) best = u;
}
return best;
}
private readonly List _handles = new(8);
private readonly List _visible = new(8);
}