Skip to content

Commit

Permalink
Merge pull request #26310 from OliBomby/grids-2
Browse files Browse the repository at this point in the history
Add hexgrid and circular grid to the osu editor
  • Loading branch information
bdach authored Jul 3, 2024
2 parents 505b854 + 17ce9cd commit 6313631
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 10 deletions.
3 changes: 3 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorGrids.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
Expand Down Expand Up @@ -160,6 +161,8 @@ private Vector2 uniqueSnappingPosition(PositionSnapGrid grid)
return grid switch
{
RectangularPositionSnapGrid rectangular => rectangular.StartPosition.Value + GeometryUtils.RotateVector(rectangular.Spacing.Value, -rectangular.GridLineRotation.Value),
TriangularPositionSnapGrid triangular => triangular.StartPosition.Value + GeometryUtils.RotateVector(new Vector2(triangular.Spacing.Value / 2, triangular.Spacing.Value / 2 * MathF.Sqrt(3)), -triangular.GridLineRotation.Value),
CircularPositionSnapGrid circular => circular.StartPosition.Value + GeometryUtils.RotateVector(new Vector2(circular.Spacing.Value, 0), -45),
_ => Vector2.Zero
};
}
Expand Down
107 changes: 105 additions & 2 deletions osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.RadioButtons;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Rulesets.Osu.Edit
{
Expand All @@ -20,6 +27,9 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
[Resolved]
private EditorBeatmap editorBeatmap { get; set; } = null!;

[Resolved]
private IExpandingContainer? expandingContainer { get; set; }

/// <summary>
/// X position of the grid's origin.
/// </summary>
Expand Down Expand Up @@ -55,8 +65,8 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
/// </summary>
public BindableFloat GridLinesRotation { get; } = new BindableFloat(0f)
{
MinValue = -45f,
MaxValue = 45f,
MinValue = -180f,
MaxValue = 180f,
Precision = 1f
};

Expand All @@ -72,10 +82,13 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
/// </summary>
public Bindable<Vector2> SpacingVector { get; } = new Bindable<Vector2>();

public Bindable<PositionSnapGridType> GridType { get; } = new Bindable<PositionSnapGridType>();

private ExpandableSlider<float> startPositionXSlider = null!;
private ExpandableSlider<float> startPositionYSlider = null!;
private ExpandableSlider<float> spacingSlider = null!;
private ExpandableSlider<float> gridLinesRotationSlider = null!;
private EditorRadioButtonCollection gridTypeButtons = null!;

public OsuGridToolboxGroup()
: base("grid")
Expand Down Expand Up @@ -109,6 +122,31 @@ private void load()
Current = GridLinesRotation,
KeyboardStep = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
gridTypeButtons = new EditorRadioButtonCollection
{
RelativeSizeAxes = Axes.X,
Items = new[]
{
new RadioButton("Square",
() => GridType.Value = PositionSnapGridType.Square,
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
new RadioButton("Triangle",
() => GridType.Value = PositionSnapGridType.Triangle,
() => new OutlineTriangle(true, 20)),
new RadioButton("Circle",
() => GridType.Value = PositionSnapGridType.Circle,
() => new SpriteIcon { Icon = FontAwesome.Regular.Circle }),
}
},
}
},
};

Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
Expand All @@ -118,6 +156,8 @@ protected override void LoadComplete()
{
base.LoadComplete();

gridTypeButtons.Items.First().Select();

StartPositionX.BindValueChanged(x =>
{
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}";
Expand Down Expand Up @@ -145,6 +185,32 @@ protected override void LoadComplete()
gridLinesRotationSlider.ContractedLabelText = $"R: {rotation.NewValue:#,0.##}";
gridLinesRotationSlider.ExpandedLabelText = $"Rotation: {rotation.NewValue:#,0.##}";
}, true);

expandingContainer?.Expanded.BindValueChanged(v =>
{
gridTypeButtons.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
gridTypeButtons.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);

GridType.BindValueChanged(v =>
{
GridLinesRotation.Disabled = v.NewValue == PositionSnapGridType.Circle;
switch (v.NewValue)
{
case PositionSnapGridType.Square:
GridLinesRotation.Value = ((GridLinesRotation.Value + 405) % 90) - 45;
GridLinesRotation.MinValue = -45;
GridLinesRotation.MaxValue = 45;
break;
case PositionSnapGridType.Triangle:
GridLinesRotation.Value = ((GridLinesRotation.Value + 390) % 60) - 30;
GridLinesRotation.MinValue = -30;
GridLinesRotation.MaxValue = 30;
break;
}
}, true);
}

private void nextGridSize()
Expand All @@ -167,5 +233,42 @@ public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}

