winmr-api/Utils/Scroll.cs
2026-01-08 21:35:06 +03:00

117 lines
3.8 KiB
C#

using WebmrAPI.Utils.Sequences;
namespace WebmrAPI.Utils
{
public sealed class ScrollAmountGenerator : BaseGenerator<int>
{
public enum Direction
{
Up,
Down
}
public class CorrectionData
{
public Direction Dir { get; set; }
public int Order { get; set; }
}
private double _fJitter = 0.12;
private double _fAccuracy = 0.83;
private int _nCorrections = 0;
private int _bAmount = 120;
public int TotalAmount { get; private set; }
public int CurrentAmount { get; private set; }
public int BaseAmount { get => _bAmount; private set => UpdateSpread(value, FJitter); }
public double FJitter { get => _fJitter; private set => UpdateSpread(BaseAmount, Math.Clamp(Math.Abs(value), 0.0, 1.0)); }
public int CorrectionLimit { get; private set; } = 1;
public override double Progress { get => (double)CurrentAmount / TotalAmount; protected set => base.Progress = value; }
public double FAccuracy { get => _fAccuracy; private set => _fAccuracy = Math.Clamp(Math.Abs(value), 0.0, 1.0); }
public Direction Dir { get; private set; }
public override double Step { get; protected set; }
public delegate void CorrectionHandler(object sender, CorrectionData data);
public event CorrectionHandler? Correction;
public ScrollAmountGenerator SetBaseAmount(int baseAmount) { BaseAmount = baseAmount; return this; }
public ScrollAmountGenerator SetJitterFactor(double jitterFactor) { FJitter = jitterFactor; return this; }
public ScrollAmountGenerator SetCorrectionLimit(int limit) { CorrectionLimit = limit; return this; }
public ScrollAmountGenerator SetAccuracyFactor(double accuracyFactor) { FAccuracy = accuracyFactor; return this; }
private void ToggleDirection()
{
Dir = Dir == Direction.Up ? Direction.Down : Direction.Up;
}
private void UpdateSpread(int baseAmount, double jitterFactor)
{
var offset = baseAmount * jitterFactor;
var amount = Math.Abs(Double.CreateChecked(baseAmount));
_bAmount = baseAmount;
_fJitter = jitterFactor;
Min = (int)(amount - offset);
Max = (int)(amount + offset);
}
protected override int Calc()
{
int value;
if (Generate(0, 100000) > (int)(FAccuracy * 100000))
value = Generate(Min, Max);
else value = BaseAmount;
if (Dir == Direction.Down)
{
value = -value;
}
CurrentAmount += value;
return value;
}
public override int Next()
{
if (Progress < 0)
{
Progress = 0;
return First();
}
if (!HasValues || IsZero(1.0 - Progress))
{
HasValues = false;
Progress = 1.0;
return Last();
}
else if (Progress > 1.0 && _nCorrections < CorrectionLimit)
{
ToggleDirection();
Correction?.Invoke(this, new CorrectionData { Dir = Dir, Order = ++_nCorrections });
}
return Calc();
}
public override ISequenceGenerator<int> Reset()
{
HasValues = true;
if ((_nCorrections & 1) != 0)
{
ToggleDirection();
}
_nCorrections = 0;
return this;
}
public ScrollAmountGenerator(int totalAmount, Direction dir)
: base(default, default)
{
TotalAmount = (dir == Direction.Up) ? totalAmount : -totalAmount;
Dir = dir;
UpdateSpread(BaseAmount, FJitter);
}
}
}