104 lines
3.7 KiB
C#
104 lines
3.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace XericUI.XChart
|
|
{
|
|
public struct XChartAggregateBucket
|
|
{
|
|
public int StartIndex;
|
|
public int SampleCount;
|
|
public float First;
|
|
public float Last;
|
|
public float Minimum;
|
|
public float Maximum;
|
|
public float Mean;
|
|
}
|
|
|
|
public sealed class XChartMultiResolutionSeries
|
|
{
|
|
private readonly float[] m_Values;
|
|
private readonly List<XChartAggregateBucket[]> m_Levels = new List<XChartAggregateBucket[]>();
|
|
private int m_Head;
|
|
private int m_Count;
|
|
private long m_Version;
|
|
private long m_BuiltVersion = -1;
|
|
|
|
public XChartMultiResolutionSeries(int capacity)
|
|
{
|
|
if (capacity < 2) throw new ArgumentOutOfRangeException(nameof(capacity));
|
|
m_Values = new float[capacity];
|
|
}
|
|
|
|
public int Capacity => m_Values.Length;
|
|
public int Count => m_Count;
|
|
public long Version => m_Version;
|
|
|
|
public void Add(float value)
|
|
{
|
|
if (float.IsNaN(value) || float.IsInfinity(value)) return;
|
|
int index = (m_Head + m_Count) % Capacity;
|
|
if (m_Count == Capacity)
|
|
{
|
|
m_Values[m_Head] = value;
|
|
m_Head = (m_Head + 1) % Capacity;
|
|
}
|
|
else
|
|
{
|
|
m_Values[index] = value;
|
|
m_Count++;
|
|
}
|
|
m_Version++;
|
|
}
|
|
|
|
public void GetBuckets(int maximumBucketCount, List<XChartAggregateBucket> output)
|
|
{
|
|
if (output == null) throw new ArgumentNullException(nameof(output));
|
|
if (maximumBucketCount < 1) throw new ArgumentOutOfRangeException(nameof(maximumBucketCount));
|
|
RebuildLevels();
|
|
output.Clear();
|
|
if (m_Count == 0) return;
|
|
int level = 0;
|
|
while (level + 1 < m_Levels.Count && m_Levels[level + 1].Length >= maximumBucketCount) level++;
|
|
var buckets = m_Levels[level];
|
|
for (int i = 0; i < buckets.Length; i++) output.Add(buckets[i]);
|
|
}
|
|
|
|
private void RebuildLevels()
|
|
{
|
|
if (m_BuiltVersion == m_Version) return;
|
|
m_Levels.Clear();
|
|
var level = new XChartAggregateBucket[m_Count];
|
|
for (int i = 0; i < m_Count; i++)
|
|
{
|
|
float value = m_Values[(m_Head + i) % Capacity];
|
|
level[i] = new XChartAggregateBucket { StartIndex = i, SampleCount = 1, First = value, Last = value, Minimum = value, Maximum = value, Mean = value };
|
|
}
|
|
m_Levels.Add(level);
|
|
while (level.Length > 1)
|
|
{
|
|
var next = new XChartAggregateBucket[(level.Length + 1) / 2];
|
|
for (int i = 0; i < next.Length; i++)
|
|
{
|
|
var left = level[i * 2];
|
|
if (i * 2 + 1 >= level.Length) { next[i] = left; continue; }
|
|
var right = level[i * 2 + 1];
|
|
int count = left.SampleCount + right.SampleCount;
|
|
next[i] = new XChartAggregateBucket
|
|
{
|
|
StartIndex = left.StartIndex,
|
|
SampleCount = count,
|
|
First = left.First,
|
|
Last = right.Last,
|
|
Minimum = Math.Min(left.Minimum, right.Minimum),
|
|
Maximum = Math.Max(left.Maximum, right.Maximum),
|
|
Mean = (left.Mean * left.SampleCount + right.Mean * right.SampleCount) / count,
|
|
};
|
|
}
|
|
m_Levels.Add(next);
|
|
level = next;
|
|
}
|
|
m_BuiltVersion = m_Version;
|
|
}
|
|
}
|
|
}
|