summaryrefslogtreecommitdiff
path: root/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
committerAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
commitc55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 (patch)
treeee4d51c7c1d633e11f46453ef1edd3c77c4ef9f7 /Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs
downloadProject-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.gz
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.bz2
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.zip
Inital commit
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs')
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs212
1 files changed, 212 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs
new file mode 100644
index 0000000..549de4c
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Runtime/Utilities/TimeUtility.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace UnityEngine.Timeline
+{
+ // Sequence specific utilities for time manipulation
+ static class TimeUtility
+ {
+ // chosen because it will cause no rounding errors between time/frames for frames values up to at least 10 million
+ public static readonly double kTimeEpsilon = 1e-14;
+ public static readonly double kFrameRateEpsilon = 1e-6;
+ public static readonly double k_MaxTimelineDurationInSeconds = 9e6; //104 days of running time
+
+ static void ValidateFrameRate(double frameRate)
+ {
+ if (frameRate <= kTimeEpsilon)
+ throw new ArgumentException("frame rate cannot be 0 or negative");
+ }
+
+ public static int ToFrames(double time, double frameRate)
+ {
+ ValidateFrameRate(frameRate);
+ time = Math.Min(Math.Max(time, -k_MaxTimelineDurationInSeconds), k_MaxTimelineDurationInSeconds);
+ // this matches OnFrameBoundary
+ double tolerance = GetEpsilon(time, frameRate) / 2.0;
+ if (time < 0)
+ {
+ return (int)Math.Ceiling(time * frameRate - tolerance);
+ }
+ return (int)Math.Floor(time * frameRate + tolerance);
+ }
+
+ public static double ToExactFrames(double time, double frameRate)
+ {
+ ValidateFrameRate(frameRate);
+ return time * frameRate;
+ }
+
+ public static double FromFrames(int frames, double frameRate)
+ {
+ ValidateFrameRate(frameRate);
+ return (frames / frameRate);
+ }
+
+ public static double FromFrames(double frames, double frameRate)
+ {
+ ValidateFrameRate(frameRate);
+ return frames / frameRate;
+ }
+
+ public static bool OnFrameBoundary(double time, double frameRate)
+ {
+ return OnFrameBoundary(time, frameRate, GetEpsilon(time, frameRate));
+ }
+
+ public static double GetEpsilon(double time, double frameRate)
+ {
+ return Math.Max(Math.Abs(time), 1) * frameRate * kTimeEpsilon;
+ }
+
+ public static bool OnFrameBoundary(double time, double frameRate, double epsilon)
+ {
+ ValidateFrameRate(frameRate);
+
+ double exact = ToExactFrames(time, frameRate);
+ double rounded = Math.Round(exact);
+
+ return Math.Abs(exact - rounded) < epsilon;
+ }
+
+ public static double RoundToFrame(double time, double frameRate)
+ {
+ ValidateFrameRate(frameRate);
+
+ var frameBefore = (int)Math.Floor(time * frameRate) / frameRate;
+ var frameAfter = (int)Math.Ceiling(time * frameRate) / frameRate;
+
+ return Math.Abs(time - frameBefore) < Math.Abs(time - frameAfter) ? frameBefore : frameAfter;
+ }
+
+ public static string TimeAsFrames(double timeValue, double frameRate, string format = "F2")
+ {
+ if (OnFrameBoundary(timeValue, frameRate)) // make integral values when on time borders
+ return ToFrames(timeValue, frameRate).ToString();
+ return ToExactFrames(timeValue, frameRate).ToString(format);
+ }
+
+ public static string TimeAsTimeCode(double timeValue, double frameRate, string format = "F2")
+ {
+ ValidateFrameRate(frameRate);
+
+ int intTime = (int)Math.Abs(timeValue);
+
+ int hours = intTime / 3600;
+ int minutes = (intTime % 3600) / 60;
+ int seconds = intTime % 60;
+
+ string result;
+ string sign = timeValue < 0 ? "-" : string.Empty;
+ if (hours > 0)
+ result = hours + ":" + minutes.ToString("D2") + ":" + seconds.ToString("D2");
+ else if (minutes > 0)
+ result = minutes + ":" + seconds.ToString("D2");
+ else
+ result = seconds.ToString();
+
+ int frameDigits = (int)Math.Floor(Math.Log10(frameRate) + 1);
+
+ // Add partial digits on the frame if needed.
+ // we are testing the original value (not the truncated), because the truncation can cause rounding errors leading
+ // to invalid strings for items on frame boundaries
+ string frames = (ToFrames(timeValue, frameRate) - ToFrames(intTime, frameRate)).ToString().PadLeft(frameDigits, '0');
+ if (!OnFrameBoundary(timeValue, frameRate))
+ {
+ string decimals = ToExactFrames(timeValue, frameRate).ToString(format);
+ int decPlace = decimals.IndexOf('.');
+ if (decPlace >= 0)
+ frames += " [" + decimals.Substring(decPlace) + "]";
+ }
+
+ return sign + result + ":" + frames;
+ }
+
+ // Given a time code string, return the time in seconds
+ // 1.5 -> 1.5 seconds
+ // 1:1.5 -> 1 minute, 1.5 seconds
+ // 1:1[.5] -> 1 second, 1.5 frames
+ // 2:3:4 -> 2 minutes, 3 seconds, 4 frames
+ // 1[.6] -> 1.6 frames
+ public static double ParseTimeCode(string timeCode, double frameRate, double defaultValue)
+ {
+ timeCode = RemoveChar(timeCode, c => char.IsWhiteSpace(c));
+ string[] sections = timeCode.Split(':');
+ if (sections.Length == 0 || sections.Length > 4)
+ return defaultValue;
+
+ int hours = 0;
+ int minutes = 0;
+ double seconds = 0;
+ double frames = 0;
+
+ try
+ {
+ // depending on the format of the last numbers
+ // seconds format
+ string lastSection = sections[sections.Length - 1];
+ if (Regex.Match(lastSection, @"^\d+\.\d+$").Success)
+ {
+ seconds = double.Parse(lastSection);
+ if (sections.Length > 3) return defaultValue;
+ if (sections.Length > 1) minutes = int.Parse(sections[sections.Length - 2]);
+ if (sections.Length > 2) hours = int.Parse(sections[sections.Length - 3]);
+ }
+ // frame formats
+ else
+ {
+ if (Regex.Match(lastSection, @"^\d+\[\.\d+\]$").Success)
+ {
+ string stripped = RemoveChar(lastSection, c => c == '[' || c == ']');
+ frames = double.Parse(stripped);
+ }
+ else if (Regex.Match(lastSection, @"^\d*$").Success)
+ {
+ frames = int.Parse(lastSection);
+ }
+ else
+ {
+ return defaultValue;
+ }
+
+ if (sections.Length > 1) seconds = int.Parse(sections[sections.Length - 2]);
+ if (sections.Length > 2) minutes = int.Parse(sections[sections.Length - 3]);
+ if (sections.Length > 3) hours = int.Parse(sections[sections.Length - 4]);
+ }
+ }
+ catch (FormatException)
+ {
+ return defaultValue;
+ }
+
+ return frames / frameRate + seconds + minutes * 60 + hours * 3600;
+ }
+
+ // fixes rounding errors from using single precision for length
+ public static double GetAnimationClipLength(AnimationClip clip)
+ {
+ if (clip == null || clip.empty)
+ return 0;
+
+ double length = clip.length;
+ if (clip.frameRate > 0)
+ {
+ double frames = Mathf.Round(clip.length * clip.frameRate);
+ length = frames / clip.frameRate;
+ }
+ return length;
+ }
+
+ static string RemoveChar(string str, Func<char, bool> charToRemoveFunc)
+ {
+ var len = str.Length;
+ var src = str.ToCharArray();
+ var dstIdx = 0;
+ for (var i = 0; i < len; i++)
+ {
+ if (!charToRemoveFunc(src[i]))
+ src[dstIdx++] = src[i];
+ }
+ return new string(src, 0, dstIdx);
+ }
+ }
+}