From 908a690316e2db726a0857cb88a7ca3da75d7b66 Mon Sep 17 00:00:00 2001 From: Nathan Gill Date: Fri, 21 Feb 2025 13:19:12 +0000 Subject: [PATCH] PowerToys Run Calculator: Add trigonometric angle unit conversion functions (#37475) * Added trig unit conversion macros to PowerToys Run Calculator plugin. * Added testing for unit conversions. * Removed debug messages. --- .../ExtendedCalculatorParserTests.cs | 87 +++++++++++++++++++ .../CalculateEngine.cs | 8 +- .../CalculateHelper.cs | 44 +++++++++- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index 594773c36b..efd6b06200 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -341,5 +341,92 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests Assert.IsNotNull(result); Assert.AreEqual(expectedResult, result); } + + [DataTestMethod] + [DataRow("rad(30)", "(180 / pi) * (30)")] + [DataRow("rad( 30 )", "(180 / pi) * ( 30 )")] + [DataRow("deg(30)", "(30)")] + [DataRow("grad(30)", "(9 / 10) * (30)")] + [DataRow("rad( 30)", "(180 / pi) * ( 30)")] + [DataRow("rad(30 )", "(180 / pi) * (30 )")] + [DataRow("rad( 30 )", "(180 / pi) * ( 30 )")] + [DataRow("rad(deg(30))", "(180 / pi) * ((30))")] + [DataRow("deg(rad(30))", "((180 / pi) * (30))")] + [DataRow("grad(rad(30))", "(9 / 10) * ((180 / pi) * (30))")] + [DataRow("rad(grad(30))", "(180 / pi) * ((9 / 10) * (30))")] + [DataRow("rad(30) + deg(45)", "(180 / pi) * (30) + (45)")] + [DataRow("sin(rad(30))", "sin((180 / pi) * (30))")] + [DataRow("cos( rad( 45 ) )", "cos( (180 / pi) * ( 45 ) )")] + [DataRow("tan(rad(grad(90)))", "tan((180 / pi) * ((9 / 10) * (90)))")] + [DataRow("rad(30) + rad(45)", "(180 / pi) * (30) + (180 / pi) * (45)")] + [DataRow("rad(30) * grad(90)", "(180 / pi) * (30) * (9 / 10) * (90)")] + [DataRow("rad(30)/rad(45)", "(180 / pi) * (30)/(180 / pi) * (45)")] + public void ExpandTrigConversions_Degrees(string input, string expectedResult) + { + // Call ExpandTrigConversions in degrees mode + string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Degrees); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(expectedResult, result); + } + + [DataTestMethod] + [DataRow("rad(30)", "(30)")] + [DataRow("rad( 30 )", "( 30 )")] + [DataRow("deg(30)", "(pi / 180) * (30)")] + [DataRow("grad(30)", "(pi / 200) * (30)")] + [DataRow("rad( 30)", "( 30)")] + [DataRow("rad(30 )", "(30 )")] + [DataRow("rad( 30 )", "( 30 )")] + [DataRow("rad(deg(30))", "((pi / 180) * (30))")] + [DataRow("deg(rad(30))", "(pi / 180) * ((30))")] + [DataRow("grad(rad(30))", "(pi / 200) * ((30))")] + [DataRow("rad(grad(30))", "((pi / 200) * (30))")] + [DataRow("rad(30) + deg(45)", "(30) + (pi / 180) * (45)")] + [DataRow("sin(rad(30))", "sin((30))")] + [DataRow("cos( rad( 45 ) )", "cos( ( 45 ) )")] + [DataRow("tan(rad(grad(90)))", "tan(((pi / 200) * (90)))")] + [DataRow("rad(30) + rad(45)", "(30) + (45)")] + [DataRow("rad(30) * grad(90)", "(30) * (pi / 200) * (90)")] + [DataRow("rad(30)/rad(45)", "(30)/(45)")] + public void ExpandTrigConversions_Radians(string input, string expectedResult) + { + // Call ExpandTrigConversions in radians mode + string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Radians); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(expectedResult, result); + } + + [DataTestMethod] + [DataRow("rad(30)", "(200 / pi) * (30)")] + [DataRow("rad( 30 )", "(200 / pi) * ( 30 )")] + [DataRow("deg(30)", "(10 / 9) * (30)")] + [DataRow("grad(30)", "(30)")] + [DataRow("rad( 30)", "(200 / pi) * ( 30)")] + [DataRow("rad(30 )", "(200 / pi) * (30 )")] + [DataRow("rad( 30 )", "(200 / pi) * ( 30 )")] + [DataRow("rad(deg(30))", "(200 / pi) * ((10 / 9) * (30))")] + [DataRow("deg(rad(30))", "(10 / 9) * ((200 / pi) * (30))")] + [DataRow("grad(rad(30))", "((200 / pi) * (30))")] + [DataRow("rad(grad(30))", "(200 / pi) * ((30))")] + [DataRow("rad(30) + deg(45)", "(200 / pi) * (30) + (10 / 9) * (45)")] + [DataRow("sin(rad(30))", "sin((200 / pi) * (30))")] + [DataRow("cos( rad( 45 ) )", "cos( (200 / pi) * ( 45 ) )")] + [DataRow("tan(rad(grad(90)))", "tan((200 / pi) * ((90)))")] + [DataRow("rad(30) + rad(45)", "(200 / pi) * (30) + (200 / pi) * (45)")] + [DataRow("rad(30) * grad(90)", "(200 / pi) * (30) * (90)")] + [DataRow("rad(30)/rad(45)", "(200 / pi) * (30)/(200 / pi) * (45)")] + public void ExpandTrigConversions_Gradians(string input, string expectedResult) + { + // Call ExpandTrigConversions in gradians mode + string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Gradians); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(expectedResult, result); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs index 9248e3ca89..ef7e84fbd8 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs @@ -59,8 +59,14 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator input = CalculateHelper.FixHumanMultiplicationExpressions(input); + // Get the user selected trigonometry unit + TrigMode trigMode = Main.GetTrigMode(); + // Modify trig functions depending on angle unit setting - input = CalculateHelper.UpdateTrigFunctions(input, Main.GetTrigMode()); + input = CalculateHelper.UpdateTrigFunctions(input, trigMode); + + // Expand conversions between trig units + input = CalculateHelper.ExpandTrigConversions(input, trigMode); var result = _magesEngine.Interpret(input); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index 95705a34ca..e35d706a26 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; +using static Microsoft.PowerToys.Run.Plugin.Calculator.CalculateEngine; namespace Microsoft.PowerToys.Run.Plugin.Calculator { @@ -18,6 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator @"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(\)|randi\s*\([^\)]|" + @"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" + @"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" + + @"rad\s*\(|deg\s*\(|grad\s*\(|" + /* trigonometry unit conversion macros */ @"pi|" + @"==|~=|&&|\|\||" + @"((-?(\d+(\.\d*)?)|-?(\.\d+))[Ee](-?\d+))|" + /* expression from CheckScientificNotation between parenthesis */ @@ -26,7 +28,9 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator RegexOptions.Compiled); private const string DegToRad = "(pi / 180) * "; + private const string DegToGrad = "(10 / 9) * "; private const string GradToRad = "(pi / 200) * "; + private const string GradToDeg = "(9 / 10) * "; private const string RadToDeg = "(180 / pi) * "; private const string RadToGrad = "(200 / pi) * "; @@ -266,10 +270,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator return input; } - public static string UpdateTrigFunctions(string input, CalculateEngine.TrigMode mode) + public static string UpdateTrigFunctions(string input, TrigMode mode) { string modifiedInput = input; - if (mode == CalculateEngine.TrigMode.Degrees) + if (mode == TrigMode.Degrees) { modifiedInput = ModifyTrigFunction(modifiedInput, "sin", DegToRad); modifiedInput = ModifyTrigFunction(modifiedInput, "cos", DegToRad); @@ -278,7 +282,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator modifiedInput = ModifyTrigFunction(modifiedInput, "arccos", RadToDeg); modifiedInput = ModifyTrigFunction(modifiedInput, "arctan", RadToDeg); } - else if (mode == CalculateEngine.TrigMode.Gradians) + else if (mode == TrigMode.Gradians) { modifiedInput = ModifyTrigFunction(modifiedInput, "sin", GradToRad); modifiedInput = ModifyTrigFunction(modifiedInput, "cos", GradToRad); @@ -290,5 +294,39 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator return modifiedInput; } + + private static string ModifyMathFunction(string input, string function, string modification) + { + // Create the pattern to match the function, opening bracket, and any spaces in between + string pattern = $@"{function}\s*\("; + return Regex.Replace(input, pattern, modification + "("); + } + + public static string ExpandTrigConversions(string input, TrigMode mode) + { + string modifiedInput = input; + + // Expand "rad", "deg" and "grad" to their respective conversions for the current trig unit + if (mode == TrigMode.Radians) + { + modifiedInput = ModifyMathFunction(modifiedInput, "deg", DegToRad); + modifiedInput = ModifyMathFunction(modifiedInput, "grad", GradToRad); + modifiedInput = ModifyMathFunction(modifiedInput, "rad", string.Empty); + } + else if (mode == TrigMode.Degrees) + { + modifiedInput = ModifyMathFunction(modifiedInput, "deg", string.Empty); + modifiedInput = ModifyMathFunction(modifiedInput, "grad", GradToDeg); + modifiedInput = ModifyMathFunction(modifiedInput, "rad", RadToDeg); + } + else if (mode == TrigMode.Gradians) + { + modifiedInput = ModifyMathFunction(modifiedInput, "deg", DegToGrad); + modifiedInput = ModifyMathFunction(modifiedInput, "grad", string.Empty); + modifiedInput = ModifyMathFunction(modifiedInput, "rad", RadToGrad); + } + + return modifiedInput; + } } }