mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-21 04:37:47 +03:00
137 lines
3.4 KiB
C#
137 lines
3.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
|
|
namespace NAPS2.Threading;
|
|
|
|
public class SmoothProgress : IDisposable
|
|
{
|
|
private const int INTERVAL = 16;
|
|
private const int VELOCITY_SAMPLE_SIZE = 5;
|
|
|
|
private double _inputPos;
|
|
private double _outputPos;
|
|
|
|
private double _inputVelocity;
|
|
private double _timeToCompletion;
|
|
private double _outputVelocity;
|
|
|
|
private Stopwatch _stopwatch;
|
|
private Timer _timer;
|
|
|
|
private LinkedList<double> _previousInputPos;
|
|
private LinkedList<long> _previousInputTimes;
|
|
|
|
public SmoothProgress()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
lock (this)
|
|
{
|
|
_inputPos = 0;
|
|
_outputPos = 0;
|
|
InvokeOutputProgressChanged();
|
|
|
|
_timer?.Dispose();
|
|
_timer = null;
|
|
|
|
_stopwatch = Stopwatch.StartNew();
|
|
|
|
_previousInputPos = new LinkedList<double>();
|
|
_previousInputPos.AddLast(0);
|
|
_previousInputTimes = new LinkedList<long>();
|
|
_previousInputTimes.AddLast(0);
|
|
}
|
|
}
|
|
|
|
public void InputProgressChanged(double value)
|
|
{
|
|
lock (this)
|
|
{
|
|
if (_inputPos < value)
|
|
{
|
|
_inputPos = value;
|
|
_previousInputPos.AddLast(_inputPos);
|
|
_previousInputTimes.AddLast(_stopwatch.ElapsedMilliseconds);
|
|
|
|
var deltaPos = _previousInputPos.Last.Value - SampleStart(_previousInputPos);
|
|
var deltaTime = _previousInputTimes.Last.Value - SampleStart(_previousInputTimes);
|
|
|
|
if (deltaTime > 0 && _inputPos < 1)
|
|
{
|
|
_inputVelocity = deltaPos / deltaTime;
|
|
_timeToCompletion = (1 - _inputPos) / _inputVelocity;
|
|
_outputVelocity = (1 - _outputPos) / _timeToCompletion;
|
|
}
|
|
|
|
if (_inputPos >= 1)
|
|
{
|
|
_inputVelocity = 0;
|
|
_timeToCompletion = 0;
|
|
_outputVelocity = 1;
|
|
}
|
|
|
|
if (_timer == null)
|
|
{
|
|
_timer = new Timer(TimerTick, null, 0, INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private T SampleStart<T>(LinkedList<T> list)
|
|
{
|
|
var node = list.Last;
|
|
for (int i = 0; i < VELOCITY_SAMPLE_SIZE; i++)
|
|
{
|
|
if (node.Previous == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
node = node.Previous;
|
|
}
|
|
if (node.Previous != null)
|
|
{
|
|
list.RemoveFirst();
|
|
}
|
|
return node.Value;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_timer?.Dispose();
|
|
}
|
|
|
|
private void TimerTick(object state)
|
|
{
|
|
lock (this)
|
|
{
|
|
_outputPos = Math.Min(_inputPos, _outputPos + _outputVelocity * INTERVAL);
|
|
}
|
|
InvokeOutputProgressChanged();
|
|
}
|
|
|
|
private void InvokeOutputProgressChanged()
|
|
{
|
|
OutputProgressChanged?.Invoke(this, new ProgressChangeEventArgs(_outputPos));
|
|
}
|
|
|
|
public event ProgressChangeEventHandle OutputProgressChanged;
|
|
|
|
public delegate void ProgressChangeEventHandle(object sender, ProgressChangeEventArgs args);
|
|
|
|
public class ProgressChangeEventArgs : EventArgs
|
|
{
|
|
public ProgressChangeEventArgs(double value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public double Value { get; set; }
|
|
}
|
|
} |