Skip to content

Commit

Permalink
Merge pull request #26313 from OliBomby/grids-4
Browse files Browse the repository at this point in the history
Add grid placement tool
  • Loading branch information
peppy authored Oct 8, 2024
2 parents b658d9a + de2f9de commit bfad281
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 52 deletions.
126 changes: 126 additions & 0 deletions osu.Game.Rulesets.Osu/Edit/Blueprints/GridPlacementBlueprint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit;
using osuTK;
using osuTK.Input;

namespace osu.Game.Rulesets.Osu.Edit.Blueprints
{
public partial class GridPlacementBlueprint : PlacementBlueprint
{
[Resolved]
private HitObjectComposer? hitObjectComposer { get; set; }

private OsuGridToolboxGroup gridToolboxGroup = null!;
private Vector2 originalOrigin;
private float originalSpacing;
private float originalRotation;

[BackgroundDependencyLoader]
private void load(OsuGridToolboxGroup gridToolboxGroup)
{
this.gridToolboxGroup = gridToolboxGroup;
originalOrigin = gridToolboxGroup.StartPosition.Value;
originalSpacing = gridToolboxGroup.Spacing.Value;
originalRotation = gridToolboxGroup.GridLinesRotation.Value;
}

public override void EndPlacement(bool commit)
{
if (!commit && PlacementActive != PlacementState.Finished)
{
gridToolboxGroup.StartPosition.Value = originalOrigin;
gridToolboxGroup.Spacing.Value = originalSpacing;
if (!gridToolboxGroup.GridLinesRotation.Disabled)
gridToolboxGroup.GridLinesRotation.Value = originalRotation;
}

base.EndPlacement(commit);

// You typically only place the grid once, so we switch back to the last tool after placement.
if (commit && hitObjectComposer is OsuHitObjectComposer osuHitObjectComposer)
osuHitObjectComposer.SetLastTool();
}

protected override bool OnClick(ClickEvent e)
{
if (e.Button == MouseButton.Left)
{
switch (PlacementActive)
{
case PlacementState.Waiting:
BeginPlacement(true);
return true;

case PlacementState.Active:
EndPlacement(true);
return true;
}
}

return base.OnClick(e);
}

protected override bool OnMouseDown(MouseDownEvent e)
{
if (e.Button == MouseButton.Right)
{
// Reset the grid to the default values.
gridToolboxGroup.StartPosition.Value = gridToolboxGroup.StartPosition.Default;
gridToolboxGroup.Spacing.Value = gridToolboxGroup.Spacing.Default;
if (!gridToolboxGroup.GridLinesRotation.Disabled)
gridToolboxGroup.GridLinesRotation.Value = gridToolboxGroup.GridLinesRotation.Default;
EndPlacement(true);
return true;
}

return base.OnMouseDown(e);
}

protected override bool OnDragStart(DragStartEvent e)
{
if (e.Button == MouseButton.Left)
{
BeginPlacement(true);
return true;
}

return base.OnDragStart(e);
}

protected override void OnDragEnd(DragEndEvent e)
{
if (PlacementActive == PlacementState.Active)
EndPlacement(true);

base.OnDragEnd(e);
}

public override SnapType SnapType => ~SnapType.GlobalGrids;

public override void UpdateTimeAndPosition(SnapResult result)
{
var pos = ToLocalSpace(result.ScreenSpacePosition);

if (PlacementActive != PlacementState.Active)
gridToolboxGroup.StartPosition.Value = pos;
else
{
// Default to the original spacing and rotation if the distance is too small.
if (Vector2.Distance(gridToolboxGroup.StartPosition.Value, pos) < 2)
{
gridToolboxGroup.Spacing.Value = originalSpacing;
if (!gridToolboxGroup.GridLinesRotation.Disabled)
gridToolboxGroup.GridLinesRotation.Value = originalRotation;
}
else
{
gridToolboxGroup.SetGridFromPoints(gridToolboxGroup.StartPosition.Value, pos);
}
}
}
}
}
29 changes: 29 additions & 0 deletions osu.Game.Rulesets.Osu/Edit/GridFromPointsTool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints;

namespace osu.Game.Rulesets.Osu.Edit
{
public partial class GridFromPointsTool : CompositionTool
{
public GridFromPointsTool()
: base("Grid")
{
TooltipText = """
Left click to set the origin.
Left click again to set the spacing and rotation.
Right click to reset to default.
Click and drag to set the origin, spacing and rotation.
""";
}

public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.DraftingCompass };

public override PlacementBlueprint CreatePlacementBlueprint() => new GridPlacementBlueprint();
}
}
70 changes: 31 additions & 39 deletions osu.Game.Rulesets.Osu/Edit/OsuGridToolboxGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.RadioButtons;
Expand All @@ -40,7 +38,6 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.X,
Precision = 1f
};

/// <summary>
Expand All @@ -50,7 +47,6 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
{
MinValue = 0f,
MaxValue = OsuPlayfield.BASE_SIZE.Y,
Precision = 1f
};

/// <summary>
Expand All @@ -60,7 +56,6 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
{
MinValue = 4f,
MaxValue = 128f,
Precision = 1f
};

