mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge branch 'master' into scrollalgorithm-timeat
This commit is contained in:
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
var layerAboveRuleset = CreateLayerContainer();
|
||||
layerAboveRuleset.Child = new BlueprintContainer();
|
||||
layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer();
|
||||
|
||||
layerContainers.Add(layerBelowRuleset);
|
||||
layerContainers.Add(layerAboveRuleset);
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// <summary>
|
||||
/// A blueprint placed above a <see cref="DrawableHitObject"/> adding editing functionality.
|
||||
/// </summary>
|
||||
public class SelectionBlueprint : CompositeDrawable, IStateful<SelectionState>
|
||||
public abstract class SelectionBlueprint : CompositeDrawable, IStateful<SelectionState>
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="SelectionBlueprint"/> has been selected.
|
||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="SelectionBlueprint"/> has requested drag.
|
||||
/// </summary>
|
||||
public event Action<SelectionBlueprint, Vector2, InputState> DragRequested;
|
||||
public event Action<DragEvent> DragRequested;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DrawableHitObject"/> which this <see cref="SelectionBlueprint"/> applies to.
|
||||
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
public override bool HandlePositionalInput => ShouldBeAlive;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public SelectionBlueprint(DrawableHitObject hitObject)
|
||||
protected SelectionBlueprint(DrawableHitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
@ -130,10 +130,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
protected override bool OnDrag(DragEvent e)
|
||||
{
|
||||
DragRequested?.Invoke(this, e.Delta, e.CurrentState);
|
||||
DragRequested?.Invoke(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract void AdjustPosition(DragEvent dragEvent);
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space point that causes this <see cref="SelectionBlueprint"/> to be selected.
|
||||
/// </summary>
|
||||
|
@ -1,13 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Types
|
||||
{
|
||||
public interface IHasEditablePosition : IHasPosition
|
||||
{
|
||||
void OffsetPosition(Vector2 offset);
|
||||
}
|
||||
}
|
@ -50,9 +50,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
X = position.X,
|
||||
NewCombo = FirstObject || newCombo,
|
||||
ComboOffset = comboOffset,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
PathType = pathType,
|
||||
Path = new SliderPath(pathType, controlPoints, length),
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -20,11 +19,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <summary>
|
||||
/// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects.
|
||||
/// </summary>
|
||||
public SliderPath Path { get; } = null;
|
||||
public Vector2[] ControlPoints { get; set; }
|
||||
public PathType PathType { get; set; }
|
||||
public SliderPath Path { get; set; }
|
||||
|
||||
public double Distance { get; set; }
|
||||
public double Distance => Path.Distance;
|
||||
|
||||
public List<List<SampleInfo>> NodeSamples { get; set; }
|
||||
public int RepeatCount { get; set; }
|
||||
|
@ -31,9 +31,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
return new ConvertSlider
|
||||
{
|
||||
X = position.X,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
PathType = pathType,
|
||||
Path = new SliderPath(pathType, controlPoints, length),
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
|
@ -51,9 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
Position = position,
|
||||
NewCombo = FirstObject || newCombo,
|
||||
ComboOffset = comboOffset,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = Math.Max(0, length),
|
||||
PathType = pathType,
|
||||
Path = new SliderPath(pathType, controlPoints, Math.Max(0, length)),
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
|
@ -27,9 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
{
|
||||
return new ConvertSlider
|
||||
{
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
PathType = pathType,
|
||||
Path = new SliderPath(pathType, controlPoints, length),
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
|
@ -4,28 +4,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
public class SliderPath
|
||||
public struct SliderPath : IEquatable<SliderPath>
|
||||
{
|
||||
public double Distance;
|
||||
/// <summary>
|
||||
/// The user-set distance of the path. If non-null, <see cref="Distance"/> will match this value,
|
||||
/// and the path will be shortened/lengthened to match this length.
|
||||
/// </summary>
|
||||
public readonly double? ExpectedDistance;
|
||||
|
||||
public Vector2[] ControlPoints = Array.Empty<Vector2>();
|
||||
/// <summary>
|
||||
/// The type of path.
|
||||
/// </summary>
|
||||
public readonly PathType Type;
|
||||
|
||||
public PathType PathType = PathType.PerfectCurve;
|
||||
[JsonProperty]
|
||||
private Vector2[] controlPoints;
|
||||
|
||||
public Vector2 Offset;
|
||||
private List<Vector2> calculatedPath;
|
||||
private List<double> cumulativeLength;
|
||||
|
||||
private readonly List<Vector2> calculatedPath = new List<Vector2>();
|
||||
private readonly List<double> cumulativeLength = new List<double>();
|
||||
private bool isInitialised;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SliderPath"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of path.</param>
|
||||
/// <param name="controlPoints">The control points of the path.</param>
|
||||
/// <param name="expectedDistance">A user-set distance of the path that may be shorter or longer than the true distance between all
|
||||
/// <paramref name="controlPoints"/>. The path will be shortened/lengthened to match this length.
|
||||
/// If null, the path will use the true distance between all <paramref name="controlPoints"/>.</param>
|
||||
[JsonConstructor]
|
||||
public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null)
|
||||
{
|
||||
this = default;
|
||||
this.controlPoints = controlPoints;
|
||||
|
||||
Type = type;
|
||||
ExpectedDistance = expectedDistance;
|
||||
|
||||
ensureInitialised();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The control points of the path.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ReadOnlySpan<Vector2> ControlPoints
|
||||
{
|
||||
get
|
||||
{
|
||||
ensureInitialised();
|
||||
return controlPoints.AsSpan();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The distance of the path after lengthening/shortening to account for <see cref="ExpectedDistance"/>.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double Distance
|
||||
{
|
||||
get
|
||||
{
|
||||
ensureInitialised();
|
||||
return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the slider path until a given progress that ranges from 0 (beginning of the slider)
|
||||
/// to 1 (end of the slider) and stores the generated path in the given list.
|
||||
/// </summary>
|
||||
/// <param name="path">The list to be filled with the computed path.</param>
|
||||
/// <param name="p0">Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
|
||||
/// <param name="p1">End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
|
||||
public void GetPathToProgress(List<Vector2> path, double p0, double p1)
|
||||
{
|
||||
ensureInitialised();
|
||||
|
||||
double d0 = progressToDistance(p0);
|
||||
double d1 = progressToDistance(p1);
|
||||
|
||||
path.Clear();
|
||||
|
||||
int i = 0;
|
||||
for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i)
|
||||
{
|
||||
}
|
||||
|
||||
path.Add(interpolateVertices(i, d0));
|
||||
|
||||
for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i)
|
||||
path.Add(calculatedPath[i]);
|
||||
|
||||
path.Add(interpolateVertices(i, d1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path)
|
||||
/// to 1 (end of the path).
|
||||
/// </summary>
|
||||
/// <param name="progress">Ranges from 0 (beginning of the path) to 1 (end of the path).</param>
|
||||
/// <returns></returns>
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
ensureInitialised();
|
||||
|
||||
double d = progressToDistance(progress);
|
||||
return interpolateVertices(indexOfDistance(d), d);
|
||||
}
|
||||
|
||||
private void ensureInitialised()
|
||||
{
|
||||
if (isInitialised)
|
||||
return;
|
||||
isInitialised = true;
|
||||
|
||||
controlPoints = controlPoints ?? Array.Empty<Vector2>();
|
||||
calculatedPath = new List<Vector2>();
|
||||
cumulativeLength = new List<double>();
|
||||
|
||||
calculatePath();
|
||||
calculateCumulativeLength();
|
||||
}
|
||||
|
||||
private List<Vector2> calculateSubpath(ReadOnlySpan<Vector2> subControlPoints)
|
||||
{
|
||||
switch (PathType)
|
||||
switch (Type)
|
||||
{
|
||||
case PathType.Linear:
|
||||
return PathApproximator.ApproximateLinear(subControlPoints);
|
||||
@ -66,7 +178,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1])
|
||||
{
|
||||
ReadOnlySpan<Vector2> cpSpan = ControlPoints.AsSpan().Slice(start, end - start);
|
||||
ReadOnlySpan<Vector2> cpSpan = ControlPoints.Slice(start, end - start);
|
||||
|
||||
foreach (Vector2 t in calculateSubpath(cpSpan))
|
||||
if (calculatedPath.Count == 0 || calculatedPath.Last() != t)
|
||||
@ -77,49 +189,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateCumulativeLengthAndTrimPath()
|
||||
{
|
||||
double l = 0;
|
||||
|
||||
cumulativeLength.Clear();
|
||||
cumulativeLength.Add(l);
|
||||
|
||||
for (int i = 0; i < calculatedPath.Count - 1; ++i)
|
||||
{
|
||||
Vector2 diff = calculatedPath[i + 1] - calculatedPath[i];
|
||||
double d = diff.Length;
|
||||
|
||||
// Shorten slider paths that are too long compared to what's
|
||||
// in the .osu file.
|
||||
if (Distance - l < d)
|
||||
{
|
||||
calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d);
|
||||
calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i);
|
||||
|
||||
l = Distance;
|
||||
cumulativeLength.Add(l);
|
||||
break;
|
||||
}
|
||||
|
||||
l += d;
|
||||
cumulativeLength.Add(l);
|
||||
}
|
||||
|
||||
// Lengthen slider paths that are too short compared to what's
|
||||
// in the .osu file.
|
||||
if (l < Distance && calculatedPath.Count > 1)
|
||||
{
|
||||
Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2];
|
||||
double d = diff.Length;
|
||||
|
||||
if (d <= 0)
|
||||
return;
|
||||
|
||||
calculatedPath[calculatedPath.Count - 1] += diff * (float)((Distance - l) / d);
|
||||
cumulativeLength[calculatedPath.Count - 1] = Distance;
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateCumulativeLength()
|
||||
{
|
||||
double l = 0;
|
||||
@ -132,21 +201,33 @@ namespace osu.Game.Rulesets.Objects
|
||||
Vector2 diff = calculatedPath[i + 1] - calculatedPath[i];
|
||||
double d = diff.Length;
|
||||
|
||||
// Shorted slider paths that are too long compared to the expected distance
|
||||
if (ExpectedDistance.HasValue && ExpectedDistance - l < d)
|
||||
{
|
||||
calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d);
|
||||
calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i);
|
||||
|
||||
l = ExpectedDistance.Value;
|
||||
cumulativeLength.Add(l);
|
||||
break;
|
||||
}
|
||||
|
||||
l += d;
|
||||
cumulativeLength.Add(l);
|
||||
}
|
||||
|
||||
Distance = l;
|
||||
}
|
||||
// Lengthen slider paths that are too short compared to the expected distance
|
||||
if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1)
|
||||
{
|
||||
Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2];
|
||||
double d = diff.Length;
|
||||
|
||||
public void Calculate(bool updateDistance = false)
|
||||
{
|
||||
calculatePath();
|
||||
if (d <= 0)
|
||||
return;
|
||||
|
||||
if (!updateDistance)
|
||||
calculateCumulativeLengthAndTrimPath();
|
||||
else
|
||||
calculateCumulativeLength();
|
||||
calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d);
|
||||
cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private int indexOfDistance(double d)
|
||||
@ -169,7 +250,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
if (i <= 0)
|
||||
return calculatedPath.First();
|
||||
else if (i >= calculatedPath.Count)
|
||||
if (i >= calculatedPath.Count)
|
||||
return calculatedPath.Last();
|
||||
|
||||
Vector2 p0 = calculatedPath[i - 1];
|
||||
@ -186,47 +267,20 @@ namespace osu.Game.Rulesets.Objects
|
||||
return p0 + (p1 - p0) * (float)w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the slider path until a given progress that ranges from 0 (beginning of the slider)
|
||||
/// to 1 (end of the slider) and stores the generated path in the given list.
|
||||
/// </summary>
|
||||
/// <param name="path">The list to be filled with the computed path.</param>
|
||||
/// <param name="p0">Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
|
||||
/// <param name="p1">End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
|
||||
public void GetPathToProgress(List<Vector2> path, double p0, double p1)
|
||||
public bool Equals(SliderPath other)
|
||||
{
|
||||
if (calculatedPath.Count == 0 && ControlPoints.Length > 0)
|
||||
Calculate();
|
||||
if (ControlPoints == null && other.ControlPoints != null)
|
||||
return false;
|
||||
if (other.ControlPoints == null && ControlPoints != null)
|
||||
return false;
|
||||
|
||||
double d0 = progressToDistance(p0);
|
||||
double d1 = progressToDistance(p1);
|
||||
|
||||
path.Clear();
|
||||
|
||||
int i = 0;
|
||||
for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { }
|
||||
|
||||
path.Add(interpolateVertices(i, d0) + Offset);
|
||||
|
||||
for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i)
|
||||
path.Add(calculatedPath[i] + Offset);
|
||||
|
||||
path.Add(interpolateVertices(i, d1) + Offset);
|
||||
return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path)
|
||||
/// to 1 (end of the path).
|
||||
/// </summary>
|
||||
/// <param name="progress">Ranges from 0 (beginning of the path) to 1 (end of the path).</param>
|
||||
/// <returns></returns>
|
||||
public Vector2 PositionAt(double progress)
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (calculatedPath.Count == 0 && ControlPoints.Length > 0)
|
||||
Calculate();
|
||||
|
||||
double d = progressToDistance(progress);
|
||||
return interpolateVertices(indexOfDistance(d), d) + Offset;
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
return obj is SliderPath other && Equals(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,6 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
/// The curve.
|
||||
/// </summary>
|
||||
SliderPath Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The control points that shape the curve.
|
||||
/// </summary>
|
||||
Vector2[] ControlPoints { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of curve.
|
||||
/// </summary>
|
||||
PathType PathType { get; }
|
||||
}
|
||||
|
||||
public static class HasCurveExtensions
|
||||
|
Reference in New Issue
Block a user