mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
重构雷达图
This commit is contained in:
@@ -13,6 +13,9 @@ using osu.Framework.Graphics.Rendering;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.LAsEzExtensions.Localization;
|
||||
using osu.Game.Localisation.SkinComponents;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Skinning;
|
||||
@@ -24,10 +27,69 @@ using Triangle = osu.Framework.Graphics.Primitives.Triangle;
|
||||
|
||||
namespace osu.Game.LAsEzExtensions.HUD
|
||||
{
|
||||
/// <summary>
|
||||
/// 雷达图面板,显示当前谱面参数的六边形图形化表示
|
||||
/// </summary>
|
||||
public partial class EzComRadarPanel : CompositeDrawable, ISerialisableDrawable
|
||||
{
|
||||
private const float axis_label_padding = 52f;
|
||||
private const float axis_label_offset = 16f;
|
||||
|
||||
private float[] parameterRatios = new float[6];
|
||||
private float[] parameterValues = new float[6];
|
||||
|
||||
private static readonly string[] default_axis_labels = { "BPM", "STAR", "CS", "OD", "HP", "AR" };
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
public float MaxBpm { get; set; } = 200f;
|
||||
|
||||
public float MaxStar { get; set; } = 10f;
|
||||
|
||||
public float MaxCs { get; set; } = 10f;
|
||||
|
||||
public float MaxOd { get; set; } = 10f;
|
||||
|
||||
public float MaxDr { get; set; } = 10f;
|
||||
|
||||
public float MaxAr { get; set; } = 10f;
|
||||
|
||||
[SettingSource(typeof(EzHUDStrings), nameof(EzHUDStrings.RADAR_BASE_LINE_COLOUR), nameof(EzHUDStrings.RADAR_BASE_LINE_COLOUR_TOOLTIP))]
|
||||
public BindableColour4 BaseLineColour { get; } = new BindableColour4(new Color4(255, 255, 210, 110));
|
||||
|
||||
[SettingSource(typeof(EzHUDStrings), nameof(EzHUDStrings.RADAR_BASE_AREA_COLOUR), nameof(EzHUDStrings.RADAR_BASE_AREA_COLOUR_TOOLTIP))]
|
||||
public BindableColour4 BaseAreaColour { get; } = new BindableColour4(new Color4(255, 255, 200, 30));
|
||||
|
||||
[SettingSource(typeof(EzHUDStrings), nameof(EzHUDStrings.RADAR_DATA_LINE_COLOUR), nameof(EzHUDStrings.RADAR_DATA_LINE_COLOUR_TOOLTIP))]
|
||||
public BindableColour4 DataLineColour { get; } = new BindableColour4(new Color4(255, 230, 128, 230));
|
||||
|
||||
[SettingSource(typeof(EzHUDStrings), nameof(EzHUDStrings.RADAR_DATA_AREA_COLOUR), nameof(EzHUDStrings.RADAR_DATA_AREA_COLOUR_TOOLTIP))]
|
||||
public BindableColour4 DataAreaColour { get; } = new BindableColour4(new Color4(255, 215, 0, 95));
|
||||
|
||||
public int AxisCount
|
||||
{
|
||||
get => chart?.AxisCount ?? parameterRatios.Length;
|
||||
set
|
||||
{
|
||||
int clamped = Math.Max(3, value);
|
||||
|
||||
if (parameterRatios.Length != clamped)
|
||||
{
|
||||
Array.Resize(ref parameterRatios, clamped);
|
||||
Array.Resize(ref parameterValues, clamped);
|
||||
}
|
||||
|
||||
if (chart != null)
|
||||
{
|
||||
chart.AxisCount = clamped;
|
||||
chart.SetData(parameterRatios);
|
||||
}
|
||||
|
||||
ensureAxisTexts();
|
||||
updateAxisTexts();
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
@@ -44,18 +106,9 @@ namespace osu.Game.LAsEzExtensions.HUD
|
||||
private CancellationTokenSource? difficultyCancellationSource;
|
||||
private ModSettingChangeTracker? modSettingTracker;
|
||||
|
||||
private readonly Bindable<float>[] parameters = new Bindable<float>[6];
|
||||
|
||||
private Hexagon hexagon = new Hexagon();
|
||||
private Hexagon parameterHexagon = new Hexagon();
|
||||
|
||||
// 定义各参数的最大值,用于归一化处理
|
||||
private const float max_bpm = 300f;
|
||||
private const float max_star = 12f;
|
||||
private const float max_cs = 10f;
|
||||
private const float max_od = 10f;
|
||||
private const float max_dr = 10f;
|
||||
private const float max_ar = 10f;
|
||||
private Container? axisLabelContainer;
|
||||
private RadarChart? chart;
|
||||
private OsuSpriteText[] axisTexts = Array.Empty<OsuSpriteText>();
|
||||
|
||||
public EzComRadarPanel()
|
||||
{
|
||||
@@ -65,51 +118,48 @@ namespace osu.Game.LAsEzExtensions.HUD
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
hexagon = new Hexagon
|
||||
chart = new RadarChart
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.LightYellow,
|
||||
Size = new Vector2(200),
|
||||
AxisCount = parameterRatios.Length,
|
||||
Size = new Vector2(220),
|
||||
GridLevels = 4,
|
||||
GridColour = BaseLineColour.Value,
|
||||
AxisColour = BaseLineColour.Value,
|
||||
BaseFillColour = BaseAreaColour.Value,
|
||||
DataFillColour = DataAreaColour.Value,
|
||||
DataStrokeColour = DataLineColour.Value,
|
||||
DataPointColour = DataLineColour.Value,
|
||||
},
|
||||
parameterHexagon = new Hexagon
|
||||
axisLabelContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.Gold,
|
||||
Size = new Vector2(200),
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
Size = new Vector2(220 + axis_label_padding * 2),
|
||||
},
|
||||
};
|
||||
|
||||
// 调试用边框
|
||||
AddInternal(new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = 2,
|
||||
Masking = true,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
ensureAxisTexts();
|
||||
updateAxisTexts();
|
||||
applyChartColours();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
hexagon.Invalidate(Invalidation.DrawNode);
|
||||
parameterHexagon.Invalidate(Invalidation.DrawNode);
|
||||
BaseLineColour.BindValueChanged(_ => applyChartColours(), true);
|
||||
BaseAreaColour.BindValueChanged(_ => applyChartColours(), true);
|
||||
DataLineColour.BindValueChanged(_ => applyChartColours(), true);
|
||||
DataAreaColour.BindValueChanged(_ => applyChartColours(), true);
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
parameters[i] = new BindableFloat();
|
||||
parameters[i].ValueChanged += _ => updatePanel();
|
||||
}
|
||||
chart?.SetData(parameterRatios);
|
||||
updateAxisTexts();
|
||||
|
||||
beatmap.BindValueChanged(b =>
|
||||
{
|
||||
@@ -120,14 +170,8 @@ namespace osu.Game.LAsEzExtensions.HUD
|
||||
difficultyBindable = difficultyCache.GetBindableDifficulty(b.NewValue.BeatmapInfo, difficultyCancellationSource.Token);
|
||||
difficultyBindable.BindValueChanged(d =>
|
||||
{
|
||||
// 归一化参数到0-1 范围
|
||||
parameters[0].Value = (float)(beatmap.Value.BeatmapInfo.BPM / max_bpm);
|
||||
parameters[1].Value = (float)beatmap.Value.BeatmapInfo.StarRating / max_star;
|
||||
parameters[2].Value = beatmap.Value.BeatmapInfo.Difficulty.CircleSize / max_cs;
|
||||
parameters[3].Value = beatmap.Value.BeatmapInfo.Difficulty.OverallDifficulty / max_od;
|
||||
parameters[4].Value = beatmap.Value.BeatmapInfo.Difficulty.DrainRate / max_dr;
|
||||
parameters[5].Value = beatmap.Value.BeatmapInfo.Difficulty.ApproachRate / max_ar;
|
||||
});
|
||||
updateParameterRatios(d.NewValue);
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
mods.BindValueChanged(m =>
|
||||
@@ -135,41 +179,211 @@ namespace osu.Game.LAsEzExtensions.HUD
|
||||
modSettingTracker?.Dispose();
|
||||
modSettingTracker = new ModSettingChangeTracker(m.NewValue)
|
||||
{
|
||||
SettingChanged = _ => updatePanel()
|
||||
SettingChanged = _ => updateParameterRatios(difficultyBindable?.Value ?? default)
|
||||
};
|
||||
updatePanel();
|
||||
updateParameterRatios(difficultyBindable?.Value ?? default);
|
||||
}, true);
|
||||
|
||||
ruleset.BindValueChanged(_ => updatePanel());
|
||||
|
||||
updatePanel();
|
||||
ruleset.BindValueChanged(_ => updateParameterRatios(difficultyBindable?.Value ?? default), true);
|
||||
}
|
||||
|
||||
private void updatePanel()
|
||||
private void updateParameterRatios(StarDifficulty difficulty)
|
||||
{
|
||||
hexagon.UpdateVertices(true); // 基础六边形(固定最大范围)
|
||||
parameterHexagon.UpdateVertices(false, parameters); // 参数六边形
|
||||
var beatmapInfo = beatmap.Value.BeatmapInfo;
|
||||
|
||||
if (parameterRatios.Length > 0)
|
||||
{
|
||||
parameterValues[0] = (float)beatmapInfo.BPM;
|
||||
parameterRatios[0] = normalise(parameterValues[0], MaxBpm);
|
||||
}
|
||||
|
||||
if (parameterRatios.Length > 1)
|
||||
{
|
||||
parameterValues[1] = (float)(difficulty.Stars > 0 ? difficulty.Stars : beatmapInfo.StarRating);
|
||||
parameterRatios[1] = normalise(parameterValues[1], MaxStar);
|
||||
}
|
||||
|
||||
if (parameterRatios.Length > 2)
|
||||
{
|
||||
parameterValues[2] = beatmapInfo.Difficulty.CircleSize;
|
||||
parameterRatios[2] = normalise(parameterValues[2], MaxCs);
|
||||
}
|
||||
|
||||
if (parameterRatios.Length > 3)
|
||||
{
|
||||
parameterValues[3] = beatmapInfo.Difficulty.OverallDifficulty;
|
||||
parameterRatios[3] = normalise(parameterValues[3], MaxOd);
|
||||
}
|
||||
|
||||
if (parameterRatios.Length > 4)
|
||||
{
|
||||
parameterValues[4] = beatmapInfo.Difficulty.DrainRate;
|
||||
parameterRatios[4] = normalise(parameterValues[4], MaxDr);
|
||||
}
|
||||
|
||||
if (parameterRatios.Length > 5)
|
||||
{
|
||||
parameterValues[5] = beatmapInfo.Difficulty.ApproachRate;
|
||||
parameterRatios[5] = normalise(parameterValues[5], MaxAr);
|
||||
}
|
||||
|
||||
chart?.SetData(parameterRatios);
|
||||
updateAxisTexts();
|
||||
}
|
||||
|
||||
private static float normalise(float value, float maxValue)
|
||||
{
|
||||
if (maxValue <= 0)
|
||||
return 0;
|
||||
|
||||
return Math.Clamp(value / maxValue, 0, 1);
|
||||
}
|
||||
|
||||
private void applyChartColours()
|
||||
{
|
||||
if (chart == null)
|
||||
return;
|
||||
|
||||
chart.GridColour = BaseLineColour.Value;
|
||||
chart.AxisColour = BaseLineColour.Value;
|
||||
chart.BaseFillColour = BaseAreaColour.Value;
|
||||
chart.DataStrokeColour = DataLineColour.Value;
|
||||
chart.DataPointColour = DataLineColour.Value;
|
||||
chart.DataFillColour = DataAreaColour.Value;
|
||||
chart.Invalidate(Invalidation.DrawNode);
|
||||
|
||||
foreach (var text in axisTexts)
|
||||
text.Colour = DataLineColour.Value;
|
||||
}
|
||||
|
||||
private void ensureAxisTexts()
|
||||
{
|
||||
if (axisLabelContainer == null || chart == null)
|
||||
return;
|
||||
|
||||
int axisCount = chart.AxisCount;
|
||||
if (axisTexts.Length == axisCount)
|
||||
return;
|
||||
|
||||
axisTexts = new OsuSpriteText[axisCount];
|
||||
Drawable[] drawables = new Drawable[axisCount];
|
||||
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
{
|
||||
axisTexts[i] = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = DataLineColour.Value,
|
||||
};
|
||||
|
||||
drawables[i] = axisTexts[i];
|
||||
}
|
||||
|
||||
axisLabelContainer.Clear();
|
||||
|
||||
foreach (var drawable in drawables)
|
||||
axisLabelContainer.Add(drawable);
|
||||
}
|
||||
|
||||
private void updateAxisTexts()
|
||||
{
|
||||
if (axisTexts.Length == 0 || chart == null || axisLabelContainer == null)
|
||||
return;
|
||||
|
||||
axisLabelContainer.Size = chart.Size + new Vector2(axis_label_padding * 2);
|
||||
|
||||
float radius = Math.Min(chart.Size.X, chart.Size.Y) * 0.5f * chart.RadiusRatio;
|
||||
int axisCount = Math.Max(3, chart.AxisCount);
|
||||
|
||||
for (int i = 0; i < axisCount && i < axisTexts.Length; i++)
|
||||
{
|
||||
float angle = MathHelper.DegreesToRadians(360f / axisCount * i - 90);
|
||||
Vector2 direction = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
||||
Vector2 position = direction * (radius + axis_label_offset);
|
||||
|
||||
axisTexts[i].Position = position;
|
||||
axisTexts[i].Text = $"{getAxisLabel(i)}\n{formatAxisValue(i)}";
|
||||
}
|
||||
}
|
||||
|
||||
private string getAxisLabel(int index)
|
||||
{
|
||||
return index < default_axis_labels.Length ? default_axis_labels[index] : $"P{index + 1}";
|
||||
}
|
||||
|
||||
private string formatAxisValue(int index)
|
||||
{
|
||||
if (index >= parameterValues.Length)
|
||||
return "0";
|
||||
|
||||
float value = parameterValues[index];
|
||||
|
||||
return index switch
|
||||
{
|
||||
0 => value.ToString("0"),
|
||||
1 => value.ToString("0.00"),
|
||||
_ => value.ToString("0.0"),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
difficultyCancellationSource?.Cancel();
|
||||
difficultyBindable?.UnbindAll();
|
||||
modSettingTracker?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Hexagon : Container
|
||||
public partial class RadarChart : Drawable
|
||||
{
|
||||
private Vector2[] vertices = new Vector2[6];
|
||||
private int axisCount = 6;
|
||||
private float[] dataRatios = new float[6];
|
||||
private Texture? whitePixel;
|
||||
private bool isBase;
|
||||
private Bindable<float>[]? parameters;
|
||||
private readonly string[] parameterNames = { "BPM", "Star", "CS", "OD", "DR", "AR" };
|
||||
|
||||
public Hexagon()
|
||||
public int AxisCount
|
||||
{
|
||||
get => axisCount;
|
||||
set
|
||||
{
|
||||
int clamped = Math.Max(3, value);
|
||||
|
||||
if (axisCount == clamped)
|
||||
return;
|
||||
|
||||
axisCount = clamped;
|
||||
Array.Resize(ref dataRatios, axisCount);
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
}
|
||||
|
||||
public int GridLevels { get; set; } = 4;
|
||||
|
||||
public float RadiusRatio { get; set; } = 0.82f;
|
||||
|
||||
public float GridThickness { get; set; } = 1.5f;
|
||||
|
||||
public float AxisThickness { get; set; } = 1.5f;
|
||||
|
||||
public float DataOutlineThickness { get; set; } = 2.2f;
|
||||
|
||||
public float DataPointSize { get; set; } = 5f;
|
||||
|
||||
public Color4 GridColour { get; set; } = new Color4(255, 255, 210, 110);
|
||||
|
||||
public Color4 AxisColour { get; set; } = new Color4(255, 255, 210, 95);
|
||||
|
||||
public Color4 BaseFillColour { get; set; } = new Color4(255, 255, 200, 30);
|
||||
|
||||
public Color4 DataFillColour { get; set; } = new Color4(255, 215, 0, 95);
|
||||
|
||||
public Color4 DataStrokeColour { get; set; } = new Color4(255, 230, 128, 230);
|
||||
|
||||
public Color4 DataPointColour { get; set; } = new Color4(255, 242, 176, 255);
|
||||
|
||||
public RadarChart()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
@@ -189,125 +403,227 @@ namespace osu.Game.LAsEzExtensions.HUD
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void UpdateVertices(bool isBase, Bindable<float>[]? parameters = null)
|
||||
public void SetData(IReadOnlyList<float> ratios)
|
||||
{
|
||||
this.isBase = isBase;
|
||||
this.parameters = parameters;
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
dataRatios[i] = i < ratios.Count ? Math.Clamp(ratios[i], 0, 1) : 0;
|
||||
|
||||
Invalidate(Invalidation.DrawNode);
|
||||
}
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new HexagonDrawNode(this);
|
||||
protected override DrawNode CreateDrawNode() => new RadarChartDrawNode(this);
|
||||
|
||||
private class HexagonDrawNode : DrawNode
|
||||
private class RadarChartDrawNode : DrawNode
|
||||
{
|
||||
private readonly Hexagon source;
|
||||
private readonly Vector2[] vertices = new Vector2[6];
|
||||
private Color4 color;
|
||||
private readonly RadarChart source;
|
||||
|
||||
private float[] ratios = Array.Empty<float>();
|
||||
private int axisCount;
|
||||
|
||||
private int gridLevels;
|
||||
private float radiusRatio;
|
||||
private float gridThickness;
|
||||
private float axisThickness;
|
||||
private float dataOutlineThickness;
|
||||
private float dataPointSize;
|
||||
|
||||
private Color4 gridColour;
|
||||
private Color4 axisColour;
|
||||
private Color4 baseFillColour;
|
||||
private Color4 dataFillColour;
|
||||
private Color4 dataStrokeColour;
|
||||
private Color4 dataPointColour;
|
||||
|
||||
private Vector2 drawSize;
|
||||
private Texture? texture;
|
||||
|
||||
public HexagonDrawNode(Hexagon hexagon)
|
||||
: base(hexagon)
|
||||
public RadarChartDrawNode(RadarChart chart)
|
||||
: base(chart)
|
||||
{
|
||||
source = hexagon;
|
||||
source = chart;
|
||||
}
|
||||
|
||||
public override void ApplyState()
|
||||
{
|
||||
base.ApplyState();
|
||||
color = source.Colour;
|
||||
|
||||
texture = source.whitePixel;
|
||||
|
||||
// 获取基于相对尺寸的实际绘制区域
|
||||
var drawSize = source.DrawSize;
|
||||
drawSize = source.DrawSize;
|
||||
axisCount = source.AxisCount;
|
||||
|
||||
float radius = Math.Min(drawSize.X, drawSize.Y) / 2 * 0.8f;
|
||||
Vector2 center = drawSize * 6;
|
||||
if (ratios.Length != axisCount)
|
||||
Array.Resize(ref ratios, axisCount);
|
||||
|
||||
if (source.isBase)
|
||||
{
|
||||
// 基于本地坐标系(原点在中心)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
float angle = MathHelper.DegreesToRadians(60 * i - 90);
|
||||
vertices[i] = new Vector2(
|
||||
radius * (float)Math.Cos(angle) + center.X,
|
||||
radius * (float)Math.Sin(angle) + center.Y
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (source.parameters != null)
|
||||
{
|
||||
// 参数六边形(本地坐标系)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
float value = Math.Clamp(source.parameters[i].Value, 0, 1);
|
||||
float angle = MathHelper.DegreesToRadians(60 * i - 90);
|
||||
vertices[i] = new Vector2(
|
||||
radius * value * (float)Math.Cos(angle) + center.X,
|
||||
radius * value * (float)Math.Sin(angle) + center.Y
|
||||
);
|
||||
}
|
||||
}
|
||||
gridLevels = Math.Max(1, source.GridLevels);
|
||||
radiusRatio = Math.Clamp(source.RadiusRatio, 0.1f, 1);
|
||||
gridThickness = Math.Max(0.5f, source.GridThickness);
|
||||
axisThickness = Math.Max(0.5f, source.AxisThickness);
|
||||
dataOutlineThickness = Math.Max(0.5f, source.DataOutlineThickness);
|
||||
dataPointSize = Math.Max(1, source.DataPointSize);
|
||||
|
||||
gridColour = source.GridColour;
|
||||
axisColour = source.AxisColour;
|
||||
baseFillColour = source.BaseFillColour;
|
||||
dataFillColour = source.DataFillColour;
|
||||
dataStrokeColour = source.DataStrokeColour;
|
||||
dataPointColour = source.DataPointColour;
|
||||
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
ratios[i] = source.dataRatios[i];
|
||||
}
|
||||
|
||||
protected override void Draw(IRenderer renderer)
|
||||
{
|
||||
if (texture == null) return;
|
||||
if (texture == null)
|
||||
return;
|
||||
|
||||
// 框架会自动应用以下变换矩阵:
|
||||
// DrawInfo.Matrix = 位置矩阵 × 缩放矩阵 × 旋转矩阵 × 锚点偏移
|
||||
float radius = Math.Min(drawSize.X, drawSize.Y) * 0.5f * radiusRatio;
|
||||
if (radius <= 0)
|
||||
return;
|
||||
|
||||
// 绘制时直接使用本地坐标系顶点即可
|
||||
// 填充六边形
|
||||
for (int i = 1; i < vertices.Length - 1; i++)
|
||||
Vector2 center = drawSize * 0.5f;
|
||||
|
||||
renderer.PushLocalMatrix(DrawInfo.Matrix);
|
||||
|
||||
var outerVertices = createVertices(center, radius, 1);
|
||||
|
||||
drawPolygonFill(renderer, outerVertices, baseFillColour);
|
||||
|
||||
for (int level = 1; level <= gridLevels; level++)
|
||||
{
|
||||
renderer.DrawTriangle(
|
||||
texture,
|
||||
new Triangle(
|
||||
vertices[0],
|
||||
vertices[i],
|
||||
vertices[(i + 1) % vertices.Length]
|
||||
),
|
||||
color
|
||||
float ratio = level / (float)gridLevels;
|
||||
var levelVertices = createVertices(center, radius, ratio);
|
||||
drawPolygonOutline(renderer, levelVertices, gridColour, gridThickness);
|
||||
}
|
||||
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
drawLine(renderer, center, outerVertices[i], axisColour, axisThickness);
|
||||
|
||||
var dataVertices = createVertices(center, radius, ratios);
|
||||
|
||||
drawFanFill(renderer, center, dataVertices, dataFillColour);
|
||||
drawPolygonOutline(renderer, dataVertices, dataStrokeColour, dataOutlineThickness);
|
||||
drawPoints(renderer, dataVertices, dataPointColour, dataPointSize);
|
||||
|
||||
renderer.PopLocalMatrix();
|
||||
}
|
||||
|
||||
private Vector2[] createVertices(Vector2 center, float radius, float ratio)
|
||||
{
|
||||
var vertices = new Vector2[axisCount];
|
||||
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
{
|
||||
float angle = MathHelper.DegreesToRadians(360f / axisCount * i - 90);
|
||||
vertices[i] = new Vector2(
|
||||
center.X + radius * ratio * (float)Math.Cos(angle),
|
||||
center.Y + radius * ratio * (float)Math.Sin(angle)
|
||||
);
|
||||
}
|
||||
|
||||
// 边线绘制
|
||||
foreach (var quad in generateLineQuads())
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private Vector2[] createVertices(Vector2 center, float radius, IReadOnlyList<float> axisRatios)
|
||||
{
|
||||
var vertices = new Vector2[axisCount];
|
||||
|
||||
for (int i = 0; i < axisCount; i++)
|
||||
{
|
||||
renderer.DrawQuad(
|
||||
texture,
|
||||
quad,
|
||||
color
|
||||
float clampedRatio = Math.Clamp(axisRatios[i], 0, 1);
|
||||
float angle = MathHelper.DegreesToRadians(360f / axisCount * i - 90);
|
||||
vertices[i] = new Vector2(
|
||||
center.X + radius * clampedRatio * (float)Math.Cos(angle),
|
||||
center.Y + radius * clampedRatio * (float)Math.Sin(angle)
|
||||
);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
private void drawPolygonFill(IRenderer renderer, IReadOnlyList<Vector2> polygonVertices, Color4 colour)
|
||||
{
|
||||
for (int i = 1; i < polygonVertices.Count - 1; i++)
|
||||
{
|
||||
renderer.DrawTriangle(
|
||||
texture!,
|
||||
new Triangle(
|
||||
polygonVertices[0],
|
||||
polygonVertices[i],
|
||||
polygonVertices[i + 1]),
|
||||
colour);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Quad> generateLineQuads()
|
||||
private void drawFanFill(IRenderer renderer, Vector2 center, IReadOnlyList<Vector2> polygonVertices, Color4 colour)
|
||||
{
|
||||
const float line_thickness = 2f;
|
||||
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
for (int i = 0; i < polygonVertices.Count; i++)
|
||||
{
|
||||
Vector2 current = vertices[i];
|
||||
Vector2 next = vertices[(i + 1) % vertices.Length];
|
||||
|
||||
Vector2 dir = next - current;
|
||||
float length = dir.Length;
|
||||
if (length < float.Epsilon) continue;
|
||||
|
||||
dir.Normalize();
|
||||
|
||||
Vector2 perpendicular = new Vector2(-dir.Y, dir.X) * line_thickness / 2;
|
||||
|
||||
yield return new Quad(
|
||||
current - perpendicular,
|
||||
current + perpendicular,
|
||||
next - perpendicular,
|
||||
next + perpendicular
|
||||
);
|
||||
renderer.DrawTriangle(
|
||||
texture!,
|
||||
new Triangle(
|
||||
center,
|
||||
polygonVertices[i],
|
||||
polygonVertices[(i + 1) % polygonVertices.Count]),
|
||||
colour);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPolygonOutline(IRenderer renderer, IReadOnlyList<Vector2> polygonVertices, Color4 colour, float thickness)
|
||||
{
|
||||
for (int i = 0; i < polygonVertices.Count; i++)
|
||||
{
|
||||
Vector2 start = polygonVertices[i];
|
||||
Vector2 end = polygonVertices[(i + 1) % polygonVertices.Count];
|
||||
drawLine(renderer, start, end, colour, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPoints(IRenderer renderer, IReadOnlyList<Vector2> polygonVertices, Color4 colour, float pointSize)
|
||||
{
|
||||
float half = pointSize * 0.5f;
|
||||
|
||||
for (int i = 0; i < polygonVertices.Count; i++)
|
||||
{
|
||||
Vector2 point = polygonVertices[i];
|
||||
|
||||
renderer.DrawTriangle(
|
||||
texture!,
|
||||
new Triangle(
|
||||
new Vector2(point.X - half, point.Y - half),
|
||||
new Vector2(point.X + half, point.Y - half),
|
||||
new Vector2(point.X + half, point.Y + half)),
|
||||
colour);
|
||||
|
||||
renderer.DrawTriangle(
|
||||
texture!,
|
||||
new Triangle(
|
||||
new Vector2(point.X - half, point.Y - half),
|
||||
new Vector2(point.X + half, point.Y + half),
|
||||
new Vector2(point.X - half, point.Y + half)),
|
||||
colour);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawLine(IRenderer renderer, Vector2 start, Vector2 end, Color4 colour, float thickness)
|
||||
{
|
||||
Vector2 direction = end - start;
|
||||
if (direction.LengthSquared <= float.Epsilon)
|
||||
return;
|
||||
|
||||
direction.Normalize();
|
||||
Vector2 perpendicular = new Vector2(-direction.Y, direction.X) * (thickness * 0.5f);
|
||||
|
||||
renderer.DrawQuad(
|
||||
texture!,
|
||||
new Quad(
|
||||
start - perpendicular,
|
||||
start + perpendicular,
|
||||
end - perpendicular,
|
||||
end + perpendicular),
|
||||
colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
osu.Game/LAsEzExtensions/Localization/EzHUDStrings.cs
Normal file
34
osu.Game/LAsEzExtensions/Localization/EzHUDStrings.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.LAsEzExtensions.Localization
|
||||
{
|
||||
public static class EzHUDStrings
|
||||
{
|
||||
public static readonly LocalisableString RADAR_BASE_LINE_COLOUR =
|
||||
new EzLocalizationManager.EzLocalisableString("雷达底板线色", "Radar Base Line Colour");
|
||||
|
||||
public static readonly LocalisableString RADAR_BASE_LINE_COLOUR_TOOLTIP =
|
||||
new EzLocalizationManager.EzLocalisableString("底板网格线和轴线的颜色", "Colour of base grid and axis lines.");
|
||||
|
||||
public static readonly LocalisableString RADAR_BASE_AREA_COLOUR =
|
||||
new EzLocalizationManager.EzLocalisableString("雷达底板区色", "Radar Base Area Colour");
|
||||
|
||||
public static readonly LocalisableString RADAR_BASE_AREA_COLOUR_TOOLTIP =
|
||||
new EzLocalizationManager.EzLocalisableString("底板填充区域的颜色", "Colour of base filled area.");
|
||||
|
||||
public static readonly LocalisableString RADAR_DATA_LINE_COLOUR =
|
||||
new EzLocalizationManager.EzLocalisableString("雷达数据线色", "Radar Data Line Colour");
|
||||
|
||||
public static readonly LocalisableString RADAR_DATA_LINE_COLOUR_TOOLTIP =
|
||||
new EzLocalizationManager.EzLocalisableString("数据轮廓线和顶点标记的颜色", "Colour of data outline and point markers.");
|
||||
|
||||
public static readonly LocalisableString RADAR_DATA_AREA_COLOUR =
|
||||
new EzLocalizationManager.EzLocalisableString("雷达数据区色", "Radar Data Area Colour");
|
||||
|
||||
public static readonly LocalisableString RADAR_DATA_AREA_COLOUR_TOOLTIP =
|
||||
new EzLocalizationManager.EzLocalisableString("数据填充区域的颜色", "Colour of data filled area.");
|
||||
}
|
||||
}
|
||||
@@ -77,13 +77,7 @@ namespace osu.Game.Skinning
|
||||
dim.Origin = Anchor.Centre;
|
||||
dim.Position = new Vector2(-80, -150);
|
||||
}
|
||||
})
|
||||
{
|
||||
// Children = new Drawable[]
|
||||
// {
|
||||
// new LAsSkinCom6DimPanel(),
|
||||
// }
|
||||
};
|
||||
});
|
||||
|
||||
return songSelectComponents;
|
||||
|
||||
|
||||
@@ -89,15 +89,9 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
dim.Anchor = Anchor.BottomCentre;
|
||||
dim.Origin = Anchor.Centre;
|
||||
dim.Position = new Vector2(-80, -150);
|
||||
dim.Position = new Vector2(0, -150);
|
||||
}
|
||||
})
|
||||
{
|
||||
// Children = new Drawable[]
|
||||
// {
|
||||
// new LAsSkinCom6DimPanel(),
|
||||
// }
|
||||
};
|
||||
});
|
||||
|
||||
return songSelectComponents;
|
||||
|
||||
|
||||
@@ -69,20 +69,7 @@ namespace osu.Game.Skinning
|
||||
case GlobalSkinnableContainers.SongSelect:
|
||||
var songSelectComponents = new DefaultSkinComponentsContainer(c =>
|
||||
{
|
||||
// var dim = c.OfType<LAsSkinCom6DimPanel>().FirstOrDefault();
|
||||
//
|
||||
// if (dim != null)
|
||||
// {
|
||||
// dim.Anchor = Anchor.Centre;
|
||||
// dim.Origin = Anchor.Centre;
|
||||
// }
|
||||
})
|
||||
{
|
||||
// Children = new Drawable[]
|
||||
// {
|
||||
// new LAsSkinCom6DimPanel(),
|
||||
// }
|
||||
};
|
||||
});
|
||||
|
||||
return songSelectComponents;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user