Exemple #1
0
        private void load()
        {
            var maniaPlayfield = (ManiaPlayfield)Playfield;

            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

            SortedList <TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;

            for (int i = 0; i < timingPoints.Count; i++)
            {
                TimingControlPoint point = timingPoints[i];

                // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
                double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;

                int index = 0;
                for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
                {
                    maniaPlayfield.Add(new DrawableBarLine(new BarLine
                    {
                        StartTime    = t,
                        ControlPoint = point,
                        BeatIndex    = index
                    }));
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Calculates the snapped time for a given time and makes sure the snapped time is not outside the time range of a hit object.
        /// This can be used to resnap stuff that has to be within the time range of a slider. For example volume changes inside a slider body.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="divisor2">The first beat snap divisor.</param>
        /// <param name="divisor3">The second beat snap divisor.</param>
        /// <param name="ho">The hit object with a time range that the specified time has to stay inside.</param>
        /// <param name="floor">Whether or not to floor the time after snapping.</param>
        /// <param name="tp">The uninherited timing point to snap to. Leave null for automatic selection.</param>
        /// <param name="firstTp">Overwrites the timing for anything that happens before the first timing point.
        /// You can set this to avoid bad timing when there could be an inherited timing point before the first red line.</param>
        /// <returns>The snapped time.</returns>
        public double ResnapInRange(double time, int divisor2, int divisor3, HitObject ho, bool floor = true, TimingPoint tp = null, TimingPoint firstTp = null)
        {
            TimingPoint beforeTp = tp ?? GetRedlineAtTime(time, firstTp);
            TimingPoint afterTp  = tp == null?GetRedlineAfterTime(time) : null;

            double newTime2      = GetNearestTimeMeter(time, beforeTp, divisor2);
            double snapDistance2 = Math.Abs(time - newTime2);

            double newTime3      = GetNearestTimeMeter(time, beforeTp, divisor3);
            double snapDistance3 = Math.Abs(time - newTime3);

            double newTime = snapDistance3 < snapDistance2 ? newTime3 : newTime2;

            if (afterTp != null && Precision.DefinitelyBigger(newTime, afterTp.Offset))
            {
                newTime = afterTp.Offset;
            }

            if (newTime <= ho.Time + 1 || newTime >= ho.EndTime - 1)  // Don't resnap if it would move outside
            {
                newTime = time;
            }

            return(floor ? Math.Floor(newTime) : newTime);
        }
Exemple #3
0
        public void TestMouseZoomInTwiceOutTwice()
        {
            reset();

            AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft));

            // Scroll in at 0.25
            AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
            AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));

            // Scroll in at 0.6
            AddStep("Move mouse to 0.75x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.75f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
            AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));
            AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));

            // Very hard to determine actual position, so approximate
            AddAssert("Box at correct position (1)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X));
            AddAssert("Box at correct position (2)", () => Precision.DefinitelyBigger(scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X, boxQuad.TopLeft.X + 0.3f * boxQuad.Size.X));
            AddAssert("Box at correct position (3)", () => Precision.DefinitelyBigger(boxQuad.TopLeft.X + 0.6f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.6f * scrollQuad.Size.X));

            // Scroll out at 0.6
            AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));

            // Scroll out at 0.25
            AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
            AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));
            AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));

            AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft));
        }
