using System;
namespace T3.Core.Animation;
///
/// Snapshot of how a timeline maps to a source-content
/// , plus the BPM needed to convert bars to seconds. Captures
/// the canonical playhead → source-time math every TimeClip-based op performs.
///
///
///
/// Lives next to and because it's
/// genuinely a property of timeline clips: audio, video, image-sequence, and the new
/// DataClip all do exactly this math inline today. New code should publish a
/// instead so consumers can call the conversion at any
/// timeline point — not just "now".
///
///
/// Designed as a readonly struct with value-typed fields so passing it on a
/// graph slot or capturing it for one-frame use is allocation-free and gives a stable
/// snapshot — the consumer can't accidentally read a mutated TimeClip on a later frame.
///
///
public readonly struct TimeRangeMapping
{
/// The clip's placement on the timeline (bars).
public readonly TimeRange TimeRange;
/// The slice of the source content that the clip exposes (bars).
public readonly TimeRange SourceRange;
/// BPM at the moment the mapping was built — baked in so consumers don't need .
public readonly double Bpm;
public TimeRangeMapping(TimeRange timeRange, TimeRange sourceRange, double bpm)
{
TimeRange = timeRange;
SourceRange = sourceRange;
Bpm = bpm;
}
///
/// Maps a timeline-local time (bars) to a source-relative time (bars). The result
/// is unbounded — caller decides what to do when the input falls outside
/// (typically: ignore, or clamp).
///
public double LocalBarsToSourceBars(double localBars)
{
var pos = localBars - TimeRange.Start;
if (Math.Abs(TimeRange.End - TimeRange.Start) > 0.0001f)
{
var rate = (SourceRange.End - SourceRange.Start) / (TimeRange.End - TimeRange.Start);
pos *= rate;
}
return pos + SourceRange.Start;
}
///
/// Maps a timeline-local time (bars) to a source-relative time (seconds), using the
/// captured at mapping construction.
///
public double LocalBarsToSourceSecs(double localBars)
=> LocalBarsToSourceBars(localBars) * 240.0 / Bpm;
///
/// Inverse of : maps a source-relative time (bars)
/// to its timeline-local position (bars). Useful for rendering source-time events
/// (e.g. recorded MIDI / data points) at the right X on the timeline.
///
public double SourceBarsToLocalBars(double sourceBars)
{
var sourceDur = SourceRange.End - SourceRange.Start;
var timeDur = TimeRange.End - TimeRange.Start;
if (Math.Abs(sourceDur) < 0.0001f)
return TimeRange.Start;
var rate = timeDur / sourceDur;
return TimeRange.Start + (sourceBars - SourceRange.Start) * rate;
}
///
/// Convenience: with the input in seconds.
///
public double SourceSecsToLocalBars(double sourceSecs)
=> SourceBarsToLocalBars(sourceSecs * Bpm / 240.0);
/// True if falls inside .
public bool IsActive(double localBars)
=> localBars >= TimeRange.Start && localBars <= TimeRange.End;
}
///
/// Convenience constructors for building a from a
/// and the current . Lives outside
/// itself to avoid pulling /
/// into the struct's already-tight API.
///
public static class TimeRangeMappingExtensions
{
public static TimeRangeMapping ToMapping(this TimeClip clip, Playback playback)
=> new(clip.TimeRange, clip.SourceRange, playback.Bpm);
}