From c84777d7bfb60d305d029db481bfa00151d65d3f Mon Sep 17 00:00:00 2001 From: IceDynamix Date: Mon, 11 May 2026 11:31:26 +0200 Subject: [PATCH] Improve ranked play rating graph's x-axis divisions (#37534) the x axis division was set to a fixed number of steps rather than picking a neat step size, which created uneven numbers. this pr changes this so an appropriate factor is determined, the x-axis min/max is floor'd/ceil'd to that factor and the divisions are created based on that factor. the cumulative rating line also extends to the new end of the graph. in addition, the bars of the bar chart are now aligned using the left edge of the bar rather than the center. in the after-image, note how the left edge of the bar for 1600 rating aligns with the division. (this is based on the assumption that a rating bucket, say "1500", spans the interval [1500, 1600]. if the bucket spans [1450, 1550] instead, i will revert the change) before image after image could optimize the while loop into a single mathematical expression, but this is easier to read imo. lmk if you'd prefer the expression instead --- .../Queue/RatingDistributionGraph.cs | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/RatingDistributionGraph.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/RatingDistributionGraph.cs index 1f6107141b..8a03e7b524 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/RatingDistributionGraph.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/Queue/RatingDistributionGraph.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue public partial class RatingDistributionGraph : CompositeDrawable, IHasCustomTooltip { private const int y_divisions = 4; - private const int x_divisions = 16; + private const int x_max_divisions = 16; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -50,6 +50,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue private int? userRating; private (int min, int max, int step) xRange; private (int min, int max) yRange; + private int xDivisionStep = 1; [BackgroundDependencyLoader] private void load() @@ -237,6 +238,17 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue data.Zip(data.Skip(1), (a, b) => Math.Abs(b.x - a.x)).DefaultIfEmpty().Min() ); + xDivisionStep = Math.Max(xRange.step, 50); // avoid division by zero + + // Keep increasing the division until number of lines is appropriately low + while ((xRange.max - xRange.min) / xDivisionStep > x_max_divisions) + { + xDivisionStep *= 2; + } + + xRange.min = (int)floorWithFactor(xRange.min, xDivisionStep); + xRange.max = (int)ceilingWithFactor(xRange.max, xDivisionStep); + if (userRating < xRange.min) { this.data = this.data.Prepend((userRating.Value, 1)).ToArray(); @@ -249,9 +261,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue xRange.max = userRating.Value; } + int yMax = this.data.Select(d => d.y).DefaultIfEmpty().Max(); + yRange = ( 0, - (int)roundToSignificant(this.data.Select(d => d.y).DefaultIfEmpty().Max()) + (int)ceilingWithFactor(yMax, significantOfNumber(yMax)) ); updateGraph(); @@ -274,13 +288,15 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue barsContainer.Clear(); userRatingContainer.Clear(); - for (int step = 0; step <= x_divisions; step++) + int xDivisions = Math.Max(1, (xRange.max - xRange.min) / xDivisionStep); + + for (int step = 0; step <= xDivisions; step++) { gridContainer.Add(new VerticalLine { RelativeSizeAxes = Axes.Y, RelativePositionAxes = Axes.X, - X = (float)step / x_divisions, + X = (float)step / xDivisions, Colour = colourProvider.Background1 }); @@ -289,10 +305,10 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue Anchor = Anchor.TopLeft, Origin = Anchor.CentreRight, RelativePositionAxes = Axes.X, - X = (float)step / x_divisions, + X = (float)step / xDivisions, Margin = new MarginPadding { Right = -2 }, Rotation = -40, - Text = (xRange.min + (xRange.max - xRange.min) / x_divisions * step).ToString(), + Text = (xRange.min + xDivisionStep * step).ToString(), UseFullGlyphHeight = false, Font = OsuFont.Default.With(size: 12), Colour = colourProvider.Foreground1 @@ -337,7 +353,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue { barsContainer.Add(new Container { - Origin = Anchor.BottomCentre, + Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, RelativePositionAxes = Axes.X, RelativeSizeAxes = Axes.Both, @@ -393,7 +409,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue currentCount += d.y; float p = (float)currentCount / totalCount; return new Vector2(pointOnGraph(d.x, 0).X, 1 - p); - }).ToArray(); + }).Append(new Vector2(1, 0)).ToArray(); }); private Vector2 pointOnGraph(int x, int y) @@ -403,13 +419,25 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue return new Vector2(xPos, yPos); } - private static double roundToSignificant(double value) + private static double significantOfNumber(double value) { - if (value == 0) + return Math.Pow(10, Math.Floor(Math.Log10(value))); + } + + private static double floorWithFactor(double value, double factor) + { + if (value == 0 || factor == 0) return 0; - double scale = Math.Pow(10, Math.Floor(Math.Log10(value))); - return Math.Ceiling(value / scale) * scale; + return Math.Floor(value / factor) * factor; + } + + private static double ceilingWithFactor(double value, double factor) + { + if (value == 0 || factor == 0) + return 0; + + return Math.Ceiling(value / factor) * factor; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) @@ -474,7 +502,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Queue int currentCount = 0; int totalCount = data.Sum(p => p.y); - for (int i = 0; i < cumulativePath.Vertices.Count; i++) + for (int i = 0; i < data.Length; i++) { currentCount += data[i].y;