Exemple #4
0
            protected override void Update()
            {
                base.Update();

                // the bounds below represent the horizontal range of scroll items to be considered fully visible/active, in the scroll's internal coordinate space.
                // note that clamping is applied to the left scroll bound to ensure scrolling past extents does not change the set of active columns.
                float leftVisibleBound  = Math.Clamp(Current, 0, ScrollableExtent);
                float rightVisibleBound = leftVisibleBound + DrawWidth;

                // if a movement is occurring at this time, the bounds below represent the full range of columns that the scroll movement will encompass.
                // this will be used to ensure that columns do not change state from active to inactive back and forth until they are fully scrolled past.
                float leftMovementBound  = Math.Min(Current, Target);
                float rightMovementBound = Math.Max(Current, Target) + DrawWidth;

                foreach (var column in Child)
                {
                    // DrawWidth/DrawPosition do not include shear effects, and we want to know the full extents of the columns post-shear,
                    // so we have to manually compensate.
                    var topLeft     = column.ToSpaceOfOtherDrawable(Vector2.Zero, ScrollContent);
                    var bottomRight = column.ToSpaceOfOtherDrawable(new Vector2(column.DrawWidth - column.DrawHeight * SHEAR, 0), ScrollContent);

                    bool isCurrentlyVisible = Precision.AlmostBigger(topLeft.X, leftVisibleBound) &&
                                              Precision.DefinitelyBigger(rightVisibleBound, bottomRight.X);
                    bool isBeingScrolledToward = Precision.AlmostBigger(topLeft.X, leftMovementBound) &&
                                                 Precision.DefinitelyBigger(rightMovementBound, bottomRight.X);

                    column.Active.Value = isCurrentlyVisible || isBeingScrolledToward;
                }
            }
        private void initialBarLine()
        {
            var beatmap        = KaraokeRulesetContainer.Beatmap;
            var objects        = beatmap.HitObjects;
            var lastObjectTime = (objects.LastOrDefault() as IHasEndTime)?.EndTime ?? objects.LastOrDefault()?.StartTime ?? double.MaxValue;
            var timingPoints   = beatmap.ControlPointInfo.TimingPoints;
            var barLines       = new List <BarLine>();

            for (var i = 0; i < timingPoints.Count; i++)
            {
                var point = timingPoints[i];

                var endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;

                var index = 0;
                for (var t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
                {
                    barLines.Add(new BarLine
                    {
                        StartTime    = t,
                        ControlPoint = point,
                        BeatIndex    = index
                    });
                }
            }
            BarLines = barLines;
            BarLines.ForEach(Add);
        }
Exemple #6
0
        public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
            : base(ruleset, beatmap)
        {
            // Generate the bar lines
            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

            var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
            var barLines     = new List <BarLine>();

            for (int i = 0; i < timingPoints.Count; i++)
            {
                TimingControlPoint point = timingPoints[i];

                // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
                double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;

                int index = 0;
                for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
                {
                    barLines.Add(new BarLine
                    {
                        StartTime    = t,
                        ControlPoint = point,
                        BeatIndex    = index
                    });
                }
            }

            BarLines = barLines;
        }
        /// <summary>
        /// Method that generates hit objects for non-osu!mania beatmaps.
        /// </summary>
        /// <param name="original">The original hit object.</param>
        /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
        /// <returns>The hit objects generated.</returns>
        private IEnumerable <ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
        {
            Patterns.PatternGenerator conversion = null;

            switch (original)
            {
            case IHasDistance _:
            {
                var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
                conversion = generator;

                var positionData = original as IHasPosition;

                for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
                {
                    recordNote(time, positionData?.Position ?? Vector2.Zero);
                    computeDensity(time);
                }

                break;
            }

            case IHasEndTime endTimeData:
            {
                conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);

                recordNote(endTimeData.EndTime, new Vector2(256, 192));
                computeDensity(endTimeData.EndTime);
                break;
            }

            case IHasPosition positionData:
            {
                computeDensity(original.StartTime);

                conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);

                recordNote(original.StartTime, positionData.Position);
                break;
            }
            }

            if (conversion == null)
            {
                yield break;
            }

            foreach (var newPattern in conversion.Generate())
            {
                lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
                lastStair   = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;

                foreach (var obj in newPattern.HitObjects)
                {
                    yield return(obj);
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Calculates the size of the effective time range of a given timing point.
        /// This range stops at the next timing point, so it just returns the offset of the next timing point.
        /// </summary>
        /// <param name="timingPoint"></param>
        /// <returns>The timing point after specified timing point.</returns>
        public double GetTimingPointEffectiveRange(TimingPoint timingPoint)
        {
            foreach (var tp in TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, timingPoint.Offset)))
            {
                return(tp.Offset);
            }

            return(double.PositiveInfinity); // Being the last timingpoint, the effective range is infinite (very big)
        }
Exemple #9
0
 public void TestOpacity()
 {
     AddMoveStep(100, 0);
     AddClickStep(MouseButton.Left);
     AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha));
     AddMoveStep(200, 0);
     AddUntilStep("outline is opaque", () => Precision.AlmostEquals(timeSpanOutline.Alpha, 1));
     AddMoveStep(100, 0);
     AddUntilStep("outline is semitransparent", () => Precision.DefinitelyBigger(1, timeSpanOutline.Alpha));
 }
