// 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 void DrawAnimationRecordBorder(ClipDrawData drawData)
        {
            if (!drawData.clip.GetParentTrack().IsRecordingToClip(drawData.clip))
            {
                return;
            }

            var time  = new DiscreteTime(TimelineWindow.instance.state.editSequence.time);
            var start = new DiscreteTime(drawData.clip.start + drawData.clip.mixInDuration);
            var end   = new DiscreteTime(drawData.clip.end - drawData.clip.mixOutDuration);

            if (time < start || time >= end)
            {
                return;
            }

            DrawClipSelectionBorder(drawData.clipCenterSection, ClipBorder.Recording(), ClipBlends.kNone);
        }
        // Finds the clip at the given time that recording should use
        //    returns whether recording at this particular point is valid
        // The target clip will be returned, even if recording at that time is invalid
        // in case of recording in a blend OR recording to a non-timeline clip
        internal static bool FindRecordingClipAtTime(this TrackAsset track, double time, out TimelineClip target)
        {
            target = null;
            if (track == null)
            {
                return(false);
            }

            var discreteTime = new DiscreteTime(time);

            // only animation tracks require the recordable flag as they are recording
            //  to an animation clip
            bool requiresRecordable = (track as AnimationTrack) != null;

            if (requiresRecordable)
            {
                track.SortClips();
                var sortedByStartTime = track.clips;
                int i = 0;
                for (i = 0; i < sortedByStartTime.Length; i++)
                {
                    var clip = sortedByStartTime[i];
                    if (new DiscreteTime(clip.start) <= discreteTime && new DiscreteTime(clip.end) > discreteTime)
                    {
                        target = clip;
                        // not recordable
                        if (!clip.recordable)
                        {
                            return(false);
                        }

                        // in a blend
                        if (!Mathf.Approximately(1.0f, clip.EvaluateMixIn(time) * clip.EvaluateMixOut(time)))
                        {
                            return(false);
                        }

                        return(true);
                    }

                    if (new DiscreteTime(clip.start) > discreteTime)
                    {
                        break;
                    }
                }

                return(false);
            }


            // Recordable playable assets -- takes the last clip that matches
            track.SortClips();
            for (int i = 0; i < track.clips.Length; i++)
            {
                var clip = track.clips[i];
                if (clip.start <= time && clip.end >= time && clip.HasAnyAnimatableParameters())
                {
                    target = clip;
                }

                if (clip.start > time)
                {
                    break;
                }
            }

            return(target != null);
        }