1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-18 05:52:56 +08:00
osu-lazer/osu.Game/Screens/Edit/Compose/Components/TriangularPositionSnapGrid.cs
Olivier Schipper 74399542d2
Use math instead of hardcoded constant values
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
2024-06-20 17:27:15 +02:00

90 lines
3.7 KiB
C#

// 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 System;
using osu.Framework.Bindables;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class TriangularPositionSnapGrid : LinedPositionSnapGrid
{
/// <summary>
/// The spacing between grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public BindableFloat Spacing { get; } = new BindableFloat(1f)
{
MinValue = 0f,
};
/// <summary>
/// The rotation in degrees of the grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public BindableFloat GridLineRotation { get; } = new BindableFloat();
public TriangularPositionSnapGrid()
{
Spacing.BindValueChanged(_ => GridCache.Invalidate());
GridLineRotation.BindValueChanged(_ => GridCache.Invalidate());
}
private static readonly float sqrt3 = float.Sqrt(3);
private static readonly float sqrt3_over2 = sqrt3 / 2;
private static readonly float one_over_sqrt3 = 1 / sqrt3;
protected override void CreateContent()
{
var drawSize = DrawSize;
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);
GenerateGridLines(step2, drawSize);
GenerateGridLines(-step2, drawSize);
GenerateGridLines(step3, drawSize);
GenerateGridLines(-step3, drawSize);
GenerateOutline(drawSize);
}
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition.Value, GridLineRotation.Value);
Vector2 hex = pixelToHex(relativeToStart);
return StartPosition.Value + GeometryUtils.RotateVector(hexToPixel(hex), -GridLineRotation.Value);
}
private Vector2 pixelToHex(Vector2 pixel)
{
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>
float t = sqrt3 * y + 1; // scaled y, plus phase
float temp1 = MathF.Floor(t + x); // (y+x) diagonal, this calc needs floor
float temp2 = t - x; // (y-x) diagonal, no floor needed
float temp3 = 2 * x + 1; // scaled horizontal, no floor needed, needs +1 to get correct phase
float qf = (temp1 + temp3) / 3.0f; // pseudo x with fraction
float rf = (temp1 + temp2) / 3.0f; // pseudo y with fraction
float q = MathF.Floor(qf); // pseudo x, quantized and thus requires floor
float r = MathF.Floor(rf); // pseudo y, quantized and thus requires floor
return new Vector2(q, r);
}
private Vector2 hexToPixel(Vector2 hex)
{
// Taken from <https://www.redblobgames.com/grids/hexagons/#hex-to-pixel>
// with modifications for the different definition of size.
return new Vector2(Spacing.Value * (hex.X - hex.Y / 2), Spacing.Value * one_over_sqrt3 * 1.5f * hex.Y);
}
}
}