public partial class OutlineTriangle : BufferedContainer
{
public OutlineTriangle(bool outlineOnly, float size)
: base(cachedFrameBuffer: true)
{
Size = new Vector2(size);

InternalChildren = new Drawable[]
{
new EquilateralTriangle { RelativeSizeAxes = Axes.Both },
};

if (outlineOnly)
{
AddInternal(new EquilateralTriangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Y = 0.48f,
Colour = Color4.Black,
Size = new Vector2(size - 7),
Blending = BlendingParameters.None,
});
}

Blending = BlendingParameters.Additive;
}
}
}

public enum PositionSnapGridType
{
Square,
Triangle,
Circle,
}
}
41 changes: 34 additions & 7 deletions osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private void load()
// we may be entering the screen with a selection already active
updateDistanceSnapGrid();

updatePositionSnapGrid();
OsuGridToolboxGroup.GridType.BindValueChanged(updatePositionSnapGrid, true);

RightToolbox.AddRange(new Drawable[]
{
Expand All @@ -115,18 +115,45 @@ private void load()
);
}

private void updatePositionSnapGrid()
private void updatePositionSnapGrid(ValueChangedEvent<PositionSnapGridType> obj)
{
if (positionSnapGrid != null)
LayerBelowRuleset.Remove(positionSnapGrid, true);

var rectangularPositionSnapGrid = new RectangularPositionSnapGrid();
switch (obj.NewValue)
{
case PositionSnapGridType.Square:
var rectangularPositionSnapGrid = new RectangularPositionSnapGrid();

rectangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.SpacingVector);
rectangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);

positionSnapGrid = rectangularPositionSnapGrid;
break;

case PositionSnapGridType.Triangle:
var triangularPositionSnapGrid = new TriangularPositionSnapGrid();

triangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);
triangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);

positionSnapGrid = triangularPositionSnapGrid;
break;

case PositionSnapGridType.Circle:
var circularPositionSnapGrid = new CircularPositionSnapGrid();

rectangularPositionSnapGrid.StartPosition.BindTo(OsuGridToolboxGroup.StartPosition);
rectangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.SpacingVector);
rectangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
circularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);

positionSnapGrid = circularPositionSnapGrid;
break;

default:
throw new ArgumentOutOfRangeException(nameof(OsuGridToolboxGroup.GridType), OsuGridToolboxGroup.GridType, "Unsupported grid type.");
}

positionSnapGrid = rectangularPositionSnapGrid;
// Bind the start position to the toolbox sliders.
positionSnapGrid.StartPosition.BindTo(OsuGridToolboxGroup.StartPosition);

positionSnapGrid.RelativeSizeAxes = Axes.Both;
LayerBelowRuleset.Add(positionSnapGrid);
Expand Down
45 changes: 45 additions & 0 deletions osu.Game.Tests/Visual/Editing/TestScenePositionSnapGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,51 @@ public void TestRectangularGrid(Vector2 position, Vector2 spacing, float rotatio
}));
}

[TestCaseSource(nameof(test_cases))]
public void TestTriangularGrid(Vector2 position, Vector2 spacing, float rotation)
{
TriangularPositionSnapGrid grid = null;

AddStep("create grid", () =>
{
Child = grid = new TriangularPositionSnapGrid
{
RelativeSizeAxes = Axes.Both,
};
grid.StartPosition.Value = position;
grid.Spacing.Value = spacing.X;
grid.GridLineRotation.Value = rotation;
});

AddStep("add snapping cursor", () => Add(new SnappingCursorContainer
{
RelativeSizeAxes = Axes.Both,
GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos))
}));
}

[TestCaseSource(nameof(test_cases))]
public void TestCircularGrid(Vector2 position, Vector2 spacing, float rotation)
{
CircularPositionSnapGrid grid = null;

AddStep("create grid", () =>
{
Child = grid = new CircularPositionSnapGrid
{
RelativeSizeAxes = Axes.Both,
};
grid.StartPosition.Value = position;
grid.Spacing.Value = spacing.X;
});

AddStep("add snapping cursor", () => Add(new SnappingCursorContainer
{
RelativeSizeAxes = Axes.Both,
GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos))
}));
}

private partial class SnappingCursorContainer : CompositeDrawable
{
public Func<Vector2, Vector2> GetSnapPosition;
Expand Down
7 changes: 6 additions & 1 deletion osu.Game/Graphics/UserInterface/ExpandableSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,14 @@ protected override void LoadComplete()
Expanded.BindValueChanged(v =>
{
label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
slider.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
slider.FadeTo(v.NewValue ? Current.Disabled ? 0.3f : 1f : 0f, 500, Easing.OutQuint);
slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);

Current.BindDisabledChanged(disabled =>
{
slider.Alpha = Expanded.Value ? disabled ? 0.3f : 1 : 0f;
});
}
}

Expand Down
Loading

0 comments on commit 6313631

Please sign in to comment.