1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 09:07:25 +08:00

Rework BarGraph to use Quads

This commit is contained in:
Andrei Zavatski 2022-11-19 00:19:49 +03:00
parent e4fc14faee
commit e8ca9f5dc5
2 changed files with 188 additions and 35 deletions

View File

@ -109,15 +109,11 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
[Flags]
public enum BarDirection public enum BarDirection
{ {
LeftToRight = 1, LeftToRight,
RightToLeft = 1 << 1, RightToLeft,
TopToBottom = 1 << 2, TopToBottom,
BottomToTop = 1 << 3, BottomToTop
Vertical = TopToBottom | BottomToTop,
Horizontal = LeftToRight | RightToLeft,
} }
} }

View File

@ -5,15 +5,23 @@
using osuTK; using osuTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Utils;
using System;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
public class BarGraph : FillFlowContainer<Bar> public class BarGraph : Drawable
{ {
private const int resize_duration = 250;
private const Easing easing = Easing.InOutCubic;
/// <summary> /// <summary>
/// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used /// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used
/// </summary> /// </summary>
@ -21,22 +29,21 @@ namespace osu.Game.Graphics.UserInterface
private BarDirection direction = BarDirection.BottomToTop; private BarDirection direction = BarDirection.BottomToTop;
public new BarDirection Direction public BarDirection Direction
{ {
get => direction; get => direction;
set set
{ {
direction = value; if (direction == value)
base.Direction = direction.HasFlagFast(BarDirection.Horizontal) ? FillDirection.Vertical : FillDirection.Horizontal; return;
foreach (var bar in Children) direction = value;
{ Invalidate(Invalidation.DrawNode);
bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, 1.0f / Children.Count) : new Vector2(1.0f / Children.Count, 1);
bar.Direction = direction;
}
} }
} }
private IEnumerable<BarDescriptor> bars;
/// <summary> /// <summary>
/// A list of floats that defines the length of each <see cref="Bar"/> /// A list of floats that defines the length of each <see cref="Bar"/>
/// </summary> /// </summary>
@ -44,38 +51,188 @@ namespace osu.Game.Graphics.UserInterface
{ {
set set
{ {
List<Bar> bars = Children.ToList(); List<BarDescriptor> newBars = bars?.ToList() ?? new List<BarDescriptor>();
foreach (var bar in value.Select((length, index) => new { Value = length, Bar = bars.Count > index ? bars[index] : null })) int newCount = value.Count();
float size = newCount;
if (size != 0)
size = 1.0f / size;
foreach (var bar in value.Select((length, index) => new { Value = length, Bar = newBars.Count > index ? newBars[index] : null }))
{ {
float length = MaxValue ?? value.Max(); float length = MaxValue ?? value.Max();
if (length != 0) if (length != 0)
length = bar.Value / length; length = Math.Max(0f, bar.Value / length);
float size = value.Count();
if (size != 0)
size = 1.0f / size;
if (bar.Bar != null) if (bar.Bar != null)
{ {
bar.Bar.Length = length; bar.Bar.OldValue = bar.Bar.Value;
bar.Bar.Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1);
bar.Bar.Value = length;
bar.Bar.ShortSide = size;
} }
else else
{ {
Add(new Bar newBars.Add(new BarDescriptor
{ {
RelativeSizeAxes = Axes.Both, Value = length,
Size = direction.HasFlagFast(BarDirection.Horizontal) ? new Vector2(1, size) : new Vector2(size, 1), ShortSide = size
Length = length,
Direction = Direction,
}); });
} }
} }
//I'm using ToList() here because Where() returns an Enumerable which can change it's elements afterwards if (newBars.Count > newCount)
RemoveRange(Children.Where((_, index) => index >= value.Count()).ToList(), true); newBars.RemoveRange(newCount, newBars.Count - newCount);
bars = newBars;
animationStartTime = Clock.CurrentTime;
animationComplete = false;
} }
} }
private double animationStartTime;
private bool animationComplete;
private IShader shader = null!;
private Texture texture = null!;
[BackgroundDependencyLoader]
private void load(IRenderer renderer, ShaderManager shaders)
{
texture = renderer.WhitePixel;
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
}
protected override void Update()
{
base.Update();
if (noBars)
return;
double currentTime = Clock.CurrentTime;
if (currentTime < animationStartTime + resize_duration)
{
foreach (var bar in bars)
bar.IntermediateValue = Interpolation.ValueAt(currentTime, bar.OldValue, bar.Value, animationStartTime, animationStartTime + resize_duration, easing);
Invalidate(Invalidation.DrawNode);
return;
}
else if (!animationComplete)
{
foreach (var bar in bars)
bar.IntermediateValue = bar.Value;
Invalidate(Invalidation.DrawNode);
animationComplete = true;
return;
}
}
private bool noBars => bars?.Any() != true;
protected override DrawNode CreateDrawNode() => new BarGraphDrawNode(this);
private class BarGraphDrawNode : DrawNode
{
public new BarGraph Source => (BarGraph)base.Source;
public BarGraphDrawNode(BarGraph source)
: base(source)
{
}
private IShader shader = null!;
private Texture texture = null!;
private Vector2 drawSize;
private BarDirection direction;
private readonly List<BarDescriptor> bars = new List<BarDescriptor>();
public override void ApplyState()
{
base.ApplyState();
shader = Source.shader;
texture = Source.texture;
drawSize = Source.DrawSize;
direction = Source.direction;
bars.Clear();
if (Source.noBars)
return;
bars.AddRange(Source.bars);
}
public override void Draw(IRenderer renderer)
{
base.Draw(renderer);
if (!bars.Any())
return;
shader.Bind();
for (int i = 0; i < bars.Count; i++)
{
var bar = bars[i];
float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? bar.IntermediateValue : bar.ShortSide);
float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? bar.IntermediateValue : bar.ShortSide);
Vector2 topLeft;
switch (direction)
{
default:
case BarDirection.LeftToRight:
topLeft = new Vector2(0, i * barHeight);
break;
case BarDirection.RightToLeft:
topLeft = new Vector2(drawSize.X - barWidth, i * barHeight);
break;
case BarDirection.TopToBottom:
topLeft = new Vector2(i * barWidth, 0);
break;
case BarDirection.BottomToTop:
topLeft = new Vector2(i * barWidth, drawSize.Y - barHeight);
break;
}
Vector2 topRight = topLeft + new Vector2(barWidth, 0);
Vector2 bottomLeft = topLeft + new Vector2(0, barHeight);
Vector2 bottomRight = bottomLeft + new Vector2(barWidth, 0);
var drawQuad = new Quad(
Vector2Extensions.Transform(topLeft, DrawInfo.Matrix),
Vector2Extensions.Transform(topRight, DrawInfo.Matrix),
Vector2Extensions.Transform(bottomLeft, DrawInfo.Matrix),
Vector2Extensions.Transform(bottomRight, DrawInfo.Matrix)
);
renderer.DrawQuad(texture, drawQuad, DrawColourInfo.Colour);
}
shader.Unbind();
}
}
private class BarDescriptor
{
public float OldValue { get; set; }
public float Value { get; set; }
public float IntermediateValue { get; set; }
public float ShortSide { get; set; }
}
} }
} }