Exemple #10
0
        public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
            : base(beatmap, isForCurrentRuleset)
        {
            // Generate the speed adjustment container lists
            hitObjectSpeedAdjustments = new List <SpeedAdjustmentContainer> [PreferredColumns];
            for (int i = 0; i < PreferredColumns; i++)
            {
                hitObjectSpeedAdjustments[i] = new List <SpeedAdjustmentContainer>();
            }

            // Generate the bar lines
            double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;

            SortedList <TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
            var barLines = new List <DrawableBarLine>();

            for (int i = 0; i < timingPoints.Count; i++)
            {
                TimingControlPoint point = timingPoints[i];

                // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
                double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;

                int index = 0;
                for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
                {
                    barLines.Add(new DrawableBarLine(new BarLine
                    {
                        StartTime    = t,
                        ControlPoint = point,
                        BeatIndex    = index
                    }));
                }
            }

            BarLines = barLines;

            // Generate speed adjustments from mods first
            bool useDefaultSpeedAdjustments = true;

            if (Mods != null)
            {
                foreach (var speedAdjustmentMod in Mods.OfType <IGenerateSpeedAdjustments>())
                {
                    useDefaultSpeedAdjustments = false;
                    speedAdjustmentMod.ApplyToHitRenderer(this, ref hitObjectSpeedAdjustments, ref barLineSpeedAdjustments);
                }
            }

            // Generate the default speed adjustments
            if (useDefaultSpeedAdjustments)
            {
                generateDefaultSpeedAdjustments();
            }
        }
Exemple #11
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var    maniaCurrent   = (ManiaDifficultyHitObject)current;
            double endTime        = maniaCurrent.EndTime;
            int    column         = maniaCurrent.BaseObject.Column;
            double closestEndTime = Math.Abs(endTime - maniaCurrent.LastObject.StartTime); // Lowest value we can assume with the current information

            double holdFactor    = 1.0;                                                    // Factor to all additional strains in case something else is held
            double holdAddition  = 0;                                                      // Addition to the current note in case it's a hold and has to be released awkwardly
            bool   isOverlapping = false;

            // Fill up the holdEndTimes array
            for (int i = 0; i < holdEndTimes.Length; ++i)
            {
                // The current note is overlapped if a previous note or end is overlapping the current note body
                isOverlapping |= Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1);

                // We give a slight bonus to everything if something is held meanwhile
                if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1))
                {
                    holdFactor = 1.25;
                }

                closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - holdEndTimes[i]));

                // Decay individual strains
                individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base);
            }

            holdEndTimes[column] = endTime;

            // The hold addition is given if there was an overlap, however it is only valid if there are no other note with a similar ending.
            // Releasing multiple notes is just as easy as releasing 1. Nerfs the hold addition by half if the closest release is release_threshold away.
            // holdAddition
            //     ^
            // 1.0 + - - - - - -+-----------
            //     |           /
            // 0.5 + - - - - -/   Sigmoid Curve
            //     |         /|
            // 0.0 +--------+-+---------------> Release Difference / ms
            //         release_threshold
            if (isOverlapping)
            {
                holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime)));
            }

            // Increase individual strain in own column
            individualStrains[column] += 2.0 * holdFactor;
            individualStrain           = individualStrains[column];

            overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;

            return(individualStrain + overallStrain - CurrentStrain);
        }
Exemple #12
0
 private bool shouldUpdateHue(float newHue)
 {
     // there are two situations in which a hue value change is possibly unwanted.
     // * if saturation is near-zero, it may not be really possible to accurately measure the hue of the colour,
     //   as hsv(x, 0, y) == hsv(z, 0, y) for any x,y,z.
     // * similarly, the hues of 0 and 1 are functionally equivalent,
     //   as hsv(0, x, y) == hsv(1, x, y) for any x,y.
     // in those cases, just keep the hue as it was, as the colour will still be roughly the same to the point of being imperceptible,
     // and doing this will prevent UX idiosyncrasies (such as the hue slider jumping to 0 for no apparent reason).
     return(Precision.DefinitelyBigger(Saturation.Value, 0) &&
            !Precision.AlmostEquals(Hue.Value - newHue, 1));
 }
Exemple #13
0
        /// <summary>
        /// Finds the timing point which is in effect at a given time with a custom set of timing points.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="timingPoints">All the timing points.</param>
        /// <param name="firstTimingpoint">The first timing point to start searching from.</param>
        /// <returns></returns>
        public static TimingPoint GetTimingPointAtTime(double time, List <TimingPoint> timingPoints, TimingPoint firstTimingpoint)
        {
            TimingPoint lastTp = firstTimingpoint;

            foreach (TimingPoint tp in timingPoints)
            {
                if (Precision.DefinitelyBigger(tp.Offset, time))
                {
                    return(lastTp);
                }
                lastTp = tp;
            }
            return(lastTp);
        }
