diff --git a/osu.Game.Rulesets.Osu/Statistics/Heatmap.cs b/osu.Game.Rulesets.Osu/Statistics/Heatmap.cs index 89d861a6d1..7e140e6fd2 100644 --- a/osu.Game.Rulesets.Osu/Statistics/Heatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/Heatmap.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Layout; using osu.Game.Rulesets.Osu.Scoring; using osuTK; using osuTK.Graphics; @@ -18,12 +19,7 @@ namespace osu.Game.Rulesets.Osu.Statistics public class Heatmap : CompositeDrawable { /// - /// Full size of the heatmap. - /// - private const float size = 130; - - /// - /// Size of the inner circle containing the "hit" points, relative to . + /// Size of the inner circle containing the "hit" points, relative to the size of this . /// All other points outside of the inner circle are "miss" points. /// private const float inner_portion = 0.8f; @@ -34,77 +30,106 @@ namespace osu.Game.Rulesets.Osu.Statistics private readonly IReadOnlyList offsets; private Container allPoints; + private readonly LayoutValue sizeLayout = new LayoutValue(Invalidation.DrawSize); + public Heatmap(IReadOnlyList offsets) { this.offsets = offsets; - Size = new Vector2(size); + AddLayout(sizeLayout); } [BackgroundDependencyLoader] private void load() { - InternalChildren = new Drawable[] + InternalChild = new Container { - new CircularContainer + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(inner_portion), - Masking = true, - BorderThickness = 2f, - BorderColour = Color4.White, - Child = new Box + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(inner_portion), + Masking = true, + BorderThickness = 2f, + BorderColour = Color4.White, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#202624") + } + }, + new Container { RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("#202624") - } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box + Masking = true, + Children = new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. - Width = 1f, - Rotation = -rotation, - Alpha = 0.3f, - }, - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. - Width = 1f, - Rotation = rotation - }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Width = 10, - Height = 2f, - }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Y = -1, - Width = 2f, - Height = 10, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Height = 2, // We're rotating along a diagonal - we don't really care how big this is. + Width = 1f, + Rotation = -rotation, + Alpha = 0.3f, + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Height = 2, // We're rotating along a diagonal - we don't really care how big this is. + Width = 1f, + Rotation = rotation + }, + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Width = 10, + Height = 2f, + }, + new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Y = -1, + Width = 2f, + Height = 10, + } } + }, + allPoints = new Container + { + RelativeSizeAxes = Axes.Both } - }, - allPoints = new Container { RelativeSizeAxes = Axes.Both } + } }; + } + + protected override void Update() + { + base.Update(); + validateHitPoints(); + } + + private void validateHitPoints() + { + if (sizeLayout.IsValid) + return; + + allPoints.Clear(); + + // Since the content is fit, both dimensions should have the same size. + float size = allPoints.DrawSize.X; Vector2 centre = new Vector2(size / 2); int rows = (int)Math.Ceiling(size / point_size); @@ -130,16 +155,24 @@ namespace osu.Game.Rulesets.Osu.Statistics foreach (var o in offsets) AddPoint(o.Position1, o.Position2, o.HitPosition, o.Radius); + + sizeLayout.Validate(); } - public void AddPoint(Vector2 start, Vector2 end, Vector2 hitPoint, float radius) + protected void AddPoint(Vector2 start, Vector2 end, Vector2 hitPoint, float radius) { + if (allPoints.Count == 0) + return; + double angle1 = Math.Atan2(end.Y - hitPoint.Y, hitPoint.X - end.X); // Angle between the end point and the hit point. double angle2 = Math.Atan2(end.Y - start.Y, start.X - end.X); // Angle between the end point and the start point. double finalAngle = angle2 - angle1; // Angle between start, end, and hit points. float normalisedDistance = Vector2.Distance(hitPoint, end) / radius; + // Since the content is fit, both dimensions should have the same size. + float size = allPoints.DrawSize.X; + // Find the most relevant hit point. double minDist = double.PositiveInfinity; HitPoint point = null; diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyHeatmap.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyHeatmap.cs index a1b2dccea3..53c8e56f53 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyHeatmap.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyHeatmap.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Ranking private readonly Box background; private readonly Drawable object1; private readonly Drawable object2; - private readonly Heatmap heatmap; + private readonly TestHeatmap heatmap; public TestSceneAccuracyHeatmap() { @@ -40,10 +40,11 @@ namespace osu.Game.Tests.Visual.Ranking { Position = new Vector2(500, 300), }, - heatmap = new Heatmap(new List()) + heatmap = new TestHeatmap(new List()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = new Vector2(130) } }; } @@ -70,6 +71,17 @@ namespace osu.Game.Tests.Visual.Ranking return true; } + private class TestHeatmap : Heatmap + { + public TestHeatmap(IReadOnlyList offsets) + : base(offsets) + { + } + + public new void AddPoint(Vector2 start, Vector2 end, Vector2 hitPoint, float radius) + => base.AddPoint(start, end, hitPoint, radius); + } + private class BorderCircle : CircularContainer { public BorderCircle()