Skip to content

Commit

Permalink
Merge pull request #27768 from 64ArthurAraujo/editor-fix-sliders-same…
Browse files Browse the repository at this point in the history
…-start-time-merge

Fix merging sliders with the same `StartTime` causing a Unhandled Exception
  • Loading branch information
peppy authored Apr 2, 2024
2 parents 51e9348 + 9315aef commit aa7b357
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
131 changes: 131 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,137 @@ public void TestSameStartTimeMerge()
(pos: circle2.Position, pathType: null)));
}

[Test]
public void TestMergeSliderSliderSameStartTime()
{
Slider? slider1 = null;
SliderPath? slider1Path = null;
Slider? slider2 = null;

AddStep("select two sliders", () =>
{
slider1 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
slider1Path = new SliderPath(slider1.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(), slider1.Path.ExpectedDistance.Value);
slider2 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider && h.StartTime > slider1.StartTime);
EditorClock.Seek(slider1.StartTime);
EditorBeatmap.SelectedHitObjects.AddRange([slider1, slider2]);
});

AddStep("move sliders to the same start time", () =>
{
slider2!.StartTime = slider1!.StartTime;
});

mergeSelection();

AddAssert("slider created", () =>
{
if (slider1 is null || slider2 is null || slider1Path is null)
return false;

var controlPoints1 = slider1Path.ControlPoints;
var controlPoints2 = slider2.Path.ControlPoints;
(Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints1.Count + controlPoints2.Count - 1];

for (int i = 0; i < controlPoints1.Count - 1; i++)
{
args[i] = (controlPoints1[i].Position + slider1.Position, controlPoints1[i].Type);
}

for (int i = 0; i < controlPoints2.Count; i++)
{
args[i + controlPoints1.Count - 1] = (controlPoints2[i].Position + controlPoints1[^1].Position + slider1.Position, controlPoints2[i].Type);
}

return sliderCreatedFor(args);
});

AddAssert("samples exist", sliderSampleExist);

AddAssert("merged slider matches first slider", () =>
{
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
&& mergedSlider.Samples.SequenceEqual(slider1.Samples);
});

AddAssert("slider end is at same completion for last slider", () =>
{
if (slider1Path is null || slider2 is null)
return false;

var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
return Precision.AlmostEquals(mergedSlider.Path.Distance, slider1Path.CalculatedDistance + slider2.Path.Distance);
});
}

[Test]
public void TestMergeSliderSliderSameStartAndEndTime()
{
Slider? slider1 = null;
SliderPath? slider1Path = null;
Slider? slider2 = null;

AddStep("select two sliders", () =>
{
slider1 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
slider1Path = new SliderPath(slider1.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(), slider1.Path.ExpectedDistance.Value);
slider2 = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider && h.StartTime > slider1.StartTime);
EditorClock.Seek(slider1.StartTime);
EditorBeatmap.SelectedHitObjects.AddRange([slider1, slider2]);
});

AddStep("move sliders to the same start & end time", () =>
{
slider2!.StartTime = slider1!.StartTime;
slider2.Path = slider1.Path;
});

mergeSelection();

AddAssert("slider created", () =>
{
if (slider1 is null || slider2 is null || slider1Path is null)
return false;

var controlPoints1 = slider1Path.ControlPoints;
var controlPoints2 = slider2.Path.ControlPoints;
(Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints1.Count + controlPoints2.Count - 1];

for (int i = 0; i < controlPoints1.Count - 1; i++)
{
args[i] = (controlPoints1[i].Position + slider1.Position, controlPoints1[i].Type);
}

for (int i = 0; i < controlPoints2.Count; i++)
{
args[i + controlPoints1.Count - 1] = (controlPoints2[i].Position + controlPoints1[^1].Position + slider1.Position, controlPoints2[i].Type);
}

return sliderCreatedFor(args);
});

AddAssert("samples exist", sliderSampleExist);

AddAssert("merged slider matches first slider", () =>
{
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
&& mergedSlider.Samples.SequenceEqual(slider1.Samples);
});

AddAssert("slider end is at same completion for last slider", () =>
{
if (slider1Path is null || slider2 is null)
return false;

var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
return Precision.AlmostEquals(mergedSlider.Path.Distance, slider1Path.CalculatedDistance + slider2.Path.Distance);
});
}

private void mergeSelection()
{
AddStep("merge selection", () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,9 @@ protected virtual void OnBlueprintSelected(SelectionBlueprint<T> blueprint)

protected virtual void OnBlueprintDeselected(SelectionBlueprint<T> blueprint)
{
SelectionBlueprints.ChangeChildDepth(blueprint, 0);
if (SelectionBlueprints.Contains(blueprint))
SelectionBlueprints.ChangeChildDepth(blueprint, 0);

SelectionHandler.HandleDeselected(blueprint);
}

Expand Down

0 comments on commit aa7b357

Please sign in to comment.