Exemple #14
0
        private void convertToStream()
        {
            if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
            {
                return;
            }

            var    timingPoint   = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime);
            double streamSpacing = timingPoint.BeatLength / beatDivisor.Value;

            changeHandler.BeginChange();

            int    i    = 0;
            double time = HitObject.StartTime;

            while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1))
            {
                // positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()]
                // and indicates how many fractional spans of a slider have passed up to time.
                double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount();
                double pathPosition        = positionWithRepeats - (int)positionWithRepeats;
                // every second span is in the reverse direction - need to reverse the path position.
                if (Precision.AlmostBigger(positionWithRepeats % 2, 1))
                {
                    pathPosition = 1 - pathPosition;
                }

                Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);

                var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
                samplePoint.Time = time;

                editorBeatmap.Add(new HitCircle
                {
                    StartTime          = time,
                    Position           = position,
                    NewCombo           = i == 0 && HitObject.NewCombo,
                    SampleControlPoint = samplePoint,
                    Samples            = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
                });

                i   += 1;
                time = HitObject.StartTime + i * streamSpacing;
            }

            editorBeatmap.Remove(HitObject);

            changeHandler.EndChange();
        }
Exemple #15
0
        /// <summary>
        /// Constructs and generates bar lines for provided beatmap.
        /// </summary>
        /// <param name="beatmap">The beatmap to generate bar lines for.</param>
        public BarLineGenerator(IBeatmap beatmap)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return;
            }

            HitObject lastObject  = beatmap.HitObjects.Last();
            double    lastHitTime = 1 + lastObject.GetEndTime();

            var timingPoints = beatmap.ControlPointInfo.TimingPoints;

            if (timingPoints.Count == 0)
            {
                return;
            }

            for (int i = 0; i < timingPoints.Count; i++)
            {
                TimingControlPoint currentTimingPoint = timingPoints[i];
                int currentBeat = 0;

                // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
                double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator;

                double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator;

                for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++)
                {
                    double roundedTime = Math.Round(t, MidpointRounding.AwayFromZero);

                    // in the case of some bar lengths, rounding errors can cause t to be slightly less than
                    // the expected whole number value due to floating point inaccuracies.
                    // if this is the case, apply rounding.
                    if (Precision.AlmostEquals(t, roundedTime))
                    {
                        t = roundedTime;
                    }

                    BarLines.Add(new TBarLine
                    {
                        StartTime = t,
                        Major     = currentBeat % currentTimingPoint.TimeSignature.Numerator == 0
                    });
                }
            }
        }
Exemple #16
0
        /// <summary>
        /// Finds the uninherited <see cref="TimingPoint"/> which is in effect at a given time.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="firstTimingPoint"></param>
        /// <returns></returns>
        public TimingPoint GetRedlineAtTime(double time, TimingPoint firstTimingPoint = null)
        {
            TimingPoint lastTp = firstTimingPoint ?? GetFirstTimingPointExtended();

            foreach (TimingPoint tp in TimingPoints)
            {
                if (Precision.DefinitelyBigger(tp.Offset, time))
                {
                    return(lastTp);
                }
                if (tp.Uninherited)
                {
                    lastTp = tp;
                }
            }
            return(lastTp);
        }
Exemple #17
0
        protected override double StrainValueOf(DifficultyHitObject current)
        {
            var maniaCurrent = (ManiaDifficultyHitObject)current;
            var endTime      = maniaCurrent.BaseObject.GetEndTime();
            var column       = maniaCurrent.BaseObject.Column;

            double holdFactor   = 1.0; // Factor to all additional strains in case something else is held
            double holdAddition = 0;   // Addition to the current note in case it's a hold and has to be released awkwardly

            // Fill up the holdEndTimes array
            for (int i = 0; i < holdEndTimes.Length; ++i)
            {
                // If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
                if (Precision.DefinitelyBigger(holdEndTimes[i], maniaCurrent.BaseObject.StartTime, 1) && Precision.DefinitelyBigger(endTime, holdEndTimes[i], 1))
                {
                    holdAddition = 1.0;
                }

                // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
                if (Precision.AlmostEquals(endTime, holdEndTimes[i], 1))
                {
                    holdAddition = 0;
                }

                // We give a slight bonus to everything if something is held meanwhile
                if (Precision.DefinitelyBigger(holdEndTimes[i], endTime, 1))
                {
                    holdFactor = 1.25;
                }

                // Decay individual strains
                individualStrains[i] = applyDecay(individualStrains[i], current.DeltaTime, individual_decay_base);
            }

            holdEndTimes[column] = endTime;

            // Increase individual strain in own column
            individualStrains[column] += 2.0 * holdFactor;
            individualStrain           = individualStrains[column];

            overallStrain = applyDecay(overallStrain, current.DeltaTime, overall_decay_base) + (1 + holdAddition) * holdFactor;

            return(individualStrain + overallStrain - CurrentStrain);
        }
