summaryrefslogtreecommitdiff
path: root/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions
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/Editor/Extensions
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/Editor/Extensions')
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs150
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs134
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs495
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs.meta11
6 files changed, 812 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs
new file mode 100644
index 0000000..06f5e47
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs
@@ -0,0 +1,150 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using UnityEngine;
+using UnityEngine.Timeline;
+
+namespace UnityEditor.Timeline
+{
+ static class AnimatedParameterExtensions
+ {
+ public static bool HasAnyAnimatableParameters(this ICurvesOwner curvesOwner)
+ {
+ return AnimatedParameterUtility.HasAnyAnimatableParameters(curvesOwner.asset);
+ }
+
+ public static IEnumerable<SerializedProperty> GetAllAnimatableParameters(this ICurvesOwner curvesOwner)
+ {
+ return AnimatedParameterUtility.GetAllAnimatableParameters(curvesOwner.asset);
+ }
+
+ public static bool IsParameterAnimatable(this ICurvesOwner curvesOwner, string parameterName)
+ {
+ return AnimatedParameterUtility.IsParameterAnimatable(curvesOwner.asset, parameterName);
+ }
+
+ public static bool IsParameterAnimated(this ICurvesOwner curvesOwner, string parameterName)
+ {
+ return AnimatedParameterUtility.IsParameterAnimated(curvesOwner.asset, curvesOwner.curves, parameterName);
+ }
+
+ public static EditorCurveBinding GetCurveBinding(this ICurvesOwner curvesOwner, string parameterName)
+ {
+ return AnimatedParameterUtility.GetCurveBinding(curvesOwner.asset, parameterName);
+ }
+
+ public static string GetUniqueRecordedClipName(this ICurvesOwner curvesOwner)
+ {
+ return AnimationTrackRecorder.GetUniqueRecordedClipName(curvesOwner.assetOwner, curvesOwner.defaultCurvesName);
+ }
+
+ public static AnimationCurve GetAnimatedParameter(this ICurvesOwner curvesOwner, string bindingName)
+ {
+ return AnimatedParameterUtility.GetAnimatedParameter(curvesOwner.asset, curvesOwner.curves, bindingName);
+ }
+
+ public static bool AddAnimatedParameterValueAt(this ICurvesOwner curvesOwner, string parameterName, float value, float time)
+ {
+ if (!curvesOwner.IsParameterAnimatable(parameterName))
+ return false;
+
+ if (curvesOwner.curves == null)
+ curvesOwner.CreateCurves(curvesOwner.GetUniqueRecordedClipName());
+
+ var binding = curvesOwner.GetCurveBinding(parameterName);
+ var curve = AnimationUtility.GetEditorCurve(curvesOwner.curves, binding) ?? new AnimationCurve();
+
+ var serializedObject = AnimatedParameterUtility.GetSerializedPlayableAsset(curvesOwner.asset);
+ var property = serializedObject.FindProperty(parameterName);
+
+ bool isStepped = property.propertyType == SerializedPropertyType.Boolean ||
+ property.propertyType == SerializedPropertyType.Integer ||
+ property.propertyType == SerializedPropertyType.Enum;
+
+ CurveEditUtility.AddKeyFrameToCurve(curve, time, curvesOwner.curves.frameRate, value, isStepped);
+ AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, curve);
+
+ return true;
+ }
+
+ public static void SanitizeCurvesData(this ICurvesOwner curvesOwner)
+ {
+ var curves = curvesOwner.curves;
+ if (curves == null)
+ return;
+
+ // Remove any 0-length curves
+ foreach (var binding in AnimationUtility.GetCurveBindings(curves))
+ {
+ var curve = AnimationUtility.GetEditorCurve(curves, binding);
+ if (curve.length == 0)
+ AnimationUtility.SetEditorCurve(curves, binding, null);
+ }
+
+ // If no curves remain, delete the curves asset
+ if (curves.empty)
+ {
+ var track = curvesOwner.targetTrack;
+ var timeline = track != null ? track.timelineAsset : null;
+ TimelineUndo.PushDestroyUndo(timeline, track, curves, "Delete Parameter Curves");
+ }
+ }
+
+ public static bool AddAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName)
+ {
+ var newBinding = new EditorCurveBinding();
+
+ SerializedProperty property;
+ if (!InternalAddParameter(curvesOwner, parameterName, ref newBinding, out property))
+ return false;
+
+ var duration = (float)curvesOwner.duration;
+ CurveEditUtility.AddKey(curvesOwner.curves, newBinding, property, 0);
+ CurveEditUtility.AddKey(curvesOwner.curves, newBinding, property, duration);
+ return true;
+ }
+
+ public static bool RemoveAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName)
+ {
+ if (!curvesOwner.IsParameterAnimated(parameterName) || curvesOwner.curves == null)
+ return false;
+
+ var binding = curvesOwner.GetCurveBinding(parameterName);
+ AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, null);
+ return true;
+ }
+
+ // Set an animated parameter. Requires the field identifier 'position.x', but will add default curves to all fields
+ public static bool SetAnimatedParameter(this ICurvesOwner curvesOwner, string parameterName, AnimationCurve curve)
+ {
+ // this will add a basic curve for all the related parameters
+ if (!curvesOwner.IsParameterAnimated(parameterName) && !curvesOwner.AddAnimatedParameter(parameterName))
+ return false;
+
+ var binding = curvesOwner.GetCurveBinding(parameterName);
+ AnimationUtility.SetEditorCurve(curvesOwner.curves, binding, curve);
+ return true;
+ }
+
+ static bool InternalAddParameter([NotNull] ICurvesOwner curvesOwner, string parameterName, ref EditorCurveBinding binding, out SerializedProperty property)
+ {
+ property = null;
+
+ if (curvesOwner.IsParameterAnimated(parameterName))
+ return false;
+
+ var serializedObject = AnimatedParameterUtility.GetSerializedPlayableAsset(curvesOwner.asset);
+ if (serializedObject == null)
+ return false;
+
+ property = serializedObject.FindProperty(parameterName);
+ if (property == null || !AnimatedParameterUtility.IsTypeAnimatable(property.propertyType))
+ return false;
+
+ if (curvesOwner.curves == null)
+ curvesOwner.CreateCurves(curvesOwner.GetUniqueRecordedClipName());
+
+ binding = curvesOwner.GetCurveBinding(parameterName);
+ return true;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs.meta
new file mode 100644
index 0000000..21de3d3
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimatedParameterExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7d3aa106cfe752241997b3759bf80163
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs
new file mode 100644
index 0000000..41677ec
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs
@@ -0,0 +1,134 @@
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Timeline;
+
+namespace UnityEditor.Timeline
+{
+ static class AnimationTrackExtensions
+ {
+ public static void ConvertToClipMode(this AnimationTrack track)
+ {
+ if (!track.CanConvertToClipMode())
+ return;
+
+ TimelineUndo.PushUndo(track, "Convert To Clip");
+
+ if (!track.infiniteClip.empty)
+ {
+ var animClip = track.infiniteClip;
+ TimelineUndo.PushUndo(animClip, "Convert To Clip");
+ TimelineUndo.PushUndo(track, "Convert To Clip");
+ var start = AnimationClipCurveCache.Instance.GetCurveInfo(animClip).keyTimes.FirstOrDefault();
+ animClip.ShiftBySeconds(-start);
+
+ track.infiniteClip = null;
+ var clip = track.CreateClip(animClip);
+
+ clip.start = start;
+ clip.preExtrapolationMode = track.infiniteClipPreExtrapolation;
+ clip.postExtrapolationMode = track.infiniteClipPostExtrapolation;
+ clip.recordable = true;
+ if (Mathf.Abs(animClip.length) < TimelineClip.kMinDuration)
+ {
+ clip.duration = 1;
+ }
+
+ var animationAsset = clip.asset as AnimationPlayableAsset;
+ if (animationAsset)
+ {
+ animationAsset.position = track.infiniteClipOffsetPosition;
+ animationAsset.eulerAngles = track.infiniteClipOffsetEulerAngles;
+
+ // going to / from infinite mode should reset this. infinite mode
+ animationAsset.removeStartOffset = track.infiniteClipRemoveOffset;
+ animationAsset.applyFootIK = track.infiniteClipApplyFootIK;
+ animationAsset.loop = track.infiniteClipLoop;
+
+ track.infiniteClipOffsetPosition = Vector3.zero;
+ track.infiniteClipOffsetEulerAngles = Vector3.zero;
+ }
+
+ track.CalculateExtrapolationTimes();
+ }
+
+ track.infiniteClip = null;
+
+ EditorUtility.SetDirty(track);
+ }
+
+ public static void ConvertFromClipMode(this AnimationTrack track, TimelineAsset timeline)
+ {
+ if (!track.CanConvertFromClipMode())
+ return;
+
+ TimelineUndo.PushUndo(track, "Convert From Clip");
+
+ var clip = track.clips[0];
+ var delta = (float)clip.start;
+ track.infiniteClipTimeOffset = 0.0f;
+ track.infiniteClipPreExtrapolation = clip.preExtrapolationMode;
+ track.infiniteClipPostExtrapolation = clip.postExtrapolationMode;
+
+ var animAsset = clip.asset as AnimationPlayableAsset;
+ if (animAsset)
+ {
+ track.infiniteClipOffsetPosition = animAsset.position;
+ track.infiniteClipOffsetEulerAngles = animAsset.eulerAngles;
+ track.infiniteClipRemoveOffset = animAsset.removeStartOffset;
+ track.infiniteClipApplyFootIK = animAsset.applyFootIK;
+ track.infiniteClipLoop = animAsset.loop;
+ }
+
+ // clone it, it may not be in the same asset
+ var animClip = clip.animationClip;
+
+ float scale = (float)clip.timeScale;
+ if (!Mathf.Approximately(scale, 1.0f))
+ {
+ if (!Mathf.Approximately(scale, 0.0f))
+ scale = 1.0f / scale;
+ animClip.ScaleTime(scale);
+ }
+
+ TimelineUndo.PushUndo(animClip, "Convert From Clip");
+ animClip.ShiftBySeconds(delta);
+
+ // manually delete the clip
+ var asset = clip.asset;
+ clip.asset = null;
+
+ // Remove the clip, remove old assets
+ ClipModifier.Delete(timeline, clip);
+ TimelineUndo.PushDestroyUndo(null, track, asset, "Convert From Clip");
+
+ track.infiniteClip = animClip;
+
+ EditorUtility.SetDirty(track);
+ }
+
+ public static bool CanConvertToClipMode(this AnimationTrack track)
+ {
+ if (track == null || track.inClipMode)
+ return false;
+ return (track.infiniteClip != null && !track.infiniteClip.empty);
+ }
+
+ // Requirements to go from clip mode
+ // - one clip, recordable, and animation clip belongs to the same asset as the track
+ public static bool CanConvertFromClipMode(this AnimationTrack track)
+ {
+ if ((track == null) ||
+ (!track.inClipMode) ||
+ (track.clips.Length != 1) ||
+ (track.clips[0].start < 0) ||
+ (!track.clips[0].recordable))
+ return false;
+
+ var asset = track.clips[0].asset as AnimationPlayableAsset;
+ if (asset == null)
+ return false;
+
+ return TimelineHelpers.HaveSameContainerAsset(track, asset.clip);
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs.meta
new file mode 100644
index 0000000..df81151
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/AnimationTrackExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5a31542ccf4e8584ca4f60843e9d02d0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs
new file mode 100644
index 0000000..637e239
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs
@@ -0,0 +1,495 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Timeline;
+using UnityEngine.Playables;
+
+namespace UnityEditor.Timeline
+{
+ // Editor-only extension methods on track assets
+ static class TrackExtensions
+ {
+ public static readonly double kMinOverlapTime = TimeUtility.kTimeEpsilon * 1000;
+
+ public static AnimationClip GetOrCreateClip(this AnimationTrack track)
+ {
+ if (track.infiniteClip == null && !track.inClipMode)
+ track.CreateInfiniteClip(AnimationTrackRecorder.GetUniqueRecordedClipName(track, AnimationTrackRecorder.kRecordClipDefaultName));
+
+ return track.infiniteClip;
+ }
+
+ public static TimelineClip CreateClip(this TrackAsset track, double time)
+ {
+ var attr = track.GetType().GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
+
+ if (attr.Length == 0)
+ return null;
+
+ if (TimelineWindow.instance.state == null)
+ return null;
+
+ if (attr.Length == 1)
+ {
+ var clipClass = (TrackClipTypeAttribute)attr[0];
+
+ var clip = TimelineHelpers.CreateClipOnTrack(clipClass.inspectedType, track, TimelineWindow.instance.state);
+ clip.start = time;
+ return clip;
+ }
+
+ return null;
+ }
+
+ static bool Overlaps(TimelineClip blendOut, TimelineClip blendIn)
+ {
+ if (blendIn == blendOut)
+ return false;
+
+ if (Math.Abs(blendIn.start - blendOut.start) < TimeUtility.kTimeEpsilon)
+ {
+ return blendIn.duration >= blendOut.duration;
+ }
+
+ return blendIn.start >= blendOut.start && blendIn.start < blendOut.end;
+ }
+
+ public static void ComputeBlendsFromOverlaps(this TrackAsset asset)
+ {
+ ComputeBlendsFromOverlaps(asset.clips);
+ }
+
+ internal static void ComputeBlendsFromOverlaps(TimelineClip[] clips)
+ {
+ foreach (var clip in clips)
+ {
+ clip.blendInDuration = -1;
+ clip.blendOutDuration = -1;
+ }
+
+ foreach (var clip in clips)
+ {
+ var blendIn = clip;
+ var blendOut = clips
+ .Where(c => Overlaps(c, blendIn))
+ .OrderBy(c => c.start)
+ .FirstOrDefault();
+
+ if (blendOut != null)
+ {
+ UpdateClipIntersection(blendOut, blendIn);
+ }
+ }
+ }
+
+ internal static void UpdateClipIntersection(TimelineClip blendOutClip, TimelineClip blendInClip)
+ {
+ if (!blendOutClip.SupportsBlending() || !blendInClip.SupportsBlending())
+ return;
+
+ if (blendInClip.end < blendOutClip.end)
+ return;
+
+ double duration = Math.Max(0, blendOutClip.start + blendOutClip.duration - blendInClip.start);
+ duration = duration <= kMinOverlapTime ? 0 : duration;
+ blendOutClip.blendOutDuration = duration;
+ blendInClip.blendInDuration = duration;
+
+ var blendInMode = blendInClip.blendInCurveMode;
+ var blendOutMode = blendOutClip.blendOutCurveMode;
+
+ if (blendInMode == TimelineClip.BlendCurveMode.Manual && blendOutMode == TimelineClip.BlendCurveMode.Auto)
+ {
+ blendOutClip.mixOutCurve = CurveEditUtility.CreateMatchingCurve(blendInClip.mixInCurve);
+ }
+ else if (blendInMode == TimelineClip.BlendCurveMode.Auto && blendOutMode == TimelineClip.BlendCurveMode.Manual)
+ {
+ blendInClip.mixInCurve = CurveEditUtility.CreateMatchingCurve(blendOutClip.mixOutCurve);
+ }
+ else if (blendInMode == TimelineClip.BlendCurveMode.Auto && blendOutMode == TimelineClip.BlendCurveMode.Auto)
+ {
+ blendInClip.mixInCurve = null; // resets to default curves
+ blendOutClip.mixOutCurve = null;
+ }
+ }
+
+ internal static void RecursiveSubtrackClone(TrackAsset source, TrackAsset duplicate, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable, PlayableAsset assetOwner)
+ {
+ var subtracks = source.GetChildTracks();
+ foreach (var sub in subtracks)
+ {
+ var newSub = TimelineHelpers.Clone(duplicate, sub, sourceTable, destTable, assetOwner);
+ duplicate.AddChild(newSub);
+ RecursiveSubtrackClone(sub, newSub, sourceTable, destTable, assetOwner);
+
+ // Call the custom editor on Create
+ var customEditor = CustomTimelineEditorCache.GetTrackEditor(newSub);
+ try
+ {
+ customEditor.OnCreate(newSub, sub);
+ }
+ catch (Exception e)
+ {
+ Debug.LogException(e);
+ }
+
+ // registration has to happen AFTER recursion
+ TimelineCreateUtilities.SaveAssetIntoObject(newSub, assetOwner);
+ TimelineUndo.RegisterCreatedObjectUndo(newSub, "Duplicate");
+ }
+ }
+
+ internal static TrackAsset Duplicate(this TrackAsset track, IExposedPropertyTable sourceTable, IExposedPropertyTable destTable,
+ TimelineAsset destinationTimeline = null)
+ {
+ if (track == null)
+ return null;
+
+ // if the destination is us, clear to avoid bad parenting (case 919421)
+ if (destinationTimeline == track.timelineAsset)
+ destinationTimeline = null;
+
+ var timelineParent = track.parent as TimelineAsset;
+ var trackParent = track.parent as TrackAsset;
+ if (timelineParent == null && trackParent == null)
+ {
+ Debug.LogWarning("Cannot duplicate track because it is not parented to known type");
+ return null;
+ }
+
+ // Determine who the final parent is. If we are pasting into another track, it's always the timeline.
+ // Otherwise it's the original parent
+ PlayableAsset finalParent = destinationTimeline != null ? destinationTimeline : track.parent;
+
+ // grab the list of tracks to generate a name from (923360) to get the list of names
+ // no need to do this part recursively
+ var finalTrackParent = finalParent as TrackAsset;
+ var finalTimelineAsset = finalParent as TimelineAsset;
+ var otherTracks = (finalTimelineAsset != null) ? finalTimelineAsset.trackObjects : finalTrackParent.subTracksObjects;
+
+ // Important to create the new objects before pushing the original undo, or redo breaks the
+ // sequence
+ var newTrack = TimelineHelpers.Clone(finalParent, track, sourceTable, destTable, finalParent);
+ newTrack.name = TimelineCreateUtilities.GenerateUniqueActorName(otherTracks, newTrack.name);
+
+ RecursiveSubtrackClone(track, newTrack, sourceTable, destTable, finalParent);
+ TimelineCreateUtilities.SaveAssetIntoObject(newTrack, finalParent);
+ TimelineUndo.RegisterCreatedObjectUndo(newTrack, "Duplicate");
+ TimelineUndo.PushUndo(finalParent, "Duplicate");
+
+ if (destinationTimeline != null) // other timeline
+ destinationTimeline.AddTrackInternal(newTrack);
+ else if (timelineParent != null) // this timeline, no parent
+ ReparentTracks(new List<TrackAsset> { newTrack }, timelineParent, timelineParent.GetRootTracks().Last(), false);
+ else // this timeline, with parent
+ trackParent.AddChild(newTrack);
+
+ // Call the custom editor. this check prevents the call when copying to the clipboard
+ if (destinationTimeline == null || destinationTimeline == TimelineEditor.inspectedAsset)
+ {
+ var customEditor = CustomTimelineEditorCache.GetTrackEditor(newTrack);
+ try
+ {
+ customEditor.OnCreate(newTrack, track);
+ }
+ catch (Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+
+ return newTrack;
+ }
+
+ // Reparents a list of tracks to a new parent
+ // the new parent cannot be null (has to be track asset or sequence)
+ // the insertAfter can be null (will not reorder)
+ internal static bool ReparentTracks(List<TrackAsset> tracksToMove, PlayableAsset targetParent,
+ TrackAsset insertMarker = null, bool insertBefore = false)
+ {
+ var targetParentTrack = targetParent as TrackAsset;
+ var targetSequenceTrack = targetParent as TimelineAsset;
+
+ if (tracksToMove == null || tracksToMove.Count == 0 || (targetParentTrack == null && targetSequenceTrack == null))
+ return false;
+
+ // invalid parent type on a track
+ if (targetParentTrack != null && tracksToMove.Any(x => !TimelineCreateUtilities.ValidateParentTrack(targetParentTrack, x.GetType())))
+ return false;
+
+ // no valid tracks means this is simply a rearrangement
+ var validTracks = tracksToMove.Where(x => x.parent != targetParent).ToList();
+ if (insertMarker == null && !validTracks.Any())
+ return false;
+
+ var parents = validTracks.Select(x => x.parent).Where(x => x != null).Distinct().ToList();
+
+ // push the current state of the tracks that will change
+ foreach (var p in parents)
+ {
+ TimelineUndo.PushUndo(p, "Reparent");
+ }
+ foreach (var t in validTracks)
+ {
+ TimelineUndo.PushUndo(t, "Reparent");
+ }
+ TimelineUndo.PushUndo(targetParent, "Reparent");
+
+ // need to reparent tracks first, before moving them.
+ foreach (var t in validTracks)
+ {
+ if (t.parent != targetParent)
+ {
+ TrackAsset toMoveParent = t.parent as TrackAsset;
+ TimelineAsset toMoveTimeline = t.parent as TimelineAsset;
+ if (toMoveTimeline != null)
+ {
+ toMoveTimeline.RemoveTrack(t);
+ }
+ else if (toMoveParent != null)
+ {
+ toMoveParent.RemoveSubTrack(t);
+ }
+
+ if (targetParentTrack != null)
+ {
+ targetParentTrack.AddChild(t);
+ targetParentTrack.SetCollapsed(false);
+ }
+ else
+ {
+ targetSequenceTrack.AddTrackInternal(t);
+ }
+ }
+ }
+
+
+ if (insertMarker != null)
+ {
+ // re-ordering track. This is using internal APIs, so invalidation of the tracks must be done manually to avoid
+ // cache mismatches
+ var children = targetParentTrack != null ? targetParentTrack.subTracksObjects : targetSequenceTrack.trackObjects;
+ TimelineUtility.ReorderTracks(children, tracksToMove, insertMarker, insertBefore);
+ if (targetParentTrack != null)
+ targetParentTrack.Invalidate();
+ if (insertMarker.timelineAsset != null)
+ insertMarker.timelineAsset.Invalidate();
+ }
+
+ return true;
+ }
+
+ internal static IEnumerable<TrackAsset> FilterTracks(IEnumerable<TrackAsset> tracks)
+ {
+ var nTracks = tracks.Count();
+ // Duplicate is recursive. If should not have parent and child in the list
+ var hash = new HashSet<TrackAsset>(tracks);
+ var take = new Dictionary<TrackAsset, bool>(nTracks);
+
+ foreach (var track in tracks)
+ {
+ var parent = track.parent as TrackAsset;
+ var foundParent = false;
+ // go up the hierarchy
+ while (parent != null && !foundParent)
+ {
+ if (hash.Contains(parent))
+ {
+ foundParent = true;
+ }
+
+ parent = parent.parent as TrackAsset;
+ }
+
+ take[track] = !foundParent;
+ }
+
+ foreach (var track in tracks)
+ {
+ if (take[track])
+ yield return track;
+ }
+ }
+
+ internal static bool IsVisibleRecursive(this TrackAsset track)
+ {
+ var t = track;
+ while ((t = t.parent as TrackAsset) != null)
+ {
+ if (t.GetCollapsed())
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static bool GetCollapsed(this TrackAsset track)
+ {
+ return TimelineWindowViewPrefs.IsTrackCollapsed(track);
+ }
+
+ internal static void SetCollapsed(this TrackAsset track, bool collapsed)
+ {
+ TimelineWindowViewPrefs.SetTrackCollapsed(track, collapsed);
+ }
+
+ internal static bool GetShowMarkers(this TrackAsset track)
+ {
+ return TimelineWindowViewPrefs.IsShowMarkers(track);
+ }
+
+ internal static void SetShowMarkers(this TrackAsset track, bool collapsed)
+ {
+ TimelineWindowViewPrefs.SetTrackShowMarkers(track, collapsed);
+ }
+
+ internal static bool GetShowInlineCurves(this TrackAsset track)
+ {
+ return TimelineWindowViewPrefs.GetShowInlineCurves(track);
+ }
+
+ internal static void SetShowInlineCurves(this TrackAsset track, bool inlineOn)
+ {
+ TimelineWindowViewPrefs.SetShowInlineCurves(track, inlineOn);
+ }
+
+ internal static bool ShouldShowInfiniteClipEditor(this TrackAsset track)
+ {
+ var animationTrack = track as AnimationTrack;
+ if (animationTrack != null)
+ return animationTrack.ShouldShowInfiniteClipEditor();
+
+ return track.HasAnyAnimatableParameters();
+ }
+
+ internal static bool ShouldShowInfiniteClipEditor(this AnimationTrack track)
+ {
+ return track != null && !track.inClipMode && track.infiniteClip != null;
+ }
+
+ // Special method to remove a track that is in a broken state. i.e. the script won't load
+ internal static bool RemoveBrokenTrack(PlayableAsset parent, ScriptableObject track)
+ {
+ var parentTrack = parent as TrackAsset;
+ var parentTimeline = parent as TimelineAsset;
+
+ if (parentTrack == null && parentTimeline == null)
+ throw new ArgumentException("parent is not a valid parent type", "parent");
+
+ // this object must be a Unity null, but not actually null;
+ object trackAsObject = track;
+ if (trackAsObject == null || track != null) // yes, this is correct
+ throw new ArgumentException("track is not in a broken state");
+
+ // this belongs to a parent track
+ if (parentTrack != null)
+ {
+ int index = parentTrack.subTracksObjects.FindIndex(t => t.GetInstanceID() == track.GetInstanceID());
+ if (index >= 0)
+ {
+ TimelineUndo.PushUndo(parentTrack, "Remove Track");
+ parentTrack.subTracksObjects.RemoveAt(index);
+ parentTrack.Invalidate();
+ Undo.DestroyObjectImmediate(track);
+ return true;
+ }
+ }
+ else if (parentTimeline != null)
+ {
+ int index = parentTimeline.trackObjects.FindIndex(t => t.GetInstanceID() == track.GetInstanceID());
+ if (index >= 0)
+ {
+ TimelineUndo.PushUndo(parentTimeline, "Remove Track");
+ parentTimeline.trackObjects.RemoveAt(index);
+ parentTimeline.Invalidate();
+ Undo.DestroyObjectImmediate(track);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Find the gap at the given time
+ // return true if there is a gap, false if there is an intersection
+ // endGap will be Infinity if the gap has no end
+ internal static bool GetGapAtTime(this TrackAsset track, double time, out double startGap, out double endGap)
+ {
+ startGap = 0;
+ endGap = Double.PositiveInfinity;
+
+ if (track == null || !track.GetClips().Any())
+ {
+ return false;
+ }
+
+ var discreteTime = new DiscreteTime(time);
+
+ track.SortClips();
+ var sortedByStartTime = track.clips;
+ for (int i = 0; i < sortedByStartTime.Length; i++)
+ {
+ var clip = sortedByStartTime[i];
+
+ // intersection
+ if (discreteTime >= new DiscreteTime(clip.start) && discreteTime < new DiscreteTime(clip.end))
+ {
+ endGap = time;
+ startGap = time;
+ return false;
+ }
+
+ if (clip.end < time)
+ {
+ startGap = clip.end;
+ }
+ if (clip.start > time)
+ {
+ endGap = clip.start;
+ break;
+ }
+ }
+
+ if (endGap - startGap < TimelineClip.kMinDuration)
+ {
+ startGap = time;
+ endGap = time;
+ return false;
+ }
+
+ return true;
+ }
+
+ public static bool IsCompatibleWithClip(this TrackAsset track, TimelineClip clip)
+ {
+ if (track == null || clip == null || clip.asset == null)
+ return false;
+
+ return TypeUtility.GetPlayableAssetsHandledByTrack(track.GetType()).Contains(clip.asset.GetType());
+ }
+
+ // Get a flattened list of all child tracks
+ public static void GetFlattenedChildTracks(this TrackAsset asset, List<TrackAsset> list)
+ {
+ if (asset == null || list == null)
+ return;
+
+ foreach (var track in asset.GetChildTracks())
+ {
+ list.Add(track);
+ GetFlattenedChildTracks(track, list);
+ }
+ }
+
+ public static IEnumerable<TrackAsset> GetFlattenedChildTracks(this TrackAsset asset)
+ {
+ if (asset == null || !asset.GetChildTracks().Any())
+ return Enumerable.Empty<TrackAsset>();
+
+ var flattenedChildTracks = new List<TrackAsset>();
+ GetFlattenedChildTracks(asset, flattenedChildTracks);
+ return flattenedChildTracks;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs.meta
new file mode 100644
index 0000000..1e9f0c2
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Extensions/TrackExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5b24618beecc3bf41acadfcf2246d772
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: