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); }