Exemple #18
0
        /// <summary>
        /// Returns the beat snap divisor closest to the given time. If two are equally close, the smallest divisor is returned.
        /// </summary>
        /// <param name="time">The time to find the closest beat snap divisor to.</param>
        /// <param name="referenceTime">An optional reference point to use for timing point lookup.</param>
        public int GetClosestBeatDivisor(double time, double?referenceTime = null)
        {
            TimingControlPoint timingPoint = TimingPointAt(referenceTime ?? time);

            int    closestDivisor = 0;
            double closestTime    = double.MaxValue;

            foreach (int divisor in BindableBeatDivisor.VALID_DIVISORS)
            {
                double distanceFromSnap = Math.Abs(time - getClosestSnappedTime(timingPoint, time, divisor));

                if (Precision.DefinitelyBigger(closestTime, distanceFromSnap))
                {
                    closestDivisor = divisor;
                    closestTime    = distanceFromSnap;
                }
            }

            return(closestDivisor);
        }
        public void TestBasicComponentLayout()
        {
            double[] times = { 100, 300, 500 };
            float[] positions = { 100, 200, 100 };
            addBlueprintStep(times, positions);

            for (int i = 0; i < times.Length; i++)
                addVertexCheckStep(times.Length, i, times[i], positions[i]);

            AddAssert("correct outline count", () =>
            {
                int expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet));
                return this.ChildrenOfType<FruitOutline>().Count() == expected;
            });
            AddAssert("correct vertex piece count", () =>
                this.ChildrenOfType<VertexPiece>().Count() == times.Length);

            AddAssert("first vertex is semitransparent", () =>
                Precision.DefinitelyBigger(1, this.ChildrenOfType<VertexPiece>().First().Alpha));
        }
Exemple #20
0
        /// <summary>
        /// Gets the slider velocity at a given time.
        /// This gives the value from the .osu.
        /// Ranges from -1000 to -10.
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public double GetSvAtTime(double time)
        {
            double lastSv = -100;

            foreach (TimingPoint tp in TimingPoints)
            {
                if (Precision.DefinitelyBigger(tp.Offset, time))
                {
                    return(MathHelper.Clamp(lastSv, -1000, -10));
                }
                if (!tp.Uninherited)
                {
                    lastSv = tp.MpB;
                }
                else
                {
                    lastSv = -100;
                }
            }
            return(MathHelper.Clamp(lastSv, -1000, -10));
        }
        public void TestWideImageNotExceedContainer()
        {
            AddStep("Add image", () =>
            {
                markdownContainer.CurrentPath = "https://dev.ppy.sh/wiki/osu!_Program_Files/";
                markdownContainer.Text        = "![](img/file_structure.jpg \"The file structure of osu!'s installation folder, on Windows and macOS\")";
            });

            AddUntilStep("Wait image to load", () => markdownContainer.ChildrenOfType <DelayedLoadWrapper>().First().DelayedLoadCompleted);

            AddStep("Change container width", () =>
            {
                markdownContainer.Width = 0.5f;
            });

            AddAssert("Image not exceed container width", () =>
            {
                var spriteImage = markdownContainer.ChildrenOfType <Sprite>().First();
                return(Precision.DefinitelyBigger(markdownContainer.DrawWidth, spriteImage.DrawWidth));
            });
        }
