diff --git a/Buttplug.Test/Client/ButtplugClientDeviceCommandTests.cs b/Buttplug.Test/Client/ButtplugClientDeviceCommandTests.cs index 4f2559cb..5600d076 100644 --- a/Buttplug.Test/Client/ButtplugClientDeviceCommandTests.cs +++ b/Buttplug.Test/Client/ButtplugClientDeviceCommandTests.cs @@ -29,5 +29,49 @@ public void TestNonRotatePercentRejectsNegativeValue() .Should() .Throw(); } + + [Test] + public void TestToStepValuePositive() + { + var p = PercentOrSteps.FromPercent(0.5); + Assert.AreEqual(50, p.ToStepValue(-100, 100)); + + p = PercentOrSteps.FromPercent(1.0); + Assert.AreEqual(100, p.ToStepValue(-100, 100)); + + p = PercentOrSteps.FromPercent(0.0); + Assert.AreEqual(0, p.ToStepValue(-100, 100)); + } + + [Test] + public void TestToStepValueNegative() + { + var p = PercentOrSteps.FromPercent(-0.5, -1.0); + // Math.Floor(-0.5 * -100 * -1) = Math.Floor(-0.5 * 100) = Math.Floor(-50) = -50 + Assert.AreEqual(-50, p.ToStepValue(-100, 100)); + + p = PercentOrSteps.FromPercent(-1.0, -1.0); + Assert.AreEqual(-100, p.ToStepValue(-100, 100)); + } + + [Test] + public void TestToStepValueAsymmetric() + { + var p = PercentOrSteps.FromPercent(0.555); + Assert.AreEqual(56, p.ToStepValue(-10, 100)); + + p = PercentOrSteps.FromPercent(-0.555, -1.0); + Assert.AreEqual(-6, p.ToStepValue(-10, 100)); + } + + [Test] + public void TestToStepValueSteps() + { + var s = PercentOrSteps.FromSteps(42); + Assert.AreEqual(42, s.ToStepValue(-100, 100)); + + s = PercentOrSteps.FromSteps(-42); + Assert.AreEqual(-42, s.ToStepValue(-100, 100)); + } } } diff --git a/Buttplug/Client/ButtplugClientDeviceCommand.cs b/Buttplug/Client/ButtplugClientDeviceCommand.cs index 9b384310..6384a13a 100644 --- a/Buttplug/Client/ButtplugClientDeviceCommand.cs +++ b/Buttplug/Client/ButtplugClientDeviceCommand.cs @@ -62,14 +62,25 @@ public static PercentOrSteps FromPercent(double percent, double minPercent = 0.0 /// /// Converts this value to an actual step value given a max step count. /// + /// The maximum number of steps in the negative direction. /// The maximum number of steps. /// The calculated step value. - public int ToStepValue(int maxSteps) + public int ToStepValue(int minSteps, int maxSteps) { if (_steps.HasValue) { return _steps.Value; } + + if (!_percent.HasValue) + { + return 0; + } + + if (_percent.Value < 0) + { + return (int)Math.Floor(_percent.Value * minSteps * -1); + } return (int)Math.Ceiling(_percent.Value * maxSteps); } } diff --git a/Buttplug/Client/ButtplugClientDeviceFeature.cs b/Buttplug/Client/ButtplugClientDeviceFeature.cs index 1aa2988c..62d585e8 100644 --- a/Buttplug/Client/ButtplugClientDeviceFeature.cs +++ b/Buttplug/Client/ButtplugClientDeviceFeature.cs @@ -108,14 +108,16 @@ public async Task RunOutputAsync(DeviceOutputCommand command, CancellationToken // Get the output definition to determine step range var outputDef = FeatureDefinition.GetOutput(command.OutputType); + int minSteps = (command.OutputType == OutputType.Rotate) ? -100 : 0; // Default if not specified int maxSteps = 100; // Default if not specified if (outputDef?.Value != null && outputDef.Value.Length >= 2) { + minSteps = outputDef.Value[0]; maxSteps = outputDef.Value[1]; } // Convert to actual step value - int actualValue = command.Value.ToStepValue(maxSteps); + int actualValue = command.Value.ToStepValue(minSteps, maxSteps); OutputCmd cmd; if (command.OutputType == OutputType.HwPositionWithDuration)