Skip to content

Commit

Permalink
fix: various fix
Browse files Browse the repository at this point in the history
  • Loading branch information
LeZi9916 committed Feb 5, 2025
1 parent 48748b7 commit 5043df5
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 123 deletions.
19 changes: 19 additions & 0 deletions Assets/Scripts/Misc/Types/MaidataAnalyzeResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
#nullable enable
namespace MajdataPlay.Types
{
internal struct MaidataAnalyzeResult
{
public float PeakDensity { get; init; }
public float Esti { get; init; }
public TimeSpan Length { get; init; }
public float MaxBPM { get; init; }
public float MinBPM { get; init; }
public Texture LineGraph { get; init; }
}
}
11 changes: 11 additions & 0 deletions Assets/Scripts/Misc/Types/MaidataAnalyzeResult.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

244 changes: 167 additions & 77 deletions Assets/Scripts/Scenes/Game/ChartAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

using UnityEngine.UIElements;
#nullable enable
namespace MajdataPlay.Game
{
public class ChartAnalyzer : MonoBehaviour
Expand All @@ -24,28 +26,49 @@ public class ChartAnalyzer : MonoBehaviour
public UnityEngine.Color slideColor;
public UnityEngine.Color touchColor;

public Text anaText;
static UnityEngine.Color _colorA;
static UnityEngine.Color _colorB;
static UnityEngine.Color _colorC;

public Text? anaText;
readonly AsyncLock _locker = new();

void Start()
{
_colorA = tapColor;
_colorB = slideColor;
_colorC = touchColor;
_rawImage = GetComponent<RawImage>();
}

public async UniTask AnalyzeSongDetail(ISongDetail songDetail, ChartLevel level, float length = -1)
public async UniTask AnalyzeAndDrawGraphAsync(ISongDetail songDetail, ChartLevel level, float length = -1, CancellationToken token = default)
{
if (!_locker.TryLock(() => { }, TimeSpan.Zero))
return;
using (await _locker.LockAsync())
try
{
try
{
var simaiFile = await songDetail.GetMaidataAsync();
var maiChart = simaiFile.Levels[(int)level];
var lastnoteTiming = length == -1 ? maiChart.NoteTimings.Last().Timing : length;
AnalyzeMaidata(maiChart, (float)lastnoteTiming);
var result = await AnalyzeMaidataAsync(maiChart, (float)lastnoteTiming);

token.ThrowIfCancellationRequested();
_rawImage.texture = result.LineGraph;
if (anaText is not null)
{
var max = result.PeakDensity;
var esti = result.Esti;
var minBPM = result.MinBPM;
var maxBPM = result.MaxBPM;
var time = result.Length;

anaText.text = "Peak Density = " + max + "\n";
anaText.text += "Esti = Lv." + (esti) + "\n";
anaText.text += "Length = " + ZString.Format("{0}:{1:00}.{2:000}", time.Minutes, time.Seconds, time.Milliseconds) + "\n";
anaText.text += "BPM = " + minBPM + " - " + maxBPM;
}
}
catch(Exception ex)
catch (Exception ex)
{
MajDebug.LogException(ex);
await UniTask.Yield();
Expand All @@ -56,90 +79,153 @@ public async UniTask AnalyzeSongDetail(ISongDetail songDetail, ChartLevel level,
}
}
}
finally
{
await UniTask.Yield();
}
}
public async UniTask AnalyzeAndDrawGraphAsync(SimaiChart data, float totalLength, CancellationToken token = default)
{
try
{
try
{
var result = await AnalyzeMaidataAsync(data, totalLength);

token.ThrowIfCancellationRequested();
_rawImage.texture = result.LineGraph;
if (anaText is not null)
{
var max = result.PeakDensity;
var esti = result.Esti;
var minBPM = result.MinBPM;
var maxBPM = result.MaxBPM;
var time = result.Length;

public void AnalyzeMaidata(SimaiChart data, float totalLength)
anaText.text = "Peak Density = " + max + "\n";
anaText.text += "Esti = Lv." + (esti) + "\n";
anaText.text += "Length = " + ZString.Format("{0}:{1:00}.{2:000}", time.Minutes, time.Seconds, time.Milliseconds) + "\n";
anaText.text += "BPM = " + minBPM + " - " + maxBPM;
}
}
catch (Exception ex)
{
MajDebug.LogException(ex);
await UniTask.Yield();
_rawImage.texture = new Texture2D(0, 0);
if (anaText is not null)
{
anaText.text = "";
}
}
}
finally
{
await UniTask.Yield();
}
}
internal static async UniTask<MaidataAnalyzeResult> AnalyzeMaidataAsync(SimaiChart data, float totalLength)
{
var tapPoints = new List<Vector2>();
var slidePoints = new List<Vector2>();
var touchPoints = new List<Vector2>();
var max = 0f;
var maxBPM = 0f;
var minBPM = 0f;
for (float time = 0; time < totalLength; time += 0.5f)
try
{
var timingPoints = data.NoteTimings.ToList().FindAll(o => o.Timing > time - 0.75f && o.Timing <= time + 0.75f);
float y0 = 0, y1 = 0, y2 = 0;
foreach (var timingPoint in timingPoints)
var tapPoints = new List<Vector2>();
var slidePoints = new List<Vector2>();
var touchPoints = new List<Vector2>();
var max = 0f;
var maxBPM = 0f;
var minBPM = 0f;
var length = TimeSpan.Zero;
var esti = 0f;

await Task.Run(() =>
{
foreach (var note in timingPoint.Notes)
for (float time = 0; time < totalLength; time += 0.5f)
{
if (note.Type == SimaiNoteType.Tap || note.Type == SimaiNoteType.Hold)
{
y0++;
}
else if (note.Type == SimaiNoteType.Slide)
{
y1 += 2;
}
else if (note.Type == SimaiNoteType.Touch || note.Type == SimaiNoteType.TouchHold)
var timingPoints = data.NoteTimings.ToList().FindAll(o => o.Timing > time - 0.75f && o.Timing <= time + 0.75f);
float y0 = 0, y1 = 0, y2 = 0;
foreach (var timingPoint in timingPoints)
{
y2++;
foreach (var note in timingPoint.Notes)
{
switch (note.Type)
{
case SimaiNoteType.Tap:
case SimaiNoteType.Hold:
y0++;
break;
case SimaiNoteType.Slide:
y1 += 2;
break;
case SimaiNoteType.Touch:
case SimaiNoteType.TouchHold:
y2++;
break;
}
}

}
if (y0 + y1 + y2 > max) max = y0 + y1 + y2;

var x = time / totalLength;
tapPoints.Add(new Vector2(x, y0));
slidePoints.Add(new Vector2(x, y1));
touchPoints.Add(new Vector2(x, y2));
maxBPM = data.NoteTimings.Max(o => o.Bpm);
minBPM = data.NoteTimings.Min(o => o.Bpm);
}

}
if (y0 + y1 + y2 > max) max = y0 + y1 + y2;

var x = time / totalLength;
tapPoints.Add(new Vector2(x, y0));
slidePoints.Add(new Vector2(x, y1));
touchPoints.Add(new Vector2(x, y2));
maxBPM = data.NoteTimings.Max(o => o.Bpm);
minBPM = data.NoteTimings.Min(o => o.Bpm);
}
if (anaText is not null)
{

var time = TimeSpan.FromSeconds(totalLength);
anaText.text = "Peak Density = " + max +"\n";
var avg = tapPoints.Average(o => o.y) + 3f * slidePoints.Average(o => o.y) + 0.5f * touchPoints.Average(o => o.y);
var esti = 7.5f * Mathf.Log10(3.8f*(avg + 0.3f * max));
anaText.text += "Esti = Lv." + (esti) + "\n";
anaText.text += "Length = " + ZString.Format("{0}:{1:00}.{2:000}", time.Minutes, time.Seconds, time.Milliseconds) + "\n";
anaText.text += "BPM = " + minBPM + " - " + maxBPM;

var avg = tapPoints.Average(o => o.y) + 3f * slidePoints.Average(o => o.y) + 0.5f * touchPoints.Average(o => o.y);
length = TimeSpan.FromSeconds(totalLength);
esti = 7.5f * Mathf.Log10(3.8f * (avg + 0.3f * max));

//normalize
for (var i = 0; i < tapPoints.Count; i++)
{
tapPoints[i] = new Vector2(tapPoints[i].x, tapPoints[i].y / max);
slidePoints[i] = new Vector2(slidePoints[i].x, slidePoints[i].y / max);
touchPoints[i] = new Vector2(touchPoints[i].x, touchPoints[i].y / max);
}
});
var tex = await DrawGraphAsync(tapPoints, slidePoints, touchPoints);

return new MaidataAnalyzeResult()
{
Esti = esti,
Length = length,
MaxBPM = maxBPM,
MinBPM = minBPM,
PeakDensity = max,
LineGraph = tex
};
}
//normalize
for (var i = 0; i < tapPoints.Count; i++)
finally
{
tapPoints[i] = new Vector2(tapPoints[i].x, tapPoints[i].y / max);
slidePoints[i] = new Vector2(slidePoints[i].x, slidePoints[i].y / max);
touchPoints[i] = new Vector2(touchPoints[i].x, touchPoints[i].y / max);
await UniTask.Yield();
}
DrawGraph(tapPoints, slidePoints, touchPoints);

}

