Change motivity type to use syncrhonous framework

This commit is contained in:
Jacob Palecki 2024-07-11 01:09:33 -07:00
parent 7427404ea0
commit 428f4e8b49
8 changed files with 163 additions and 23 deletions

View file

@ -8,21 +8,67 @@ namespace rawaccel {
template <>
struct loglog_sigmoid<LEGACY> {
double accel;
double motivity;
double midpoint;
double constant;
double log_motivity;
double gamma_const;
double log_syncspeed;
double syncspeed;
double sharpness;
double sharpness_recip;
bool use_linear_clamp;
double minimum_sens;
double maximum_sens;
loglog_sigmoid(const accel_args& args) :
accel(exp(args.growth_rate)),
motivity(2 * log(args.motivity)),
midpoint(log(args.midpoint)),
constant(-motivity / 2) {}
log_motivity(log(args.motivity)),
gamma_const(args.gamma / log_motivity),
log_syncspeed(log(args.sync_speed)),
syncspeed(args.sync_speed),
sharpness(args.smooth == 0 ? 16 : 0.5 / args.smooth),
sharpness_recip(1 / sharpness),
use_linear_clamp(sharpness >= 16),
minimum_sens(1 / args.motivity),
maximum_sens(args.motivity) {}
double operator()(double x, const accel_args&) const
{
double denom = exp(accel * (midpoint - log(x))) + 1;
return exp(motivity / denom + constant);
// if sharpness >= 16, use linear clamp for activation function.
// linear clamp means: clamp(x, -1, 1).
if (use_linear_clamp)
{
double log_space = gamma_const * (log(x) - log_syncspeed);
if (log_space < -1)
{
return minimum_sens;
}
if (log_space > 1)
{
return maximum_sens;
}
return exp(log_space * log_motivity);
}
if (x == syncspeed) {
return 1.0;
}
double log_x = log(x);
double log_diff = log_x - log_syncspeed;
if (log_diff > 0)
{
double log_space = gamma_const * log_diff;
double exponent = pow(tanh(pow(log_space, sharpness)), sharpness_recip);
return exp(exponent * log_motivity);
}
else
{
double log_space = -gamma_const * log_diff;
double exponent = -pow(tanh(pow(log_space, sharpness)), sharpness_recip);
return exp(exponent * log_motivity);
}
}
};

View file

@ -49,13 +49,13 @@ namespace rawaccel {
double output_offset = 0;
double acceleration = 0.005;
double decay_rate = 0.1;
double growth_rate = 1;
double gamma = 1;
double motivity = 1.5;
double exponent_classic = 2;
double scale = 1;
double exponent_power = 0.05;
double limit = 1.5;
double midpoint = 5;
double sync_speed = 5;
double smooth = 0.5;
vec2d cap = { 15, 1.5 };

View file

@ -96,8 +96,8 @@ namespace rawaccel {
error("scale"" must be positive");
}
if (args.growth_rate <= 0) {
error("growth rate"" must be positive");
if (args.gamma <= 0) {
error("gamma"" must be positive");
}
if (args.decay_rate <= 0) {
@ -120,8 +120,8 @@ namespace rawaccel {
error("limit"" must be positive");
}
if (args.midpoint <= 0) {
error("midpoint"" must be positive");
if (args.sync_speed <= 0) {
error("synchronous speed"" must be positive");
}
if (args.smooth < 0 || args.smooth > 1) {

View file

@ -420,7 +420,6 @@ namespace rawaccel {
if (flags.apply_dir_mul_y && in.y < 0) {
in.y *= args.ud_output_dpi_ratio;
}
}
modifier(modifier_settings& settings)

View file

@ -281,12 +281,12 @@ namespace grapher
OutputOffset.SetActiveValue(args.outputOffset);
InputOffset.SetActiveValue(args.inputOffset);
DecayRate.SetActiveValue(args.decayRate);
GrowthRate.SetActiveValue(args.growthRate);
GrowthRate.SetActiveValue(args.gamma);
Smooth.SetActiveValue(args.smooth);
Limit.SetActiveValue((args.mode == AccelMode.motivity) ? args.motivity : args.limit);
PowerClassic.SetActiveValue(args.exponentClassic);
Exponent.SetActiveValue(args.exponentPower);
Midpoint.SetActiveValue(args.midpoint);
Midpoint.SetActiveValue(args.syncSpeed);
LutPanel.SetActiveValues(args.data, args.length, args.mode);
LutApply.SetActiveValue(args.gain);
}
@ -326,7 +326,7 @@ namespace grapher
GainSwitch.CheckBox.Checked;
if (DecayRate.Visible) args.decayRate = DecayRate.Field.Data;
if (GrowthRate.Visible) args.growthRate = GrowthRate.Field.Data;
if (GrowthRate.Visible) args.gamma = GrowthRate.Field.Data;
if (Smooth.Visible) args.smooth = Smooth.Field.Data;
if (ClassicCap.Visible)
{
@ -360,7 +360,7 @@ namespace grapher
if (InputOffset.Visible) args.inputOffset = InputOffset.Field.Data;
if (OutputOffset.Visible) args.outputOffset = OutputOffset.Field.Data;
if (Midpoint.Visible) args.midpoint = Midpoint.Field.Data;
if (Midpoint.Visible) args.syncSpeed = Midpoint.Field.Data;
if (LutPanel.Visible)
{
(var points, var length) = LutPanel.GetPoints();

View file

@ -0,0 +1,94 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
namespace wrapper_tests
{
[TestClass]
public class SynchronousAccelTests
{
[TestMethod]
public void GivenSpeeds_SynchronousAccel_YieldsCorrectSens()
{
double syncSpeed = 20;
double gamma = 0.5;
double motivity = 1.3;
double smooth = 0.5;
var profile = new Profile();
profile.outputDPI = 1000;
profile.argsX.mode = AccelMode.motivity;
profile.argsX.gain = false;
profile.argsX.syncSpeed = syncSpeed;
profile.argsX.gamma = gamma;
profile.argsX.motivity = motivity;
profile.argsX.smooth = smooth;
var accel = new ManagedAccel(profile);
var accelSimulator = new SynchronousAccelSimulator(syncSpeed, motivity, gamma, smooth);
List<int> inputs = new List<int>()
{
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181,
};
foreach (int input in inputs)
{
Tuple<double, double> output = accel.Accelerate(input, 0, 1, 10);
double expectedOutput = input * accelSimulator.Accelerate(input / 10.0);
Assert.AreEqual(expectedOutput, output.Item1, expectedOutput * 0.0001);
}
}
}
/// <summary>
/// Contains definition of how synchronous accel is expected to accelerate inputs
/// No optimization tricks are used for clarity of behavior.
/// </summary>
public class SynchronousAccelSimulator
{
public SynchronousAccelSimulator(
double syncSpeed,
double motivity,
double gamma,
double smooth)
{
SyncSpeed = syncSpeed;
Motivity = motivity;
Gamma = gamma;
Sharpness = smooth <= 0 ? 16 : 0.5 / smooth;
}
public double SyncSpeed { get; }
public double Motivity { get; }
public double Gamma { get; }
public double Sharpness { get; }
public double Accelerate(double inputSpeed)
{
double logSpace = CalculateLogSpace(inputSpeed);
double activation = ActivationFunction(logSpace);
return Math.Pow(Motivity, activation);
}
public double CalculateLogSpace(double x)
{
double syncRatio = x / SyncSpeed;
double logSpaceUnadjusted = Math.Log(syncRatio, Motivity);
double gammaAdjusted = Gamma * logSpaceUnadjusted;
return gammaAdjusted;
}
public double ActivationFunction(double x)
{
if (Sharpness >= 16)
{
return Math.Min(1, Math.Max(-1, x));
}
return Math.Sign(x) * Math.Pow(Math.Tanh(Math.Pow(Math.Abs(x), Sharpness)), 1 / Sharpness);
}
}
}

View file

@ -53,6 +53,7 @@
<Compile Include="EndToEndTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SpeedTests.cs" />
<Compile Include="SynchronousAccelTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View file

@ -68,13 +68,13 @@ public value struct AccelArgs
double outputOffset;
double acceleration;
double decayRate;
double growthRate;
double gamma;
double motivity;
double exponentClassic;
double scale;
double exponentPower;
double limit;
double midpoint;
double syncSpeed;
double smooth;
[JsonProperty("Cap / Jump")]