// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; 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; /// /// The spacing between grid lines of this . /// public float Spacing { get => spacing; set { if (spacing <= 0) throw new ArgumentException("Grid spacing must be positive."); spacing = value; GridCache.Invalidate(); } } protected override void CreateContent() { var drawSize = DrawSize; // 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) ); generateCircles((int)(maxDist / Spacing) + 1); GenerateOutline(drawSize); } private void generateCircles(int count) { // Make lines the same width independent of display resolution. float lineWidth = 2 * DrawWidth / ScreenSpaceDrawQuad.Width; List generatedCircles = new List(); 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); var gridCircle = new CircularContainer { BorderColour = Colour4.White, BorderThickness = lineWidth, Alpha = 0.2f, Origin = Anchor.Centre, RelativeSizeAxes = Axes.None, Width = diameter, Height = diameter, Position = StartPosition, Masking = true, Child = new Box { RelativeSizeAxes = Axes.Both, AlwaysPresent = true, Alpha = 0f, } }; generatedCircles.Add(gridCircle); } if (generatedCircles.Count == 0) return; generatedCircles.First().Alpha = 0.8f; AddRangeInternal(generatedCircles); } public override Vector2 GetSnappedPosition(Vector2 original) { Vector2 relativeToStart = original - StartPosition; if (relativeToStart.LengthSquared < Precision.FLOAT_EPSILON) return StartPosition; float length = relativeToStart.Length; float wantedLength = MathF.Round(length / Spacing) * Spacing; return StartPosition + Vector2.Multiply(relativeToStart, wantedLength / length); } } }