Exemple #22
0
        /// <summary>
        /// Constructs and generates bar lines for provided beatmap.
        /// </summary>
        /// <param name="beatmap">The beatmap to generate bar lines for.</param>
        public BarLineGenerator(IBeatmap beatmap)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return;
            }

            HitObject lastObject  = beatmap.HitObjects.Last();
            double    lastHitTime = 1 + lastObject.GetEndTime();

            var timingPoints = beatmap.ControlPointInfo.TimingPoints;

            if (timingPoints.Count == 0)
            {
                return;
            }

            for (int i = 0; i < timingPoints.Count; i++)
            {
                TimingControlPoint currentTimingPoint = timingPoints[i];
                int currentBeat = 0;

                // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
                double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;

                double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;

                for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++)
                {
                    BarLines.Add(new TBarLine
                    {
                        StartTime = t,
                        Major     = currentBeat % (int)currentTimingPoint.TimeSignature == 0
                    });
                }
            }
        }
        private string Merge_Sliders(SliderMergerVm arg, BackgroundWorker worker)
        {
            var slidersMerged = 0;

            var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1);

            if (arg.ImportModeSetting == 0 && editorReaderException1 != null)
            {
                throw new Exception("Could not fetch selected hit objects.", editorReaderException1);
            }

            foreach (var path in arg.Paths)
            {
                var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader, out var selected, out var editorReaderException2);

                if (arg.ImportModeSetting == SliderMergerVm.ImportMode.Selected && editorReaderException2 != null)
                {
                    throw new Exception("Could not fetch selected hit objects.", editorReaderException2);
                }

                var beatmap       = editor.Beatmap;
                var markedObjects = arg.ImportModeSetting == 0 ? selected :
                                    arg.ImportModeSetting == SliderMergerVm.ImportMode.Bookmarked ? beatmap.GetBookmarkedObjects() :
                                    arg.ImportModeSetting == SliderMergerVm.ImportMode.Time ? beatmap.QueryTimeCode(arg.TimeCode).ToList() :
                                    beatmap.HitObjects;

                var mergeLast = false;
                for (var i = 0; i < markedObjects.Count - 1; i++)
                {
                    if (worker != null && worker.WorkerReportsProgress)
                    {
                        worker.ReportProgress(i / markedObjects.Count);
                    }

                    var ho1 = markedObjects[i];
                    var ho2 = markedObjects[i + 1];

                    var lastPos1 = ho1.IsSlider
                        ? arg.MergeOnSliderEnd ? ho1.GetSliderPath().PositionAt(1) : ho1.CurvePoints.Last()
                        : ho1.Pos;

                    double dist = Vector2.Distance(lastPos1, ho2.Pos);

                    if (dist > arg.Leniency || !(ho2.IsSlider || ho2.IsCircle) || !(ho1.IsSlider || ho1.IsCircle))
                    {
                        mergeLast = false;
                        continue;
                    }

                    if (ho1.IsSlider && ho2.IsSlider)
                    {
                        if (arg.MergeOnSliderEnd)
                        {
                            // In order to merge on the slider end we first move the anchors such that the last anchor is exactly on the slider end
                            // After that merge as usual
                            ho1.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength(
                                                      ho1.GetAllCurvePoints(), ho1.SliderType, ho1.PixelLength, out var pathType));
                            ho1.SliderType = pathType;
                        }

                        var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints;
                        var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints;

                        double extraLength = 0;
                        switch (arg.ConnectionModeSetting)
                        {
                        case SliderMergerVm.ConnectionMode.Move:
                            Move(sp2, sp1.Last() - sp2.First());
                            break;

                        case SliderMergerVm.ConnectionMode.Linear:
                            sp1.Add(sp1.Last());
                            sp1.Add(sp2.First());
                            extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length;
                            break;
                        }

                        var mergedAnchors = sp1.Concat(sp2).ToList();
                        mergedAnchors.Round();

                        var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1) && IsLinearBezier(sp2);
                        if (linearLinear)
                        {
                            for (var j = 0; j < mergedAnchors.Count - 1; j++)
                            {
                                if (mergedAnchors[j] != mergedAnchors[j + 1])
                                {
                                    continue;
                                }
                                mergedAnchors.RemoveAt(j);
                                j--;
                            }
                        }

                        var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(),
                                                        ho1.PixelLength + ho2.PixelLength + extraLength);
                        ho1.SliderPath = mergedPath;

                        beatmap.HitObjects.Remove(ho2);
                        markedObjects.Remove(ho2);
                        i--;

                        slidersMerged++;
                        if (!mergeLast)
                        {
                            slidersMerged++;
                        }
                        mergeLast = true;
                    }
                    else if (ho1.IsSlider && ho2.IsCircle)
                    {
                        if (Precision.DefinitelyBigger(dist, 0))
                        {
                            var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints;

                            sp1.Add(sp1.Last());
                            sp1.Add(ho2.Pos);
                            var extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length;

                            var mergedAnchors = sp1;
                            mergedAnchors.Round();

                            var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1);
                            if (linearLinear)
                            {
                                for (var j = 0; j < mergedAnchors.Count - 1; j++)
                                {
                                    if (mergedAnchors[j] != mergedAnchors[j + 1])
                                    {
                                        continue;
                                    }
                                    mergedAnchors.RemoveAt(j);
                                    j--;
                                }
                            }

                            var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier,
                                                            mergedAnchors.ToArray(), ho1.PixelLength + extraLength);
                            ho1.SliderPath = mergedPath;
                        }

                        beatmap.HitObjects.Remove(ho2);
                        markedObjects.Remove(ho2);
                        i--;

                        slidersMerged++;
                        if (!mergeLast)
                        {
                            slidersMerged++;
                        }
                        mergeLast = true;
                    }
                    else if (ho1.IsCircle && ho2.IsSlider)
                    {
                        if (Precision.DefinitelyBigger(dist, 0))
                        {
                            var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints;

                            sp2.Insert(0, sp2.First());
                            sp2.Insert(0, ho1.Pos);
                            var extraLength = (ho1.Pos - ho2.Pos).Length;

                            var mergedAnchors = sp2;
                            mergedAnchors.Round();

                            var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp2);
                            if (linearLinear)
                            {
                                for (var j = 0; j < mergedAnchors.Count - 1; j++)
                                {
                                    if (mergedAnchors[j] != mergedAnchors[j + 1])
                                    {
                                        continue;
                                    }
                                    mergedAnchors.RemoveAt(j);
                                    j--;
                                }
                            }

                            var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho2.PixelLength + extraLength);
                            ho2.SliderPath = mergedPath;
                        }

                        beatmap.HitObjects.Remove(ho1);
                        markedObjects.Remove(ho1);
                        i--;

                        slidersMerged++;
                        if (!mergeLast)
                        {
                            slidersMerged++;
                        }
                        mergeLast = true;
                    }
                    else if (ho1.IsCircle && ho2.IsCircle)
                    {
                        if (Precision.DefinitelyBigger(dist, 0))
                        {
                            var mergedAnchors = new List <Vector2> {
                                ho1.Pos, ho2.Pos
                            };

                            var mergedPath = new SliderPath(arg.LinearOnLinear ? PathType.Linear : PathType.Bezier,
                                                            mergedAnchors.ToArray(), (ho1.Pos - ho2.Pos).Length);
                            ho1.SliderPath    = mergedPath;
                            ho1.IsCircle      = false;
                            ho1.IsSlider      = true;
                            ho1.Repeat        = 1;
                            ho1.EdgeHitsounds = new List <int> {
                                ho1.GetHitsounds(), ho2.GetHitsounds()
                            };
                            ho1.EdgeSampleSets = new List <SampleSet> {
                                ho1.SampleSet, ho2.SampleSet
                            };
                            ho1.EdgeAdditionSets = new List <SampleSet> {
                                ho1.AdditionSet, ho2.AdditionSet
                            };
                        }

                        beatmap.HitObjects.Remove(ho2);
                        markedObjects.Remove(ho2);
                        i--;

                        slidersMerged++;
                        if (!mergeLast)
                        {
                            slidersMerged++;
                        }
                        mergeLast = true;
                    }
                    else
                    {
                        mergeLast = false;
                    }

                    if (mergeLast && arg.Leniency == 727)
                    {
                        ho1.SetAllCurvePoints(MakePenis(ho1.GetAllCurvePoints(), ho1.PixelLength));
                        ho1.PixelLength *= 2;
                        ho1.SliderType   = PathType.Bezier;
                    }
                }

                // Save the file
                editor.SaveFile();
            }

            // Complete progressbar
            if (worker != null && worker.WorkerReportsProgress)
            {
                worker.ReportProgress(100);
            }

            // Do stuff
            RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, arg.Quick));

            // Make an accurate message
            var message = "";

            if (Math.Abs(slidersMerged) == 1)
            {
                message += "Successfully merged " + slidersMerged + " slider!";
            }
            else
            {
                message += "Successfully merged " + slidersMerged + " sliders!";
            }
            return(arg.Quick ? "" : message);
        }
