1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 16:12:54 +08:00

Added bindables and binding with BindTo

This commit is contained in:
OliBomby 2023-12-30 00:43:41 +01:00
parent d0ca3f2b2b
commit f649fa106f
8 changed files with 92 additions and 112 deletions

View File

@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
}
private void gridSizeIs(int size)
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing == new Vector2(size)
=> AddAssert($"grid size is {size}", () => this.ChildrenOfType<RectangularPositionSnapGrid>().Single().Spacing.Value == new Vector2(size)
&& EditorBeatmap.BeatmapInfo.GridSize == size);
}
}

View File

@ -16,6 +16,7 @@ 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;
namespace osu.Game.Rulesets.Osu.Edit
{
@ -28,6 +29,9 @@ namespace osu.Game.Rulesets.Osu.Edit
[Resolved]
private EditorBeatmap editorBeatmap { get; set; } = null!;
/// <summary>
/// X position of the grid's origin.
/// </summary>
public BindableFloat StartPositionX { get; } = new BindableFloat(OsuPlayfield.BASE_SIZE.X / 2)
{
MinValue = 0f,
@ -35,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.Edit
Precision = 1f
};
/// <summary>
/// Y position of the grid's origin.
/// </summary>
public BindableFloat StartPositionY { get; } = new BindableFloat(OsuPlayfield.BASE_SIZE.Y / 2)
{
MinValue = 0f,
@ -42,6 +49,9 @@ namespace osu.Game.Rulesets.Osu.Edit
Precision = 1f
};
/// <summary>
/// The spacing between grid lines.
/// </summary>
public BindableFloat Spacing { get; } = new BindableFloat(4f)
{
MinValue = 4f,
@ -49,6 +59,9 @@ namespace osu.Game.Rulesets.Osu.Edit
Precision = 1f
};
/// <summary>
/// Rotation of the grid lines in degrees.
/// </summary>
public BindableFloat GridLinesRotation { get; } = new BindableFloat(0f)
{
MinValue = -45f,
@ -56,6 +69,18 @@ namespace osu.Game.Rulesets.Osu.Edit
Precision = 1f
};
/// <summary>
/// Read-only bindable representing the grid's origin.
/// Equivalent to <code>new Vector2(StartPositionX, StartPositionY)</code>
/// </summary>
public Bindable<Vector2> StartPosition { get; } = new Bindable<Vector2>();
/// <summary>
/// Read-only bindable representing the grid's spacing in both the X and Y dimension.
/// Equivalent to <code>new Vector2(Spacing)</code>
/// </summary>
public Bindable<Vector2> SpacingVector { get; } = new Bindable<Vector2>();
public Bindable<PositionSnapGridType> GridType { get; } = new Bindable<PositionSnapGridType>();
private ExpandableSlider<float> startPositionXSlider = null!;
@ -124,18 +149,21 @@ namespace osu.Game.Rulesets.Osu.Edit
{
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}";
startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:N0}";
StartPosition.Value = new Vector2(x.NewValue, StartPosition.Value.Y);
}, true);
StartPositionY.BindValueChanged(y =>
{
startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:N0}";
startPositionYSlider.ExpandedLabelText = $"Y Offset: {y.NewValue:N0}";
StartPosition.Value = new Vector2(StartPosition.Value.X, y.NewValue);
}, true);
Spacing.BindValueChanged(spacing =>
{
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:N0}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:N0}";
SpacingVector.Value = new Vector2(spacing.NewValue);
}, true);
GridLinesRotation.BindValueChanged(rotation =>

View File

@ -120,8 +120,8 @@ namespace osu.Game.Rulesets.Osu.Edit
case PositionSnapGridType.Square:
var rectangularPositionSnapGrid = new RectangularPositionSnapGrid();
OsuGridToolboxGroup.Spacing.BindValueChanged(s => rectangularPositionSnapGrid.Spacing = new Vector2(s.NewValue), true);
OsuGridToolboxGroup.GridLinesRotation.BindValueChanged(r => rectangularPositionSnapGrid.GridLineRotation = r.NewValue, true);
rectangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.SpacingVector);
rectangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
positionSnapGrid = rectangularPositionSnapGrid;
break;
@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Osu.Edit
case PositionSnapGridType.Triangle:
var triangularPositionSnapGrid = new TriangularPositionSnapGrid();
OsuGridToolboxGroup.Spacing.BindValueChanged(s => triangularPositionSnapGrid.Spacing = s.NewValue, true);
OsuGridToolboxGroup.GridLinesRotation.BindValueChanged(r => triangularPositionSnapGrid.GridLineRotation = r.NewValue, true);
triangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);
triangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
positionSnapGrid = triangularPositionSnapGrid;
break;
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit
case PositionSnapGridType.Circle:
var circularPositionSnapGrid = new CircularPositionSnapGrid();
OsuGridToolboxGroup.Spacing.BindValueChanged(s => circularPositionSnapGrid.Spacing = s.NewValue, true);
circularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);
positionSnapGrid = circularPositionSnapGrid;
break;
@ -147,18 +147,11 @@ namespace osu.Game.Rulesets.Osu.Edit
throw new NotImplementedException($"{OsuGridToolboxGroup.GridType} has an incorrect value.");
}
bindPositionSnapGridStartPosition(positionSnapGrid);
// Bind the start position to the toolbox sliders.
positionSnapGrid.StartPosition.BindTo(OsuGridToolboxGroup.StartPosition);
positionSnapGrid.RelativeSizeAxes = Axes.Both;
LayerBelowRuleset.Add(positionSnapGrid);
return;
void bindPositionSnapGridStartPosition(PositionSnapGrid snapGrid)
{
OsuGridToolboxGroup.StartPositionX.BindValueChanged(x =>
snapGrid.StartPosition = new Vector2(x.NewValue, snapGrid.StartPosition.Y), true);
OsuGridToolboxGroup.StartPositionY.BindValueChanged(y =>
snapGrid.StartPosition = new Vector2(snapGrid.StartPosition.X, y.NewValue), true);
}
}
protected override ComposeBlueprintContainer CreateBlueprintContainer()

