mirror of
https://github.com/ppy/osu.git
synced 2025-02-12 04:03:20 +08:00
Rename rhythm preprocessing objects to be clearer with intent
This commit is contained in:
parent
ef8867704a
commit
20a76d832d
@ -25,32 +25,32 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||
double samePattern = 0;
|
||||
double intervalPenalty = 0;
|
||||
|
||||
if (rhythm.SameRhythmHitObjects?.FirstHitObject == hitObject) // Difficulty for SameRhythmHitObjects
|
||||
if (rhythm.SameRhythmGroupedHitObjects?.FirstHitObject == hitObject) // Difficulty for SameRhythmGroupedHitObjects
|
||||
{
|
||||
sameRhythm += 10.0 * evaluateDifficultyOf(rhythm.SameRhythmHitObjects, hitWindow);
|
||||
intervalPenalty = repeatedIntervalPenalty(rhythm.SameRhythmHitObjects, hitWindow);
|
||||
sameRhythm += 10.0 * evaluateDifficultyOf(rhythm.SameRhythmGroupedHitObjects, hitWindow);
|
||||
intervalPenalty = repeatedIntervalPenalty(rhythm.SameRhythmGroupedHitObjects, hitWindow);
|
||||
}
|
||||
|
||||
if (rhythm.SamePatterns?.FirstHitObject == hitObject) // Difficulty for SamePatterns
|
||||
samePattern += 1.15 * ratioDifficulty(rhythm.SamePatterns.IntervalRatio);
|
||||
if (rhythm.SamePatternsGroupedHitObjects?.FirstHitObject == hitObject) // Difficulty for SamePatternsGroupedHitObjects
|
||||
samePattern += 1.15 * ratioDifficulty(rhythm.SamePatternsGroupedHitObjects.IntervalRatio);
|
||||
|
||||
difficulty += Math.Max(sameRhythm, samePattern) * intervalPenalty;
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
private static double evaluateDifficultyOf(SameRhythmHitObjects sameRhythmHitObjects, double hitWindow)
|
||||
private static double evaluateDifficultyOf(SameRhythmGroupedHitObjects sameRhythmGroupedHitObjects, double hitWindow)
|
||||
{
|
||||
double intervalDifficulty = ratioDifficulty(sameRhythmHitObjects.HitObjectIntervalRatio);
|
||||
double? previousInterval = sameRhythmHitObjects.Previous?.HitObjectInterval;
|
||||
double intervalDifficulty = ratioDifficulty(sameRhythmGroupedHitObjects.HitObjectIntervalRatio);
|
||||
double? previousInterval = sameRhythmGroupedHitObjects.Previous?.HitObjectInterval;
|
||||
|
||||
intervalDifficulty *= repeatedIntervalPenalty(sameRhythmHitObjects, hitWindow);
|
||||
intervalDifficulty *= repeatedIntervalPenalty(sameRhythmGroupedHitObjects, hitWindow);
|
||||
|
||||
// If a previous interval exists and there are multiple hit objects in the sequence:
|
||||
if (previousInterval != null && sameRhythmHitObjects.Children.Count > 1)
|
||||
if (previousInterval != null && sameRhythmGroupedHitObjects.Children.Count > 1)
|
||||
{
|
||||
double expectedDurationFromPrevious = (double)previousInterval * sameRhythmHitObjects.Children.Count;
|
||||
double durationDifference = sameRhythmHitObjects.Duration - expectedDurationFromPrevious;
|
||||
double expectedDurationFromPrevious = (double)previousInterval * sameRhythmGroupedHitObjects.Children.Count;
|
||||
double durationDifference = sameRhythmGroupedHitObjects.Duration - expectedDurationFromPrevious;
|
||||
|
||||
if (durationDifference > 0)
|
||||
{
|
||||
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||
|
||||
// Penalise patterns that can be hit within a single hit window.
|
||||
intervalDifficulty *= DifficultyCalculationUtils.Logistic(
|
||||
sameRhythmHitObjects.Duration / hitWindow,
|
||||
sameRhythmGroupedHitObjects.Duration / hitWindow,
|
||||
midpointOffset: 0.6,
|
||||
multiplier: 1,
|
||||
maxValue: 1);
|
||||
@ -75,20 +75,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||
/// <summary>
|
||||
/// Determines if the changes in hit object intervals is consistent based on a given threshold.
|
||||
/// </summary>
|
||||
private static double repeatedIntervalPenalty(SameRhythmHitObjects sameRhythmHitObjects, double hitWindow, double threshold = 0.1)
|
||||
private static double repeatedIntervalPenalty(SameRhythmGroupedHitObjects sameRhythmGroupedHitObjects, double hitWindow, double threshold = 0.1)
|
||||
{
|
||||
double longIntervalPenalty = sameInterval(sameRhythmHitObjects, 3);
|
||||
double longIntervalPenalty = sameInterval(sameRhythmGroupedHitObjects, 3);
|
||||
|
||||
double shortIntervalPenalty = sameRhythmHitObjects.Children.Count < 6
|
||||
? sameInterval(sameRhythmHitObjects, 4)
|
||||
double shortIntervalPenalty = sameRhythmGroupedHitObjects.Children.Count < 6
|
||||
? sameInterval(sameRhythmGroupedHitObjects, 4)
|
||||
: 1.0; // Returns a non-penalty if there are 6 or more notes within an interval.
|
||||
|
||||
// The duration penalty is based on hit object duration relative to hitWindow.
|
||||
double durationPenalty = Math.Max(1 - sameRhythmHitObjects.Duration * 2 / hitWindow, 0.5);
|
||||
double durationPenalty = Math.Max(1 - sameRhythmGroupedHitObjects.Duration * 2 / hitWindow, 0.5);
|
||||
|
||||
return Math.Min(longIntervalPenalty, shortIntervalPenalty) * durationPenalty;
|
||||
|
||||
double sameInterval(SameRhythmHitObjects startObject, int intervalCount)
|
||||
double sameInterval(SameRhythmGroupedHitObjects startObject, int intervalCount)
|
||||
{
|
||||
List<double?> intervals = new List<double?>();
|
||||
var currentObject = startObject;
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
{
|
||||
@ -10,35 +10,26 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
/// A base class for grouping <see cref="IHasInterval"/>s by their interval. In edges where an interval change
|
||||
/// occurs, the <see cref="IHasInterval"/> is added to the group with the smaller interval.
|
||||
/// </summary>
|
||||
public abstract class SameRhythm<ChildType>
|
||||
where ChildType : IHasInterval
|
||||
public abstract class IntervalGroupedHitObjects<TChildType>
|
||||
where TChildType : IHasInterval
|
||||
{
|
||||
public IReadOnlyList<ChildType> Children { get; private set; }
|
||||
public IReadOnlyList<TChildType> Children { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the intervals between two child objects are within a specified margin of error,
|
||||
/// indicating that the intervals are effectively "flat" or consistent.
|
||||
/// </summary>
|
||||
private bool isFlat(ChildType current, ChildType previous, double marginOfError)
|
||||
{
|
||||
return Math.Abs(current.Interval - previous.Interval) <= marginOfError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SameRhythm{ChildType}"/> from a list of <see cref="IHasInterval"/>s, and add
|
||||
/// Create a new <see cref="IntervalGroupedHitObjects{TChildType}"/> from a list of <see cref="IHasInterval"/>s, and add
|
||||
/// them to the <see cref="Children"/> list until the end of the group.
|
||||
/// </summary>
|
||||
/// <param name="data">The list of <see cref="IHasInterval"/>s.</param>
|
||||
/// <param name="i">
|
||||
/// Index in <paramref name="data"/> to start adding children. This will be modified and should be passed into
|
||||
/// the next <see cref="SameRhythm{ChildType}"/>'s constructor.
|
||||
/// the next <see cref="IntervalGroupedHitObjects{TChildType}"/>'s constructor.
|
||||
/// </param>
|
||||
/// <param name="marginOfError">
|
||||
/// The margin of error for the interval, within of which no interval change is considered to have occured.
|
||||
/// </param>
|
||||
protected SameRhythm(List<ChildType> data, ref int i, double marginOfError)
|
||||
protected IntervalGroupedHitObjects(List<TChildType> data, ref int i, double marginOfError)
|
||||
{
|
||||
List<ChildType> children = new List<ChildType>();
|
||||
List<TChildType> children = new List<TChildType>();
|
||||
Children = children;
|
||||
children.Add(data[i]);
|
||||
i++;
|
||||
@ -46,9 +37,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
for (; i < data.Count - 1; i++)
|
||||
{
|
||||
// An interval change occured, add the current data if the next interval is larger.
|
||||
if (!isFlat(data[i], data[i + 1], marginOfError))
|
||||
if (!Precision.AlmostEquals(data[i].Interval, data[i + 1].Interval, marginOfError))
|
||||
{
|
||||
if (data[i + 1].Interval > data[i].Interval + marginOfError)
|
||||
if (Precision.DefinitelyBigger(data[i].Interval, data[i + 1].Interval, marginOfError))
|
||||
{
|
||||
children.Add(data[i]);
|
||||
i++;
|
||||
@ -63,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
|
||||
// Check if the last two objects in the data form a "flat" rhythm pattern within the specified margin of error.
|
||||
// If true, add the current object to the group and increment the index to process the next object.
|
||||
if (data.Count > 2 && isFlat(data[^1], data[^2], marginOfError))
|
||||
if (data.Count > 2 && Precision.AlmostEquals(data[^1].Interval, data[^2].Interval, marginOfError))
|
||||
{
|
||||
children.Add(data[i]);
|
||||
i++;
|
@ -7,21 +7,21 @@ using System.Linq;
|
||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents <see cref="SameRhythmHitObjects"/> grouped by their <see cref="SameRhythmHitObjects.StartTime"/>'s interval.
|
||||
/// Represents <see cref="SameRhythmGroupedHitObjects"/> grouped by their <see cref="SameRhythmGroupedHitObjects.StartTime"/>'s interval.
|
||||
/// </summary>
|
||||
public class SamePatterns : SameRhythm<SameRhythmHitObjects>
|
||||
public class SamePatternsGroupedHitObjects : IntervalGroupedHitObjects<SameRhythmGroupedHitObjects>
|
||||
{
|
||||
public SamePatterns? Previous { get; private set; }
|
||||
public SamePatternsGroupedHitObjects? Previous { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SameRhythmHitObjects.Interval"/> between children <see cref="SameRhythmHitObjects"/> within this group.
|
||||
/// If there is only one child, this will have the value of the first child's <see cref="SameRhythmHitObjects.Interval"/>.
|
||||
/// The <see cref="SameRhythmGroupedHitObjects.Interval"/> between children <see cref="SameRhythmGroupedHitObjects"/> within this group.
|
||||
/// If there is only one child, this will have the value of the first child's <see cref="SameRhythmGroupedHitObjects.Interval"/>.
|
||||
/// </summary>
|
||||
public double ChildrenInterval => Children.Count > 1 ? Children[1].Interval : Children[0].Interval;
|
||||
|
||||
/// <summary>
|
||||
/// The ratio of <see cref="ChildrenInterval"/> between this and the previous <see cref="SamePatterns"/>. In the
|
||||
/// case where there is no previous <see cref="SamePatterns"/>, this will have a value of 1.
|
||||
/// The ratio of <see cref="ChildrenInterval"/> between this and the previous <see cref="SamePatternsGroupedHitObjects"/>. In the
|
||||
/// case where there is no previous <see cref="SamePatternsGroupedHitObjects"/>, this will have a value of 1.
|
||||
/// </summary>
|
||||
public double IntervalRatio => ChildrenInterval / Previous?.ChildrenInterval ?? 1.0d;
|
||||
|
||||
@ -29,26 +29,26 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
|
||||
public IEnumerable<TaikoDifficultyHitObject> AllHitObjects => Children.SelectMany(child => child.Children);
|
||||
|
||||
private SamePatterns(SamePatterns? previous, List<SameRhythmHitObjects> data, ref int i)
|
||||
private SamePatternsGroupedHitObjects(SamePatternsGroupedHitObjects? previous, List<SameRhythmGroupedHitObjects> data, ref int i)
|
||||
: base(data, ref i, 5)
|
||||
{
|
||||
Previous = previous;
|
||||
|
||||
foreach (TaikoDifficultyHitObject hitObject in AllHitObjects)
|
||||
{
|
||||
hitObject.Rhythm.SamePatterns = this;
|
||||
hitObject.Rhythm.SamePatternsGroupedHitObjects = this;
|
||||
}
|
||||
}
|
||||
|
||||
public static void GroupPatterns(List<SameRhythmHitObjects> data)
|
||||
public static void GroupPatterns(List<SameRhythmGroupedHitObjects> data)
|
||||
{
|
||||
List<SamePatterns> samePatterns = new List<SamePatterns>();
|
||||
List<SamePatternsGroupedHitObjects> samePatterns = new List<SamePatternsGroupedHitObjects>();
|
||||
|
||||
// Index does not need to be incremented, as it is handled within the SameRhythm constructor.
|
||||
// Index does not need to be incremented, as it is handled within the IntervalGroupedHitObjects constructor.
|
||||
for (int i = 0; i < data.Count;)
|
||||
{
|
||||
SamePatterns? previous = samePatterns.Count > 0 ? samePatterns[^1] : null;
|
||||
samePatterns.Add(new SamePatterns(previous, data, ref i));
|
||||
SamePatternsGroupedHitObjects? previous = samePatterns.Count > 0 ? samePatterns[^1] : null;
|
||||
samePatterns.Add(new SamePatternsGroupedHitObjects(previous, data, ref i));
|
||||
}
|
||||
}
|
||||
}
|
@ -9,11 +9,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
/// <summary>
|
||||
/// Represents a group of <see cref="TaikoDifficultyHitObject"/>s with no rhythm variation.
|
||||
/// </summary>
|
||||
public class SameRhythmHitObjects : SameRhythm<TaikoDifficultyHitObject>, IHasInterval
|
||||
public class SameRhythmGroupedHitObjects : IntervalGroupedHitObjects<TaikoDifficultyHitObject>, IHasInterval
|
||||
{
|
||||
public TaikoDifficultyHitObject FirstHitObject => Children[0];
|
||||
|
||||
public SameRhythmHitObjects? Previous;
|
||||
public SameRhythmGroupedHitObjects? Previous;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="DifficultyHitObject.StartTime"/> of the first hit object.
|
||||
@ -26,30 +26,28 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
public double Duration => Children[^1].StartTime - Children[0].StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The interval in ms of each hit object in this <see cref="SameRhythmHitObjects"/>. This is only defined if there is
|
||||
/// more than two hit objects in this <see cref="SameRhythmHitObjects"/>.
|
||||
/// The interval in ms of each hit object in this <see cref="SameRhythmGroupedHitObjects"/>. This is only defined if there is
|
||||
/// more than two hit objects in this <see cref="SameRhythmGroupedHitObjects"/>.
|
||||
/// </summary>
|
||||
public double? HitObjectInterval;
|
||||
|
||||
/// <summary>
|
||||
/// The ratio of <see cref="HitObjectInterval"/> between this and the previous <see cref="SameRhythmHitObjects"/>. In the
|
||||
/// The ratio of <see cref="HitObjectInterval"/> between this and the previous <see cref="SameRhythmGroupedHitObjects"/>. In the
|
||||
/// case where one or both of the <see cref="HitObjectInterval"/> is undefined, this will have a value of 1.
|
||||
/// </summary>
|
||||
public double HitObjectIntervalRatio = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The interval between the <see cref="StartTime"/> of this and the previous <see cref="SameRhythmHitObjects"/>.
|
||||
/// </summary>
|
||||
public double Interval { get; private set; } = double.PositiveInfinity;
|
||||
/// <inheritdoc/>
|
||||
public double Interval { get; private set; }
|
||||
|
||||
public SameRhythmHitObjects(SameRhythmHitObjects? previous, List<TaikoDifficultyHitObject> data, ref int i)
|
||||
public SameRhythmGroupedHitObjects(SameRhythmGroupedHitObjects? previous, List<TaikoDifficultyHitObject> data, ref int i)
|
||||
: base(data, ref i, 5)
|
||||
{
|
||||
Previous = previous;
|
||||
|
||||
foreach (var hitObject in Children)
|
||||
{
|
||||
hitObject.Rhythm.SameRhythmHitObjects = this;
|
||||
hitObject.Rhythm.SameRhythmGroupedHitObjects = this;
|
||||
|
||||
// Pass the HitObjectInterval to each child.
|
||||
hitObject.HitObjectInterval = HitObjectInterval;
|
||||
@ -58,15 +56,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm.Data
|
||||
calculateIntervals();
|
||||
}
|
||||
|
||||
public static List<SameRhythmHitObjects> GroupHitObjects(List<TaikoDifficultyHitObject> data)
|
||||
public static List<SameRhythmGroupedHitObjects> GroupHitObjects(List<TaikoDifficultyHitObject> data)
|
||||
{
|
||||
List<SameRhythmHitObjects> flatPatterns = new List<SameRhythmHitObjects>();
|
||||
List<SameRhythmGroupedHitObjects> flatPatterns = new List<SameRhythmGroupedHitObjects>();
|
||||
|
||||
// Index does not need to be incremented, as it is handled within SameRhythm's constructor.
|
||||
// Index does not need to be incremented, as it is handled within IntervalGroupedHitObjects's constructor.
|
||||
for (int i = 0; i < data.Count;)
|
||||
{
|
||||
SameRhythmHitObjects? previous = flatPatterns.Count > 0 ? flatPatterns[^1] : null;
|
||||
flatPatterns.Add(new SameRhythmHitObjects(previous, data, ref i));
|
||||
SameRhythmGroupedHitObjects? previous = flatPatterns.Count > 0 ? flatPatterns[^1] : null;
|
||||
flatPatterns.Add(new SameRhythmGroupedHitObjects(previous, data, ref i));
|
||||
}
|
||||
|
||||
return flatPatterns;
|
@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm
|
||||
/// <summary>
|
||||
/// The group of hit objects with consistent rhythm that this object belongs to.
|
||||
/// </summary>
|
||||
public SameRhythmHitObjects? SameRhythmHitObjects;
|
||||
public SameRhythmGroupedHitObjects? SameRhythmGroupedHitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The larger pattern of rhythm groups that this object is part of.
|
||||
/// </summary>
|
||||
public SamePatterns? SamePatterns;
|
||||
public SamePatternsGroupedHitObjects? SamePatternsGroupedHitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// The ratio of current <see cref="Rulesets.Difficulty.Preprocessing.DifficultyHitObject.DeltaTime"/>
|
||||
|
Loading…
Reference in New Issue
Block a user