/// <summary>
Expand All @@ -70,14 +65,13 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
{
MinValue = -180f,
MaxValue = 180f,
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>();
public Bindable<Vector2> StartPosition { get; } = new Bindable<Vector2>(OsuPlayfield.BASE_SIZE / 2);

/// <summary>
/// Read-only bindable representing the grid's spacing in both the X and Y dimension.
Expand All @@ -93,15 +87,33 @@ public partial class OsuGridToolboxGroup : EditorToolboxGroup, IKeyBindingHandle
private ExpandableSlider<float> gridLinesRotationSlider = null!;
private EditorRadioButtonCollection gridTypeButtons = null!;

private ExpandableButton useSelectedObjectPositionButton = null!;

public OsuGridToolboxGroup()
: base("grid")
{
}

private const float max_automatic_spacing = 64;

public void SetGridFromPoints(Vector2 point1, Vector2 point2)
{
StartPositionX.Value = point1.X;
StartPositionY.Value = point1.Y;

// Get the angle between the two points and normalize to the valid range.
if (!GridLinesRotation.Disabled)
{
float period = GridLinesRotation.MaxValue - GridLinesRotation.MinValue;
GridLinesRotation.Value = normalizeRotation(MathHelper.RadiansToDegrees(MathF.Atan2(point2.Y - point1.Y, point2.X - point1.X)), period);
}

// Divide the distance so that there is a good density of grid lines.
// This matches the maximum grid size of the grid size cycling hotkey.
float dist = Vector2.Distance(point1, point2);
while (dist >= max_automatic_spacing)
dist /= 2;
Spacing.Value = dist;
}

[BackgroundDependencyLoader]
private void load()
{
Expand All @@ -117,20 +129,6 @@ private void load()
Current = StartPositionY,
KeyboardStep = 1,
},
useSelectedObjectPositionButton = new ExpandableButton
{
ExpandedLabelText = "Centre on selected object",
Action = () =>
{
if (editorBeatmap.SelectedHitObjects.Count != 1)
return;

var position = ((IHasPosition)editorBeatmap.SelectedHitObjects.Single()).Position;
StartPosition.Value = new Vector2(MathF.Round(position.X), MathF.Round(position.Y));
updateEnabledStates();
},
RelativeSizeAxes = Axes.X,
},
spacingSlider = new ExpandableSlider<float>
{
Current = Spacing,
Expand Down Expand Up @@ -179,29 +177,28 @@ protected override void LoadComplete()

StartPositionX.BindValueChanged(x =>
{
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}";
startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:N0}";
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:#,0.##}";
startPositionXSlider.ExpandedLabelText = $"X Offset: {x.NewValue:#,0.##}";
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}";
startPositionYSlider.ContractedLabelText = $"Y: {y.NewValue:#,0.##}";
startPositionYSlider.ExpandedLabelText = $"Y Offset: {y.NewValue:#,0.##}";
StartPosition.Value = new Vector2(StartPosition.Value.X, y.NewValue);
}, true);

StartPosition.BindValueChanged(pos =>
{
StartPositionX.Value = pos.NewValue.X;
StartPositionY.Value = pos.NewValue.Y;
updateEnabledStates();
});

Spacing.BindValueChanged(spacing =>
{
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:N0}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:N0}";
spacingSlider.ContractedLabelText = $"S: {spacing.NewValue:#,0.##}";
spacingSlider.ExpandedLabelText = $"Spacing: {spacing.NewValue:#,0.##}";
SpacingVector.Value = new Vector2(spacing.NewValue);
editorBeatmap.BeatmapInfo.GridSize = (int)spacing.NewValue;
}, true);
Expand All @@ -219,34 +216,29 @@ protected override void LoadComplete()
switch (v.NewValue)
{
case PositionSnapGridType.Square:
GridLinesRotation.Value = ((GridLinesRotation.Value + 405) % 90) - 45;
GridLinesRotation.Value = normalizeRotation(GridLinesRotation.Value, 90);
GridLinesRotation.MinValue = -45;
GridLinesRotation.MaxValue = 45;
break;

case PositionSnapGridType.Triangle:
GridLinesRotation.Value = ((GridLinesRotation.Value + 390) % 60) - 30;
GridLinesRotation.Value = normalizeRotation(GridLinesRotation.Value, 60);
GridLinesRotation.MinValue = -30;
GridLinesRotation.MaxValue = 30;
break;
}
}, true);

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

private void updateEnabledStates()
private float normalizeRotation(float rotation, float period)
{
useSelectedObjectPositionButton.Enabled.Value = expandingContainer?.Expanded.Value == true
&& editorBeatmap.SelectedHitObjects.Count == 1
&& !Precision.AlmostEquals(StartPosition.Value, ((IHasPosition)editorBeatmap.SelectedHitObjects.Single()).Position, 0.5f);
return ((rotation + 360 + period * 0.5f) % period) - period * 0.5f;
}

private void nextGridSize()
Expand Down
8 changes: 4 additions & 4 deletions osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset r
{
new HitCircleCompositionTool(),
new SliderCompositionTool(),
new SpinnerCompositionTool()
new SpinnerCompositionTool(),
new GridFromPointsTool()
};

private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
Expand Down Expand Up @@ -79,13 +80,12 @@ private void load()
// Give a bit of breathing room around the playfield content.
PlayfieldContentContainer.Padding = new MarginPadding(10);

LayerBelowRuleset.AddRange(new Drawable[]
{
LayerBelowRuleset.Add(
distanceSnapGridContainer = new Container
{
RelativeSizeAxes = Axes.Both
}
});
);

selectedHitObjects = EditorBeatmap.SelectedHitObjects.GetBoundCopy();
selectedHitObjects.CollectionChanged += (_, _) => updateDistanceSnapGrid();
Expand Down
4 changes: 2 additions & 2 deletions osu.Game/Overlays/Settings/SettingsButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ public SettingsButton()
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS };
}

public LocalisableString TooltipText { get; set; }

public IEnumerable<string> Keywords { get; set; } = Array.Empty<string>();

public BindableBool CanBeShown { get; } = new BindableBool(true);
IBindable<bool> IConditionalFilterable.CanBeShown => CanBeShown;

public LocalisableString TooltipText { get; set; }

public override IEnumerable<LocalisableString> FilterTerms
{
get
Expand Down
Loading

0 comments on commit bfad281

Please sign in to comment.