Skip to content

Commit

Permalink
Add PreemptionComputer interface and it's implementations: SimplePree…
Browse files Browse the repository at this point in the history
…mptionComputer and StabilizedPreemptionComputer;

Conditionally register PreemptionComputerin HotKeyProfileRunner;
  • Loading branch information
Neakita committed Oct 19, 2023
1 parent 57e3e10 commit 533842d
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 20 deletions.
16 changes: 16 additions & 0 deletions SightKeeper.Commons/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommunityToolkit.Diagnostics;

namespace SightKeeper.Commons;

public static class EnumerableExtensions
{
public static float Median(this IEnumerable<float> values)
{
var sortedValues = values.Order().ToList();
Guard.IsNotEmpty(sortedValues);
var middle = sortedValues.Count / 2;
if (sortedValues.Count % 2 == 0)
return (sortedValues[middle - 1] + sortedValues[middle]) / 2;
return sortedValues[middle];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Numerics;

namespace SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators.Preemption;

public interface PreemptionComputer
{
Vector2 ComputePreemption(Vector2 moveVector, TimeSpan timeDelta);
void Reset();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Numerics;
using CommunityToolkit.Diagnostics;
using Serilog;
using SightKeeper.Domain.Model;

namespace SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators.Preemption;

public sealed class SimplePreemptionComputer : PreemptionComputer
{
public SimplePreemptionComputer(Profile profile)
{
Guard.IsNotNull(profile.PreemptionSettings);
_preemptionFactor = new Vector2(profile.PreemptionSettings.HorizontalFactor, profile.PreemptionSettings.VerticalFactor);
}

public Vector2 ComputePreemption(Vector2 moveVector, TimeSpan timeDelta)
{
var targetVelocity = moveVector + _previousPreemption;
targetVelocity /= (float)timeDelta.TotalMilliseconds;
var preemption = targetVelocity * BasePreemptionFactor * _preemptionFactor;
Log.ForContext<SimplePreemptionComputer>().Debug("Preemption is {Preemption}", preemption);
_previousPreemption = preemption;
return preemption;
}

public void Reset()
{
_previousPreemption = Vector2.Zero;
}

private const float BasePreemptionFactor = 100;
private readonly Vector2 _preemptionFactor;
private Vector2 _previousPreemption = Vector2.Zero;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Numerics;
using CommunityToolkit.Diagnostics;
using Serilog;
using SightKeeper.Commons;
using SightKeeper.Domain.Model;

namespace SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators.Preemption;

public sealed class StabilizedPreemptionComputer : PreemptionComputer
{
public StabilizedPreemptionComputer(Profile profile)
{
Guard.IsNotNull(profile.PreemptionSettings?.StabilizationSettings);
_preemptionFactor = new Vector2(profile.PreemptionSettings.HorizontalFactor, profile.PreemptionSettings.VerticalFactor);
_velocities = new List<Vector2>(profile.PreemptionSettings.StabilizationSettings.BufferSize);
_method = profile.PreemptionSettings.StabilizationSettings.Method switch
{
PreemptionStabilizationMethod.Median => EnumerableExtensions.Median,
PreemptionStabilizationMethod.Mean => Enumerable.Average,
_ => ThrowHelper.ThrowArgumentOutOfRangeException<Func<IEnumerable<float>, float>>()
};
}

public Vector2 ComputePreemption(Vector2 moveVector, TimeSpan timeDelta)
{
var targetVelocity = moveVector + _previousPreemption;
targetVelocity /= (float)timeDelta.TotalMilliseconds;

if (_velocities.Count == _velocities.Capacity)
_velocities.RemoveAt(0);
_velocities.Add(targetVelocity);

Vector2 preemption = new(
_method(_velocities.Select(velocity => velocity.X)),
_method(_velocities.Select(velocity => velocity.Y)));

preemption *= BasePreemptionFactor * _preemptionFactor;
Log.ForContext<StabilizedPreemptionComputer>().Debug("Preemption is {Preemption}", preemption);
_previousPreemption = preemption;
return preemption;
}

public void Reset()
{
_previousPreemption = Vector2.Zero;
}

private const float BasePreemptionFactor = 100;
private readonly Vector2 _preemptionFactor;
private readonly List<Vector2> _velocities;
private readonly Func<IEnumerable<float>, float> _method;
private Vector2 _previousPreemption = Vector2.Zero;
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
using System.Numerics;
using CommunityToolkit.Diagnostics;
using Serilog;
using SightKeeper.Domain.Model;
using SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators.Preemption;

namespace SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators;

public sealed class PreemptionDecorator : DetectionMouseMover
{
public PreemptionDecorator(DetectionMouseMover mouseMover, Profile profile)
public PreemptionDecorator(DetectionMouseMover mouseMover, PreemptionComputer preemptionComputer)
{
Guard.IsNotNull(profile.PreemptionSettings);
_mouseMover = mouseMover;
_preemptionFactor = new Vector2(profile.PreemptionSettings.HorizontalFactor, profile.PreemptionSettings.VerticalFactor);
_preemptionComputer = preemptionComputer;
}

public void Move(MouseMovingContext context, Vector2 vector)
Expand All @@ -20,35 +18,23 @@ public void Move(MouseMovingContext context, Vector2 vector)
var timeDelta = now - _previousMoveTime;
var preemption = Vector2.Zero;
if (!_isFirstMove)
preemption = ComputePreemption(vector, timeDelta);
preemption = _preemptionComputer.ComputePreemption(vector, timeDelta);
else
_isFirstMove = false;

_mouseMover.Move(context, vector + preemption);
_previousMoveTime = now;
}

private Vector2 ComputePreemption(Vector2 moveVector, TimeSpan timeDelta)
{
var targetVelocity = moveVector + _previousPreemption;
targetVelocity /= (float)timeDelta.TotalMilliseconds;
var preemption = targetVelocity * BasePreemptionFactor * _preemptionFactor;
Log.ForContext<PreemptionDecorator>().Debug("Preemption is {Preemption}", preemption);
_previousPreemption = preemption;
return preemption;
}

public void OnPaused()
{
_isFirstMove = true;
_previousPreemption = Vector2.Zero;
_preemptionComputer.Reset();
Log.ForContext<PreemptionDecorator>().Debug("State cleared");
}

private const float BasePreemptionFactor = 100;
private readonly DetectionMouseMover _mouseMover;
private readonly Vector2 _preemptionFactor;
private readonly PreemptionComputer _preemptionComputer;
private bool _isFirstMove = true;
private DateTime _previousMoveTime;
private Vector2 _previousPreemption = Vector2.Zero;
}
7 changes: 7 additions & 0 deletions SightKeeper.Services/Prediction/HotKeyProfileRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using SightKeeper.Services.Prediction.Handling;
using SightKeeper.Services.Prediction.Handling.MouseMoving;
using SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators;
using SightKeeper.Services.Prediction.Handling.MouseMoving.Decorators.Preemption;

namespace SightKeeper.Services.Prediction;

Expand Down Expand Up @@ -85,7 +86,13 @@ private static void BuildScope(ContainerBuilder builder, Profile profile)
builder.RegisterInstance(profile);
builder.RegisterInstance(profile.Weights.Library.DataSet);
if (profile.PreemptionSettings != null)
{
builder.RegisterDecorator<PreemptionDecorator, DetectionMouseMover>();
if (profile.PreemptionSettings.StabilizationSettings == null)
builder.RegisterType<SimplePreemptionComputer>().As<PreemptionComputer>();
else
builder.RegisterType<StabilizedPreemptionComputer>().As<PreemptionComputer>();
}
builder.RegisterComposite<CompositeDetectionObserver, DetectionObserver>();
}

Expand Down

0 comments on commit 533842d

Please sign in to comment.