Example #1
0
        public override RibbonParameters GetSlideRibbonParameters(RuntimeNote startNote, RuntimeNote endNote, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var startStatus = NoteAnimationHelper.GetOnStageStatusOf(startNote, now, animationMetrics);

            if (startNote.IsSlideEnd() || startStatus < OnStageStatus.Passed)
            {
                return(GetHoldRibbonParameters(startNote, endNote, now, noteMetrics, animationMetrics));
            }

            var startX1 = GetEndXByNotePosition(startNote.EndX, animationMetrics);
            var startX2 = GetEndXByNotePosition(endNote.EndX, animationMetrics);

            var y1 = animationMetrics.Bottom;
            var x1 = (float)((now - startNote.HitTime) / (endNote.HitTime - startNote.HitTime)) * (startX2 - startX1) + startX1;

            var t1   = GetTransformedTime(startNote, now, animationMetrics);
            var t2   = GetTransformedTime(endNote, now, animationMetrics);
            var tmid = (t1 + t2) * 0.5f;

            var x2   = GetNoteXByTransformedTime(endNote, t2, animationMetrics);
            var xmid = GetNoteXByTransformedTime(endNote, tmid, animationMetrics);

            var y2   = GetNoteYByTransformedTime(t2, animationMetrics);
            var ymid = GetNoteYByTransformedTime(tmid, animationMetrics);

            var(controlX1, controlX2) = GetBezierFromQuadratic(x1, xmid, x2);
            var(controlY1, controlY2) = GetBezierFromQuadratic(y1, ymid, y2);

            return(new RibbonParameters(x1, y1, controlX1, controlY1, controlX2, controlY2, x2, y2));
        }
        public override float GetNoteY(RuntimeNote note, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var timePoints    = NoteAnimationHelper.CalculateNoteTimePoints(note, animationMetrics);
            var onStageStatus = NoteAnimationHelper.GetOnStageStatusOf(note, now, timePoints);

            float y;

            switch (onStageStatus)
            {
            case OnStageStatus.Incoming:
                y = animationMetrics.Top;
                break;

            case OnStageStatus.Visible:
                y = GetNoteOnStageY(note, now, timePoints, animationMetrics);
                break;

            case OnStageStatus.Passed:
                y = animationMetrics.Bottom;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(y);
        }
Example #3
0
        public override SizeF GetNoteRadius(RuntimeNote note, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var timeRemaining   = note.HitTime - now;
            var timePoints      = NoteAnimationHelper.CalculateNoteTimePoints(note, animationMetrics);
            var timeTransformed = NoteTimeTransform((float)timeRemaining / timePoints.Duration);
            var endRadius       = noteMetrics.EndRadius;

            if (timeTransformed < 0.75f)
            {
                if (timeTransformed < 0f)
                {
                    return(endRadius);
                }
                else
                {
                    var r = 1 - (float)timeTransformed * 0.933333333f;
                    return(new SizeF(endRadius.Width * r, endRadius.Height * r));
                }
            }
            else
            {
                if (timeTransformed < 1f)
                {
                    var r = (1 - (float)timeTransformed) * 1.2f;
                    return(new SizeF(endRadius.Width * r, endRadius.Height * r));
                }
                else
                {
                    return(SizeF.Empty);
                }
            }
        }
        public override RibbonParameters GetHoldRibbonParameters(RuntimeNote startNote, RuntimeNote endNote, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var tp1 = NoteAnimationHelper.CalculateNoteTimePoints(startNote, animationMetrics);
            var tp2 = NoteAnimationHelper.CalculateNoteTimePoints(endNote, animationMetrics);

            var t1    = GetTransformedTime(startNote, now, tp1);
            var t2    = GetTransformedTime(endNote, now, tp2);
            var tperc = startNote.IsHold() ? 0.5 : 0.4;
            var tm    = MathHelperEx.Lerp(t1, t2, tperc);

            var x1 = GetNoteX(startNote, now, noteMetrics, animationMetrics);
            var x2 = GetNoteX(endNote, now, noteMetrics, animationMetrics);

            var   startStatus = NoteAnimationHelper.GetOnStageStatusOf(startNote, now, tp1);
            float y1;

            if (startStatus == OnStageStatus.Passed)
            {
                y1 = animationMetrics.Bottom;
            }
            else
            {
                y1 = GetNoteOnStageY(t1, animationMetrics);
            }

            var   endStatus = NoteAnimationHelper.GetOnStageStatusOf(endNote, now, tp2);
            float y2;

            if (endStatus == OnStageStatus.Incoming)
            {
                y2 = animationMetrics.Top;
            }
            else
            {
                y2 = GetNoteOnStageY(t2, animationMetrics);
            }

            // CGSS-like
            //var xm = GetNoteOnStageX(endNote.StartX, endNote.EndX, tm, animationMetrics);
            // Naive guess
            //var xm = (x1 + x2) / 2;
            var xm = MathHelperEx.Lerp(x1, x2, 0.5f);

            if (startNote.IsSlide())
            {
                if (endNote.EndX < startNote.EndX)
                {
                    xm -= animationMetrics.Width * 0.02f * ((tp2.Leave - now) / (tp2.Leave - tp1.Enter));
                }
                else if (endNote.EndX > startNote.EndX)
                {
                    xm += animationMetrics.Width * 0.02f * ((tp2.Leave - now) / (tp2.Leave - tp1.Enter));
                }
            }
            var ym = GetNoteOnStageY(tm, animationMetrics);

            var(cx1, cx2) = GetBezierFromQuadratic(x1, xm, x2);
            var(cy1, cy2) = GetBezierFromQuadratic(y1, ym, y2);
            return(new RibbonParameters(x1, y1, cx1, cy1, cx2, cy2, x2, y2));
        }
        public override float GetSpecialNoteX(RuntimeNote note, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var leftRatio  = animationMetrics.NoteEndXRatios[0];
            var rightRatio = animationMetrics.NoteEndXRatios[animationMetrics.TrackCount - 1];
            var xRatio     = (leftRatio + rightRatio) / 2;

            return(animationMetrics.Width * xRatio);
        }
Example #6
0
        public override RibbonParameters GetSlideRibbonParameters(RuntimeNote startNote, RuntimeNote endNote, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var x1 = GetNoteX(startNote, now, noteMetrics, animationMetrics);
            var y1 = GetNoteY(startNote, now, noteMetrics, animationMetrics);
            var x2 = GetNoteX(endNote, now, noteMetrics, animationMetrics);
            var y2 = GetNoteY(endNote, now, noteMetrics, animationMetrics);

            return(new RibbonParameters(x1, y1, x2, y2));
        }
Example #7
0
        public static NoteTimePoints CalculateNoteTimePoints(RuntimeNote note, float globalSpeedScale)
        {
            // Empirical formula: s = pow(game_setting, 3) * pow(note_speed, 2)
            var relativeSpeed  = note.RelativeSpeed;
            var absoluteSpeed  = globalSpeedScale * globalSpeedScale * globalSpeedScale * relativeSpeed * relativeSpeed;
            var leadTimeScaled = (float)note.LeadTime / absoluteSpeed;

            return(new NoteTimePoints(note.HitTime - leadTimeScaled, note.HitTime));
        }
Example #8
0
        private static RuntimeNote FindFirstHold(RuntimeNote note)
        {
            var firstHold = note;

            do
            {
                firstHold = firstHold.PrevHold;
            } while (firstHold.PrevHold != null);
            return(firstHold);
        }
Example #9
0
        private static RuntimeNote FindFirstSlide(RuntimeNote note)
        {
            var firstSlide = note;

            do
            {
                firstSlide = firstSlide.PrevSlide;
            } while (firstSlide.PrevSlide != null);
            return(firstSlide);
        }
Example #10
0
        private static RuntimeNote FindFirstSlide(RuntimeNote note)
        {
            var firstSlide = note;

            do
            {
                firstSlide = firstSlide.PrevSlide;
                Debug.Assert(firstSlide != null, nameof(firstSlide) + " != null");
            } while (firstSlide.PrevSlide != null);
            return(firstSlide);
        }
Example #11
0
        private static RuntimeNote FindFirstHold(RuntimeNote note)
        {
            var firstHold = note;

            do
            {
                firstHold = firstHold.PrevHold;
                Debug.Assert(firstHold != null, nameof(firstHold) + " != null");
            } while (firstHold.PrevHold != null);
            return(firstHold);
        }
        public override RibbonParameters GetSlideRibbonParameters(RuntimeNote startNote, RuntimeNote endNote, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var tp1        = NoteAnimationHelper.CalculateNoteTimePoints(startNote, animationMetrics);
            var thisStatus = NoteAnimationHelper.GetOnStageStatusOf(startNote, now, tp1);

            if (thisStatus < OnStageStatus.Passed)
            {
                return(GetHoldRibbonParameters(startNote, endNote, now, noteMetrics, animationMetrics));
            }

            var tp2 = NoteAnimationHelper.CalculateNoteTimePoints(endNote, animationMetrics);

            var t1    = GetTransformedTime(startNote, now, tp1);
            var t2    = GetTransformedTime(endNote, now, tp2);
            var tperc = startNote.IsHold() ? 0.5 : 0.4;
            var tm    = MathHelperEx.Lerp(t1, t2, tperc);

            var trackCount       = animationMetrics.TrackCount;
            var leftMarginRatio  = animationMetrics.NoteEndXRatios[0];
            var rightMarginRatio = animationMetrics.NoteEndXRatios[trackCount - 1];
            var startXRatio      = leftMarginRatio + (rightMarginRatio - leftMarginRatio) * (startNote.EndX / (trackCount - 1));
            var endXRatio        = leftMarginRatio + (rightMarginRatio - leftMarginRatio) * (endNote.EndX / (trackCount - 1));

            var perc    = (now - startNote.HitTime) / (endNote.HitTime - startNote.HitTime);
            var x1Ratio = MathHelperEx.Lerp(startXRatio, endXRatio, perc);

            var x1 = animationMetrics.Width * x1Ratio;
            var y1 = animationMetrics.Bottom;
            var x2 = GetNoteX(endNote, now, noteMetrics, animationMetrics);
            var y2 = GetNoteOnStageY(t2, animationMetrics);

            // CGSS-like
            //var xm = GetNoteOnStageX(endNote.StartX, endNote.EndX, tm, animationMetrics);
            // Naive guess
            //var xm = (x1 + x2) / 2;
            var xm = MathHelperEx.Lerp(x1, x2, 0.5f);

            if (startNote.IsSlide())
            {
                if (endNote.EndX < startNote.EndX)
                {
                    xm -= animationMetrics.Width * 0.02f * ((tp2.Leave - now) / (tp2.Leave - tp1.Enter));
                }
                else if (endNote.EndX > startNote.EndX)
                {
                    xm += animationMetrics.Width * 0.02f * ((tp2.Leave - now) / (tp2.Leave - tp1.Enter));
                }
            }
            var ym = GetNoteOnStageY(tm, animationMetrics);

            var(cx1, cx2) = GetBezierFromQuadratic(x1, xm, x2);
            var(cy1, cy2) = GetBezierFromQuadratic(y1, ym, y2);
            return(new RibbonParameters(x1, y1, cx1, cy1, cx2, cy2, x2, y2));
        }
Example #13
0
        public override float GetNoteY(RuntimeNote note, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            if (now >= note.HitTime)
            {
                return(animationMetrics.Bottom);
            }

            var transformedTime = GetTransformedTime(note, now, animationMetrics);

            return(GetNoteYByTransformedTime(transformedTime, animationMetrics));
        }
Example #14
0
        public static RuntimeNote[] CreateSpecialNotes(SourceNote note, Conductor[] conductors, SourceNote[] gamingNotes, ref int currentID)
        {
            var rn = new RuntimeNote();

            rn.ID            = ++currentID;
            rn.HitTime       = TicksToSeconds(note.Ticks, conductors);
            rn.LeadTime      = note.LeadTime;
            rn.RelativeSpeed = note.Speed;
            rn.Type          = NoteType.Special;
            rn.StartX        = note.StartX;
            rn.EndX          = note.EndX;
            rn.ExtraInfo     = null;

            var prepare = new RuntimeNote();

            prepare.ID = ++currentID;
            // 0.8 seconds before the Special note.
            // Just a guess.
            // This value must keep the same as tap points' "transform" animation length.
            // See NoteReactor.Update() for more information.
            prepare.HitTime       = rn.HitTime - 0.8f;
            prepare.LeadTime      = rn.LeadTime;
            prepare.RelativeSpeed = rn.RelativeSpeed;
            prepare.Type          = NoteType.SpecialPrepare;
            prepare.StartX        = rn.StartX;
            prepare.EndX          = rn.EndX;
            prepare.ExtraInfo     = null;

            var end = new RuntimeNote();

            end.ID = ++currentID;
            // 1.5 seconds before next valid note (tap, flick, hold, slide).
            // Just a guess. Didn't find any proof or ways to calculate this.
            // This value must keep the same as tap points' "fade in" animation length.
            // See NoteReactor.Update() for more information.
            var firstNoteAfterSpecial = gamingNotes.FirstOrDefault(n => n.Ticks > note.Ticks);

            if (firstNoteAfterSpecial == null)
            {
                throw new ArgumentException("Malformed score: no note after special note.");
            }

            end.HitTime       = TicksToSeconds(firstNoteAfterSpecial.Ticks, conductors) - 1.5f;
            end.LeadTime      = rn.LeadTime;
            end.RelativeSpeed = rn.RelativeSpeed;
            end.Type          = NoteType.SpecialEnd;
            end.StartX        = rn.StartX;
            end.EndX          = rn.EndX;
            end.ExtraInfo     = null;

            // The order here is not very important because later they will all be sorted by HitTime.
            return(new[] { rn, prepare, end });
        }
Example #15
0
 public static OnStageStatus GetOnStageStatusOf(RuntimeNote note, double now, NoteTimePoints timePoints)
 {
     if (now < timePoints.Enter)
     {
         return(OnStageStatus.Incoming);
     }
     else if (now > timePoints.Leave)
     {
         return(OnStageStatus.Passed);
     }
     else
     {
         return(OnStageStatus.Visible);
     }
 }
Example #16
0
        private static float GetIncomingNoteXRatio([NotNull] RuntimeNote prevNote, RuntimeNote thisNote, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var trackCount       = animationMetrics.TrackCount;
            var trackXRatioStart = animationMetrics.NoteEndXRatios[0];
            var trackXRatioEnd   = animationMetrics.NoteEndXRatios[trackCount - 1];

            var thisXRatio = trackXRatioStart + (trackXRatioEnd - trackXRatioStart) * (prevNote.EndX / (trackCount - 1));
            var nextXRatio = trackXRatioStart + (trackXRatioEnd - trackXRatioStart) * (thisNote.EndX / (trackCount - 1));

            var thisTimePoints = NoteAnimationHelper.CalculateNoteTimePoints(prevNote, animationMetrics);
            var nextTimePoints = NoteAnimationHelper.CalculateNoteTimePoints(thisNote, animationMetrics);

            var perc = (float)(now - thisTimePoints.Enter) / (float)(nextTimePoints.Enter - thisTimePoints.Enter);

            return(MathHelper.Lerp(thisXRatio, nextXRatio, perc));
        }
Example #17
0
        private static float GetNoteXByTransformedTime(RuntimeNote note, double transformedTime, NoteAnimationMetrics animationMetrics)
        {
            var trackCount            = animationMetrics.TrackCount;
            var startLeftMarginRatio  = animationMetrics.NoteStartXRatios[0];
            var startRightMarginRatio = animationMetrics.NoteStartXRatios[trackCount - 1];
            var endLeftMarginRatio    = animationMetrics.NoteEndXRatios[0];
            var endRightMarginRatio   = animationMetrics.NoteEndXRatios[trackCount - 1];

            var startXRatio = startLeftMarginRatio + (startRightMarginRatio - startLeftMarginRatio) * (note.StartX / (trackCount - 1));
            var endXRatio   = endLeftMarginRatio + (endRightMarginRatio - endLeftMarginRatio) * (note.EndX / (trackCount - 1));

            var startX = animationMetrics.Width * startXRatio;
            var endX   = animationMetrics.Width * endXRatio;

            return(endX - (endX - startX) * NoteXTransform(transformedTime));
        }
Example #18
0
        public static RuntimeNote[] CreateTapNote(SourceNote note, Conductor[] conductors, ref int currentID)
        {
            var rn = new RuntimeNote();

            rn.Type           = note.Type;
            rn.Size           = note.Size;
            rn.FlickDirection = note.FlickDirection;

            rn.ID            = ++currentID;
            rn.HitTime       = TicksToSeconds(note.Ticks, conductors);
            rn.LeadTime      = note.LeadTime;
            rn.RelativeSpeed = note.Speed;
            rn.StartX        = note.StartX;
            rn.EndX          = note.EndX;

            return(new[] { rn });
        }
Example #19
0
        private static double GetTransformedTime(RuntimeNote note, double now, NoteAnimationMetrics animationMetrics)
        {
            var timePoints            = NoteAnimationHelper.CalculateNoteTimePoints(note, animationMetrics);
            var timeRemaining         = note.HitTime - now;
            var timeRemainingInWindow = (float)timeRemaining / timePoints.Duration;

            if (timeRemaining > timePoints.Duration)
            {
                timeRemainingInWindow = 1;
            }

            if (timeRemaining < 0)
            {
                timeRemainingInWindow = 0;
            }

            return(NoteTimeTransform(timeRemainingInWindow));
        }
Example #20
0
        public override RibbonParameters GetHoldRibbonParameters(RuntimeNote startNote, RuntimeNote endNote, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var t1   = (float)GetTransformedTime(startNote, now, animationMetrics);
            var t2   = (float)GetTransformedTime(endNote, now, animationMetrics);
            var tmid = (t1 + t2) * 0.5f;

            var x1   = GetNoteXByTransformedTime(startNote, t1, animationMetrics);
            var x2   = GetNoteXByTransformedTime(endNote, t2, animationMetrics);
            var xmid = GetNoteXByTransformedTime(endNote, tmid, animationMetrics);

            var y1   = GetNoteYByTransformedTime(t1, animationMetrics);
            var y2   = GetNoteYByTransformedTime(t2, animationMetrics);
            var ymid = GetNoteYByTransformedTime(tmid, animationMetrics);

            var(controlX1, controlX2) = GetBezierFromQuadratic(x1, xmid, x2);
            var(controlY1, controlY2) = GetBezierFromQuadratic(y1, ymid, y2);

            return(new RibbonParameters(x1, y1, controlX1, controlY1, controlX2, controlY2, x2, y2));
        }
Example #21
0
        public override Vector2 GetNoteRadius(RuntimeNote note, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var onStageStatus = NoteAnimationHelper.GetOnStageStatusOf(note, now, animationMetrics);

            switch (onStageStatus)
            {
            case OnStageStatus.Incoming:
                return(Vector2.Zero);

            case OnStageStatus.Passed:
                return(noteMetrics.EndRadius);
            }

            var timeRemaining   = note.HitTime - now;
            var timePoints      = NoteAnimationHelper.CalculateNoteTimePoints(note, animationMetrics);
            var timeTransformed = NoteTimeTransform(timeRemaining / timePoints.Duration);
            var endRadius       = noteMetrics.EndRadius;

            if (timeTransformed < 0.75f)
            {
                if (timeTransformed < 0f)
                {
                    return(endRadius);
                }
                else
                {
                    var r = 1 - timeTransformed * 0.933333333f;
                    return(new Vector2(endRadius.X * r, endRadius.Y * r));
                }
            }
            else
            {
                if (timeTransformed < 1f)
                {
                    var r = (1 - timeTransformed) * 1.2f;
                    return(new Vector2(endRadius.X * r, endRadius.Y * r));
                }
                else
                {
                    return(Vector2.Zero);
                }
            }
        }
Example #22
0
        public override float GetNoteX(RuntimeNote note, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var trackCount       = animationMetrics.TrackCount;
            var trackXRatioStart = animationMetrics.NoteEndXRatios[0];
            var trackXRatioEnd   = animationMetrics.NoteEndXRatios[trackCount - 1];

            var endXRatio = trackXRatioStart + (trackXRatioEnd - trackXRatioStart) * (note.EndX / (trackCount - 1));

            var   onStage = NoteAnimationHelper.GetOnStageStatusOf(note, now, animationMetrics);
            float xRatio;

            switch (onStage)
            {
            case OnStageStatus.Incoming:
                if (note.HasPrevHold())
                {
                    xRatio = GetIncomingNoteXRatio(note.PrevHold, note, now, noteMetrics, animationMetrics);
                }
                else if (note.HasPrevSlide())
                {
                    xRatio = GetIncomingNoteXRatio(note.PrevSlide, note, now, noteMetrics, animationMetrics);
                }
                else
                {
                    xRatio = endXRatio;
                }
                break;

            case OnStageStatus.Passed when note.HasNextSlide():
                var destXRatio = trackXRatioStart + (trackXRatioEnd - trackXRatioStart) * (note.NextSlide.EndX / (trackCount - 1));

                var nextPerc = (float)(now - note.HitTime) / (float)(note.NextSlide.HitTime - note.HitTime);
                xRatio = MathHelper.Lerp(endXRatio, destXRatio, nextPerc);
                break;

            default:
                xRatio = endXRatio;
                break;
            }

            return(animationMetrics.Width * xRatio);
        }
        private static double GetTransformedTime(RuntimeNote note, double now, NoteTimePoints timePoints, bool clampIncoming = true, bool clampPassed = true)
        {
            var timeRemaining = note.HitTime - now;

            double timeRemainingInWindow;

            if (clampIncoming && timeRemaining > timePoints.Duration)
            {
                timeRemainingInWindow = 1f;
            }
            else if (clampPassed && timeRemaining < 0f)
            {
                timeRemainingInWindow = 0f;
            }
            else
            {
                timeRemainingInWindow = timeRemaining / timePoints.Duration;
            }

            return(WtfTransform(timeRemainingInWindow));
        }
Example #24
0
        public override float GetNoteX(RuntimeNote note, double now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            if (note.IsSlide() && note.HasNextSlide())
            {
                var thisStatus = NoteAnimationHelper.GetOnStageStatusOf(note, now, animationMetrics);
                if (thisStatus >= OnStageStatus.Passed)
                {
                    var nextSlide  = note.NextSlide;
                    var nextStatus = NoteAnimationHelper.GetOnStageStatusOf(nextSlide, now, animationMetrics);
                    if (nextStatus < OnStageStatus.Passed)
                    {
                        var x1 = GetEndXByNotePosition(note.EndX, animationMetrics);
                        var x2 = GetEndXByNotePosition(nextSlide.EndX, animationMetrics);
                        return((float)((now - note.HitTime) / (nextSlide.HitTime - note.HitTime)) * (x2 - x1) + x1);
                    }
                }
            }

            var transformedTime = GetTransformedTime(note, now, animationMetrics);

            return(GetNoteXByTransformedTime(note, transformedTime, animationMetrics));
        }
        private static float GetIncomingNoteXRatio(RuntimeNote prevNote, RuntimeNote thisNote, float now, NoteAnimationMetrics animationMetrics)
        {
            var trackCount            = animationMetrics.TrackCount;
            var startLeftMarginRatio  = animationMetrics.NoteStartXRatios[0];
            var startRightMarginRatio = animationMetrics.NoteStartXRatios[trackCount - 1];

            float xRatio;

            if (thisNote.IsSlide())
            {
                var thisXRatio = startLeftMarginRatio + (startRightMarginRatio - startLeftMarginRatio) * (prevNote.EndX / (trackCount - 1));
                var nextXRatio = startLeftMarginRatio + (startRightMarginRatio - startLeftMarginRatio) * (thisNote.EndX / (trackCount - 1));

                var thisTimePoints = NoteAnimationHelper.CalculateNoteTimePoints(prevNote, animationMetrics);
                var nextTimePoints = NoteAnimationHelper.CalculateNoteTimePoints(thisNote, animationMetrics);

                var perc = (now - thisTimePoints.Enter) / (nextTimePoints.Enter - thisTimePoints.Enter);
                xRatio = MathHelperEx.Lerp(thisXRatio, nextXRatio, perc);
            }
            else
            {
                float nextStartX;
                if (thisNote.StartX < 0)
                {
                    nextStartX = thisNote.StartX * 0.5f;
                }
                else if (thisNote.StartX > trackCount - 1)
                {
                    nextStartX = (trackCount - 1) + (thisNote.StartX - (trackCount - 1)) * 0.5f;
                }
                else
                {
                    nextStartX = thisNote.StartX;
                }
                xRatio = startLeftMarginRatio + (startRightMarginRatio - startLeftMarginRatio) * (nextStartX / (trackCount - 1));
            }

            return(xRatio);
        }
        public override Vector2 GetNoteRadius(RuntimeNote note, float now, NoteMetrics noteMetrics, NoteAnimationMetrics animationMetrics)
        {
            var timePoints    = NoteAnimationHelper.CalculateNoteTimePoints(note, animationMetrics);
            var onStageStatus = NoteAnimationHelper.GetOnStageStatusOf(note, now, timePoints);

            switch (onStageStatus)
            {
            case OnStageStatus.Incoming:
                return(noteMetrics.StartRadius);

            case OnStageStatus.Visible:
                var passed = now - timePoints.Enter;
                var perc   = passed / timePoints.Duration;
                var w      = MathHelperEx.Lerp(noteMetrics.StartRadius.X, noteMetrics.EndRadius.X, perc);
                var h      = MathHelperEx.Lerp(noteMetrics.StartRadius.Y, noteMetrics.EndRadius.Y, perc);
                return(new Vector2(w, h));

            case OnStageStatus.Passed:
                return(noteMetrics.EndRadius);

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
 public static bool HasPrevSlide(this RuntimeNote note)
 {
     return(note.PrevSlide != null);
 }
 public static bool IsSlideEnd(this RuntimeNote note)
 {
     return(note.PrevSlide != null && note.NextSlide == null);
 }
 public static bool IsSlide(this RuntimeNote note)
 {
     return(note.PrevSlide != null || note.NextSlide != null);
 }
 public static bool HasNextFlick(this RuntimeNote note)
 {
     return(note.NextFlick != null);
 }