Exemple #24
0
 private static bool definitelyBigger(double value1, double value2)
 {
     return(Precision.DefinitelyBigger(value1, value2, timing_precision));
 }
Exemple #25
0
 /// <summary>
 /// Finds all the timing points in a specified time range.
 /// </summary>
 /// <param name="startTime"></param>
 /// <param name="endTime"></param>
 /// <returns></returns>
 public List <TimingPoint> GetTimingPointsInTimeRange(double startTime, double endTime)
 {
     return(TimingPoints.Where(tp => Precision.DefinitelyBigger(tp.Offset, startTime) && Precision.DefinitelyBigger(endTime, tp.Offset)).ToList());
 }
Exemple #26
0
 /// <summary>
 /// Finds the nearest uninherited timing point which starts after a given time.
 /// </summary>
 /// <param name="time"></param>
 /// <returns></returns>
 public TimingPoint GetRedlineAfterTime(double time)
 {
     return(TimingPoints.FirstOrDefault(tp => Precision.DefinitelyBigger(tp.Offset, time) && tp.Uninherited));
 }
Exemple #27
0
        private void updatePopoverPositioning()
        {
            if (target == null || currentPopover == null)
            {
                return;
            }

            var targetLocalQuad = ToLocalSpace(target.ScreenSpaceDrawQuad);

            Anchor bestAnchor  = Anchor.Centre;
            float  biggestArea = 0;

            float totalSize = Math.Max(DrawSize.X * DrawSize.Y, 1);

            foreach (var anchor in candidate_anchors)
            {
                // Compute how much free space is available on this side of the target.
                var   availableSize = availableSizeAroundTargetForAnchor(targetLocalQuad, anchor);
                float area          = availableSize.X * availableSize.Y / totalSize;

                // If the free space is insufficient for the popover to fit in, do not consider this anchor further.
                if (availableSize.X < currentPopover.BoundingBoxContainer.DrawWidth || availableSize.Y < currentPopover.BoundingBoxContainer.DrawHeight)
                {
                    continue;
                }

                // The heuristic used to find the "best" anchor is the biggest area of free space available in the popover container
                // on the side of the anchor.
                if (Precision.DefinitelyBigger(area, biggestArea, 0.01f))
                {
                    biggestArea = area;
                    bestAnchor  = anchor;
                }
            }

            currentPopover.PopoverAnchor = bestAnchor.Opposite();

            var positionOnQuad = bestAnchor.PositionOnQuad(targetLocalQuad);

            currentPopover.Position = new Vector2(positionOnQuad.X - Padding.Left, positionOnQuad.Y - Padding.Top);

            // While the side has been chosen to maximise the area of free space available, that doesn't mean that the popover's body
            // will still fit in its entirety in the default configuration.
            // To avoid this, offset the popover so that it fits in the bounds of this container.
            var adjustment = new Vector2();

            var popoverContentLocalQuad = ToLocalSpace(currentPopover.Body.ScreenSpaceDrawQuad);

            if (popoverContentLocalQuad.TopLeft.X < 0)
            {
                adjustment.X = -popoverContentLocalQuad.TopLeft.X;
            }
            else if (popoverContentLocalQuad.BottomRight.X > DrawWidth)
            {
                adjustment.X = DrawWidth - popoverContentLocalQuad.BottomRight.X;
            }
            if (popoverContentLocalQuad.TopLeft.Y < 0)
            {
                adjustment.Y = -popoverContentLocalQuad.TopLeft.Y;
            }
            else if (popoverContentLocalQuad.BottomRight.Y > DrawHeight)
            {
                adjustment.Y = DrawHeight - popoverContentLocalQuad.BottomRight.Y;
            }

            currentPopover.Position += adjustment;

            // Even if the popover was moved, the arrow should stay fixed in place and point at the target's centre.
            // In such a case, apply a counter-adjustment to the arrow position.
            // The reason why just the body isn't moved is that the popover's autosize does not play well with that
            // (setting X/Y on the body can lead BoundingBox to be larger than it actually needs to be, causing 1-frame-errors)
            currentPopover.Arrow.Position = -adjustment;
        }
 static bool checkScrollCurrent(BasicScrollContainer scrolled, BasicScrollContainer notScrolled) => notScrolled.Current == 0 && Precision.DefinitelyBigger(scrolled.Current, 0f);