Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grid placement tool #26313

Merged
merged 52 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
3495510
kinda working grid from points
OliBomby Dec 30, 2023
fea0ceb
improve the grid from points
OliBomby Dec 30, 2023
c2ea184
fix period
OliBomby Dec 30, 2023
c952e2f
deselect stuff stuff when grid from points
OliBomby Dec 31, 2023
7a0535a
add right click for abort
OliBomby Dec 31, 2023
85bfd61
improve UI
OliBomby Dec 31, 2023
4e3fe51
merge conflict fix
OliBomby Jan 1, 2024
98505d0
improve grid from points tool code
OliBomby Dec 31, 2023
b54c9a3
move GridFromPointsClicked handler creation code
OliBomby Dec 31, 2023
f8979cf
fix distance
OliBomby Jan 1, 2024
2918ecf
Remove Masking from PositionSnapGrid
OliBomby Feb 1, 2024
93dbd75
Fix masking in circular snap grid
OliBomby Feb 1, 2024
e3aeaf6
Merge remote-tracking branch 'upstream/master' into grids-4
OliBomby Sep 19, 2024
d2f97f5
take into account rotation period for each grid type
OliBomby Sep 19, 2024
1a81e12
Refactor PlacementBlueprint to not be hitobject specific
OliBomby Sep 23, 2024
0a5a463
Convert 'grid from points' button to placement tool
OliBomby Sep 23, 2024
fe10621
Clarify criteria of grid spacing subdivision
OliBomby Sep 23, 2024
0f0f490
Don't snap to global grid while placing grid
OliBomby Sep 23, 2024
b274ed9
fix warnings
OliBomby Sep 23, 2024
b179e08
Merge remote-tracking branch 'upstream/master' into grids-4
OliBomby Sep 26, 2024
4e2bc0d
place grid with drag instead
OliBomby Sep 28, 2024
b1e381a
Update tooltip for drag
OliBomby Sep 28, 2024
1c6e426
return grid placement tool to right toolbox
OliBomby Sep 28, 2024
3e4cd0a
Add tooltip to rounded button
OliBomby Sep 28, 2024
4568af8
Combine drag and clicky interactions
OliBomby Sep 28, 2024
1912b1f
Revert "Add tooltip to rounded button"
OliBomby Sep 28, 2024
4de73dd
Reapply "Add tooltip to rounded button"
OliBomby Oct 1, 2024
9fa2849
Fixed tooltip inheritors of RoundedButton
OliBomby Oct 1, 2024
c84bb4b
Update tooltip
OliBomby Oct 1, 2024
f8397cc
Merge remote-tracking branch 'upstream/master' into grids-4
OliBomby Oct 1, 2024
4959045
Remove 'Centre on selected object' button
OliBomby Oct 1, 2024
4bbefa3
fix using directive
OliBomby Oct 1, 2024
80dffa9
Use new keyword instead of overriding TooltipText to remove setter
OliBomby Oct 3, 2024
2c39ecb
Add xmldoc to SnapType
OliBomby Oct 3, 2024
ad734b1
Revert "Use new keyword instead of overriding TooltipText to remove s…
OliBomby Oct 5, 2024
cc29e8c
introduce tooltip to rounded button with subclass instead
OliBomby Oct 5, 2024
ae8abc7
fix readybutton and favouritebutton
OliBomby Oct 5, 2024
1ab02b9
Merge branch 'master' into grids-4
peppy Oct 7, 2024
75f15cc
Maybe fix compilation? Fuck knows if this is correct.
peppy Oct 7, 2024
0a7d239
fix tooltip in SettingsButton
OliBomby Oct 7, 2024
d7526be
Merge remote-tracking branch 'upstream/master' into grids-4
OliBomby Oct 7, 2024
7b99821
move grid placement tool to left toolbox
OliBomby Oct 7, 2024
a755ae7
fix warning
OliBomby Oct 7, 2024
533ed60
Fix crash on placing circular grid
OliBomby Oct 7, 2024
db10202
Use new place grid icon
OliBomby Oct 7, 2024
076c8de
Revert "Use new place grid icon"
OliBomby Oct 7, 2024
66459c5
Rename to 'Grid'
OliBomby Oct 7, 2024
dc26773
Use FA icon instead
OliBomby Oct 7, 2024
682023e
Merge branch 'master' into grids-4
peppy Oct 8, 2024
b1be31c
Switch back to last tool after using grid tool, rather than always se…
peppy Oct 8, 2024
e794862
update comment
OliBomby Oct 8, 2024
de2f9de
Let right-click reset grid to default values
OliBomby Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <contact@ppy.sh>. 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 <contact@ppy.sh>. 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
Loading