void DrawGraph(List<Vector2> tapPoints, List<Vector2> slidePoints, List<Vector2> touchPoints)
static async ValueTask<Texture> DrawGraphAsync(List<Vector2> tapPoints, List<Vector2> slidePoints, List<Vector2> touchPoints)
{
var width = 1018;
var height = 187;
var imageInfo = new SKImageInfo(width, height);
using (var surface = SKSurface.Create(imageInfo))
using var surface = SKSurface.Create(imageInfo);

await Task.Run(() =>
{
var canvas = surface.Canvas;
canvas.Clear(SKColor.Empty);
using (var tapPaint = new SKPaint())
using (var slidePaint = new SKPaint())
using (var touchPaint = new SKPaint())
{
tapPaint.Color = ToSkColor(tapColor);
tapPaint.Color = ToSkColor(_colorA);
tapPaint.IsAntialias = true;
tapPaint.Style = SKPaintStyle.Fill;
slidePaint.Color = ToSkColor(slideColor);
slidePaint.Color = ToSkColor(_colorB);
slidePaint.IsAntialias = true;
slidePaint.Style = SKPaintStyle.Fill;
touchPaint.Color = ToSkColor(touchColor);
touchPaint.Color = ToSkColor(_colorC);
touchPaint.IsAntialias = true;
touchPaint.Style = SKPaintStyle.Fill;
using (var tapPath = new SKPath())
Expand Down Expand Up @@ -171,22 +257,26 @@ void DrawGraph(List<Vector2> tapPoints, List<Vector2> slidePoints, List<Vector2>
canvas.DrawPath(tapPath, tapPaint);
}
}
});
await UniTask.Yield();
return GraphSnapshot(surface);
}
static Texture GraphSnapshot(SKSurface surface)
{
//sort it into rawimage
using (var image = surface.Snapshot())
{
var bitmap = SKBitmap.FromImage(image);
var skcolors = bitmap.Pixels.AsSpan();
var writer = new ArrayBufferWriter<SKColor>(bitmap.Width * bitmap.Height);
for (var i = bitmap.Height - 1; i >= 0; i--) writer.Write(skcolors.Slice(i * bitmap.Width, bitmap.Width));
var colors = writer.WrittenSpan.ToArray().AsParallel().AsOrdered().Select(s => ToUnityColor32(s)).ToArray();

//sort it into rawimage
using (var image = surface.Snapshot())
{
var bitmap = SKBitmap.FromImage(image);
var skcolors = bitmap.Pixels.AsSpan();
var writer = new ArrayBufferWriter<SKColor>(bitmap.Width * bitmap.Height);
for (var i = bitmap.Height - 1; i >= 0; i--) writer.Write(skcolors.Slice(i * bitmap.Width, bitmap.Width));
var colors = writer.WrittenSpan.ToArray().AsParallel().AsOrdered().Select(s => ToUnityColor32(s)).ToArray();

var tex0 = new Texture2D(bitmap.Width, bitmap.Height);
tex0.SetPixels32(colors);
tex0.Apply();
var tex0 = new Texture2D(bitmap.Width, bitmap.Height);
tex0.SetPixels32(colors);
tex0.Apply();

_rawImage.texture = tex0;
}
return tex0;
}
}
public static Color32 ToUnityColor32(SKColor skColor)
Expand Down
Loading

0 comments on commit 5043df5

Please sign in to comment.