View File

@ -4,33 +4,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class CircularPositionSnapGrid : PositionSnapGrid
{
private float spacing = 1;
/// <summary>
/// The spacing between grid lines of this <see cref="CircularPositionSnapGrid"/>.
/// </summary>
public float Spacing
public BindableFloat Spacing { get; } = new BindableFloat(1f)
{
get => spacing;
set
{
if (spacing <= 0)
throw new ArgumentException("Grid spacing must be positive.");
MinValue = 0f,
};
spacing = value;
GridCache.Invalidate();
}
public CircularPositionSnapGrid()
{
Spacing.BindValueChanged(_ => GridCache.Invalidate());
}
protected override void CreateContent()
@ -39,11 +34,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
// Calculate the maximum distance from the origin to the edge of the grid.
float maxDist = MathF.Max(
MathF.Max(StartPosition.Length, (StartPosition - drawSize).Length),
MathF.Max((StartPosition - new Vector2(drawSize.X, 0)).Length, (StartPosition - new Vector2(0, drawSize.Y)).Length)
MathF.Max(StartPosition.Value.Length, (StartPosition.Value - drawSize).Length),
MathF.Max((StartPosition.Value - new Vector2(drawSize.X, 0)).Length, (StartPosition.Value - new Vector2(0, drawSize.Y)).Length)
);
generateCircles((int)(maxDist / Spacing) + 1);
generateCircles((int)(maxDist / Spacing.Value) + 1);
GenerateOutline(drawSize);
}
@ -58,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
for (int i = 0; i < count; i++)
{
// Add a minimum diameter so the center circle is clearly visible.
float diameter = MathF.Max(lineWidth * 1.5f, i * Spacing * 2);
float diameter = MathF.Max(lineWidth * 1.5f, i * Spacing.Value * 2);
var gridCircle = new CircularContainer
{
@ -69,7 +64,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
RelativeSizeAxes = Axes.None,
Width = diameter,
Height = diameter,
Position = StartPosition,
Position = StartPosition.Value,
Masking = true,
Child = new Box
{
@ -92,15 +87,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = original - StartPosition;
Vector2 relativeToStart = original - StartPosition.Value;
if (relativeToStart.LengthSquared < Precision.FLOAT_EPSILON)
return StartPosition;
return StartPosition.Value;
float length = relativeToStart.Length;
float wantedLength = MathF.Round(length / Spacing) * Spacing;
float wantedLength = MathF.Round(length / Spacing.Value) * Spacing.Value;
return StartPosition + Vector2.Multiply(relativeToStart, wantedLength / length);
return StartPosition.Value + Vector2.Multiply(relativeToStart, wantedLength / length);
}
}
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected void GenerateGridLines(Vector2 step, Vector2 drawSize)
{
int index = 0;
var currentPosition = StartPosition;
var currentPosition = StartPosition.Value;
// Make lines the same width independent of display resolution.
float lineWidth = DrawWidth / ScreenSpaceDrawQuad.Width;
@ -42,7 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
generatedLines.Add(gridLine);
index += 1;
currentPosition = StartPosition + index * step;
currentPosition = StartPosition.Value + index * step;
}
if (generatedLines.Count == 0)

View File

@ -1,6 +1,7 @@
// 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 osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -11,20 +12,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
public abstract partial class PositionSnapGrid : CompositeDrawable
{
private Vector2 startPosition;
/// <summary>
/// The position of the origin of this <see cref="PositionSnapGrid"/> in local coordinates.
/// </summary>
public Vector2 StartPosition
{
get => startPosition;
set
{
startPosition = value;
GridCache.Invalidate();
}
}
public Bindable<Vector2> StartPosition { get; } = new Bindable<Vector2>(Vector2.Zero);
protected readonly LayoutValue GridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
@ -32,6 +23,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
Masking = true;
StartPosition.BindValueChanged(_ => GridCache.Invalidate());
AddLayout(GridCache);
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Bindables;
using osu.Game.Utils;
using osuTK;
@ -9,60 +10,43 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class RectangularPositionSnapGrid : LinedPositionSnapGrid
{
private Vector2 spacing = Vector2.One;
/// <summary>
/// The spacing between grid lines of this <see cref="RectangularPositionSnapGrid"/>.
/// </summary>
public Vector2 Spacing
{
get => spacing;
set
{
if (spacing.X <= 0 || spacing.Y <= 0)
throw new ArgumentException("Grid spacing must be positive.");
spacing = value;
GridCache.Invalidate();
}
}
private float gridLineRotation;
public Bindable<Vector2> Spacing { get; } = new Bindable<Vector2>(Vector2.One);
/// <summary>
/// The rotation in degrees of the grid lines of this <see cref="RectangularPositionSnapGrid"/>.
/// </summary>
public float GridLineRotation
public BindableFloat GridLineRotation { get; } = new BindableFloat();
public RectangularPositionSnapGrid()
{
get => gridLineRotation;
set
{
gridLineRotation = value;
GridCache.Invalidate();
}
Spacing.BindValueChanged(_ => GridCache.Invalidate());
GridLineRotation.BindValueChanged(_ => GridCache.Invalidate());
}
protected override void CreateContent()
{
var drawSize = DrawSize;
var rot = Quaternion.FromAxisAngle(Vector3.UnitZ, MathHelper.DegreesToRadians(GridLineRotation));
var rot = Quaternion.FromAxisAngle(Vector3.UnitZ, MathHelper.DegreesToRadians(GridLineRotation.Value));
GenerateGridLines(Vector2.Transform(new Vector2(0, -Spacing.Y), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(0, Spacing.Y), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(0, -Spacing.Value.Y), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(0, Spacing.Value.Y), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(-Spacing.X, 0), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(Spacing.X, 0), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(-Spacing.Value.X, 0), rot), drawSize);
GenerateGridLines(Vector2.Transform(new Vector2(Spacing.Value.X, 0), rot), drawSize);
GenerateOutline(drawSize);
}
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition, GridLineRotation);
Vector2 offset = Vector2.Divide(relativeToStart, Spacing);
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition.Value, GridLineRotation.Value);
Vector2 offset = Vector2.Divide(relativeToStart, Spacing.Value);
Vector2 roundedOffset = new Vector2(MathF.Round(offset.X), MathF.Round(offset.Y));
return StartPosition + GeometryUtils.RotateVector(Vector2.Multiply(roundedOffset, Spacing), -GridLineRotation);
return StartPosition.Value + GeometryUtils.RotateVector(Vector2.Multiply(roundedOffset, Spacing.Value), -GridLineRotation.Value);
}
}
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Bindables;
using osu.Game.Utils;
using osuTK;
@ -9,37 +10,23 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class TriangularPositionSnapGrid : LinedPositionSnapGrid
{
private float spacing = 1;
/// <summary>
/// The spacing between grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public float Spacing
public BindableFloat Spacing { get; } = new BindableFloat(1f)
{
get => spacing;
set
{
if (spacing <= 0)
throw new ArgumentException("Grid spacing must be positive.");
spacing = value;
GridCache.Invalidate();
}
}
private float gridLineRotation;
MinValue = 0f,
};
/// <summary>
/// The rotation in degrees of the grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public float GridLineRotation
public BindableFloat GridLineRotation { get; } = new BindableFloat();
public TriangularPositionSnapGrid()
{
get => gridLineRotation;
set
{
gridLineRotation = value;
GridCache.Invalidate();
}
Spacing.BindValueChanged(_ => GridCache.Invalidate());
GridLineRotation.BindValueChanged(_ => GridCache.Invalidate());
}
private const float sqrt3 = 1.73205080757f;
@ -49,10 +36,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected override void CreateContent()
{
var drawSize = DrawSize;
float stepSpacing = Spacing * sqrt3_over2;
var step1 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation - 30);
var step2 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation - 90);
var step3 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation - 150);
float stepSpacing = Spacing.Value * sqrt3_over2;
var step1 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 30);
var step2 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 90);
var step3 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 150);
GenerateGridLines(step1, drawSize);
GenerateGridLines(-step1, drawSize);
@ -68,16 +55,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition, GridLineRotation);
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition.Value, GridLineRotation.Value);
Vector2 hex = pixelToHex(relativeToStart);
return StartPosition + GeometryUtils.RotateVector(hexToPixel(hex), -GridLineRotation);
return StartPosition.Value + GeometryUtils.RotateVector(hexToPixel(hex), -GridLineRotation.Value);
}
private Vector2 pixelToHex(Vector2 pixel)
{
float x = pixel.X / Spacing;
float y = pixel.Y / Spacing;
float x = pixel.X / Spacing.Value;
float y = pixel.Y / Spacing.Value;
// Algorithm from Charles Chambers
// with modifications and comments by Chris Cox 2023
// <https://gitlab.com/chriscox/hex-coordinates>
@ -96,7 +83,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
// Taken from <https://www.redblobgames.com/grids/hexagons/#hex-to-pixel>
// with modifications for the different definition of size.
return new Vector2(Spacing * (hex.X - hex.Y / 2), Spacing * one_over_sqrt3 * 1.5f * hex.Y);
return new Vector2(Spacing.Value * (hex.X - hex.Y / 2), Spacing.Value * one_over_sqrt3 * 1.5f * hex.Y);
}
}
}