2019-01-24 16:43:03 +08:00
|
|
|
// 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.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
#nullable disable
|
|
|
|
|
2018-11-20 15:51:59 +08:00
|
|
|
using osuTK;
|
2017-03-26 06:33:03 +08:00
|
|
|
using osu.Framework.Graphics;
|
2017-04-04 01:28:05 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
2022-11-19 05:19:49 +08:00
|
|
|
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;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-04-04 01:28:05 +08:00
|
|
|
namespace osu.Game.Graphics.UserInterface
|
2017-03-26 06:33:03 +08:00
|
|
|
{
|
2022-11-19 05:19:49 +08:00
|
|
|
public class BarGraph : Drawable
|
2017-04-04 01:28:05 +08:00
|
|
|
{
|
2022-11-19 05:19:49 +08:00
|
|
|
private const int resize_duration = 250;
|
|
|
|
private const Easing easing = Easing.InOutCubic;
|
|
|
|
|
2017-04-10 22:42:23 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Manually sets the max value, if null <see cref="Enumerable.Max(IEnumerable{float})"/> is instead used
|
|
|
|
/// </summary>
|
|
|
|
public float? MaxValue { get; set; }
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2017-04-04 23:17:22 +08:00
|
|
|
private BarDirection direction = BarDirection.BottomToTop;
|
2019-02-28 12:31:40 +08:00
|
|
|
|
2022-11-19 05:19:49 +08:00
|
|
|
public BarDirection Direction
|
2017-04-04 23:17:22 +08:00
|
|
|
{
|
2019-02-28 12:58:19 +08:00
|
|
|
get => direction;
|
2017-04-04 23:17:22 +08:00
|
|
|
set
|
|
|
|
{
|
2022-11-19 05:19:49 +08:00
|
|
|
if (direction == value)
|
|
|
|
return;
|
2019-04-01 11:16:05 +08:00
|
|
|
|
2022-11-19 05:19:49 +08:00
|
|
|
direction = value;
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
2017-04-04 23:17:22 +08:00
|
|
|
}
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
private readonly List<BarStruct> bars = new List<BarStruct>();
|
2022-11-19 05:19:49 +08:00
|
|
|
|
2017-04-10 22:42:23 +08:00
|
|
|
/// <summary>
|
|
|
|
/// A list of floats that defines the length of each <see cref="Bar"/>
|
|
|
|
/// </summary>
|
2017-04-04 01:28:05 +08:00
|
|
|
public IEnumerable<float> Values
|
|
|
|
{
|
|
|
|
set
|
|
|
|
{
|
2022-11-20 04:27:48 +08:00
|
|
|
if (!value.Any())
|
|
|
|
{
|
|
|
|
bars.Clear();
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-19 05:19:49 +08:00
|
|
|
int newCount = value.Count();
|
2019-04-01 11:16:05 +08:00
|
|
|
|
2022-11-20 04:27:48 +08:00
|
|
|
float size = 1.0f / newCount;
|
2022-11-19 05:19:49 +08:00
|
|
|
|
2022-11-20 04:27:48 +08:00
|
|
|
float maxLength = MaxValue ?? value.Max();
|
2022-11-19 15:16:58 +08:00
|
|
|
|
2022-11-20 04:29:50 +08:00
|
|
|
foreach (var bar in value.Select((length, index) => (Value: length, Index: index)))
|
2017-08-19 11:26:54 +08:00
|
|
|
{
|
2022-11-19 15:16:58 +08:00
|
|
|
float length = maxLength == 0 ? 0 : Math.Max(0f, bar.Value / maxLength);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
if (bar.Index < bars.Count)
|
2017-04-04 01:28:05 +08:00
|
|
|
{
|
2022-11-19 15:16:58 +08:00
|
|
|
BarStruct b = bars[bar.Index];
|
|
|
|
|
|
|
|
b.OldValue = b.Value;
|
|
|
|
b.Value = length;
|
|
|
|
b.ShortSide = size;
|
2022-11-19 05:19:49 +08:00
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
bars[bar.Index] = b;
|
2017-04-04 01:28:05 +08:00
|
|
|
}
|
|
|
|
else
|
2017-08-19 11:26:54 +08:00
|
|
|
{
|
2022-11-19 15:16:58 +08:00
|
|
|
bars.Add(new BarStruct
|
2017-04-04 01:28:05 +08:00
|
|
|
{
|
2022-11-19 05:19:49 +08:00
|
|
|
Value = length,
|
|
|
|
ShortSide = size
|
2017-04-04 01:28:05 +08:00
|
|
|
});
|
2017-08-19 11:26:54 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-28 12:31:40 +08:00
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
if (bars.Count > newCount)
|
|
|
|
bars.RemoveRange(newCount, bars.Count - newCount);
|
2022-11-19 05:19:49 +08:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
if (!bars.Any())
|
2022-11-19 05:19:49 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
double currentTime = Clock.CurrentTime;
|
|
|
|
|
|
|
|
if (currentTime < animationStartTime + resize_duration)
|
|
|
|
{
|
2022-11-19 15:23:37 +08:00
|
|
|
for (int i = 0; i < bars.Count; i++)
|
2022-11-19 15:16:58 +08:00
|
|
|
{
|
|
|
|
BarStruct bar = bars[i];
|
2022-11-19 05:19:49 +08:00
|
|
|
bar.IntermediateValue = Interpolation.ValueAt(currentTime, bar.OldValue, bar.Value, animationStartTime, animationStartTime + resize_duration, easing);
|
2022-11-19 15:16:58 +08:00
|
|
|
bars[i] = bar;
|
|
|
|
}
|
2022-11-19 05:19:49 +08:00
|
|
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
}
|
|
|
|
else if (!animationComplete)
|
|
|
|
{
|
2022-11-19 15:23:37 +08:00
|
|
|
for (int i = 0; i < bars.Count; i++)
|
2022-11-19 15:16:58 +08:00
|
|
|
{
|
|
|
|
BarStruct bar = bars[i];
|
2022-11-19 05:19:49 +08:00
|
|
|
bar.IntermediateValue = bar.Value;
|
2022-11-19 15:16:58 +08:00
|
|
|
bars[i] = bar;
|
|
|
|
}
|
2022-11-19 05:19:49 +08:00
|
|
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
|
|
|
animationComplete = 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;
|
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
private readonly List<BarStruct> bars = new List<BarStruct>();
|
2022-11-19 05:19:49 +08:00
|
|
|
|
|
|
|
public override void ApplyState()
|
|
|
|
{
|
|
|
|
base.ApplyState();
|
|
|
|
|
|
|
|
shader = Source.shader;
|
|
|
|
texture = Source.texture;
|
|
|
|
drawSize = Source.DrawSize;
|
|
|
|
direction = Source.direction;
|
|
|
|
|
|
|
|
bars.Clear();
|
|
|
|
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();
|
2017-04-04 01:28:05 +08:00
|
|
|
}
|
|
|
|
}
|
2022-11-19 05:19:49 +08:00
|
|
|
|
2022-11-19 15:16:58 +08:00
|
|
|
private struct BarStruct
|
2022-11-19 05:19:49 +08:00
|
|
|
{
|
|
|
|
public float OldValue { get; set; }
|
|
|
|
public float Value { get; set; }
|
|
|
|
public float IntermediateValue { get; set; }
|
|
|
|
public float ShortSide { get; set; }
|
|
|
|
}
|
2017-04-04 01:28:05 +08:00
|
|
|
}
|
2018-01-05 19:21:19 +08:00
|
|
|
}
|