mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
[PTRun]Add setting for different trigonometric units in Calculator (#36717)
* Added angle units to PowerToys Run Calculator plugin. * Update Resources.resx * Added GitHub SpellCheck rule for 'gradians'. --------- Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
This commit is contained in:
@@ -279,5 +279,67 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(expectedResult, result.Result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("sin(90)", "sin((pi / 180) * (90))")]
|
||||
[DataRow("arcsin(0.5)", "(180 / pi) * (arcsin(0.5))")]
|
||||
[DataRow("sin(sin(30))", "sin((pi / 180) * (sin((pi / 180) * (30))))")]
|
||||
[DataRow("cos(tan(45))", "cos((pi / 180) * (tan((pi / 180) * (45))))")]
|
||||
[DataRow("arctan(sin(30))", "(180 / pi) * (arctan(sin((pi / 180) * (30))))")]
|
||||
[DataRow("sin(cos(tan(30)))", "sin((pi / 180) * (cos((pi / 180) * (tan((pi / 180) * (30))))))")]
|
||||
[DataRow("sin(arcsin(0.5))", "sin((pi / 180) * ((180 / pi) * (arcsin(0.5))))")]
|
||||
[DataRow("sin(30) + cos(60)", "sin((pi / 180) * (30)) + cos((pi / 180) * (60))")]
|
||||
[DataRow("sin(30 + 15)", "sin((pi / 180) * (30 + 15))")]
|
||||
[DataRow("sin(45) * cos(45) - tan(30)", "sin((pi / 180) * (45)) * cos((pi / 180) * (45)) - tan((pi / 180) * (30))")]
|
||||
[DataRow("arcsin(arccos(0.5))", "(180 / pi) * (arcsin((180 / pi) * (arccos(0.5))))")]
|
||||
[DataRow("sin(sin(sin(30)))", "sin((pi / 180) * (sin((pi / 180) * (sin((pi / 180) * (30))))))")]
|
||||
[DataRow("log(10)", "log(10)")]
|
||||
[DataRow("sin(30) + pi", "sin((pi / 180) * (30)) + pi")]
|
||||
[DataRow("sin(-30)", "sin((pi / 180) * (-30))")]
|
||||
[DataRow("sin((30))", "sin((pi / 180) * ((30)))")]
|
||||
[DataRow("arcsin(1) * 2", "(180 / pi) * (arcsin(1)) * 2")]
|
||||
[DataRow("cos(1/2)", "cos((pi / 180) * (1/2))")]
|
||||
[DataRow("sin ( 90 )", "sin ((pi / 180) * ( 90 ))")]
|
||||
[DataRow("cos(arcsin(sin(45)))", "cos((pi / 180) * ((180 / pi) * (arcsin(sin((pi / 180) * (45))))))")]
|
||||
public void UpdateTrigFunctions_Degrees(string input, string expectedResult)
|
||||
{
|
||||
// Call UpdateTrigFunctions in degrees mode
|
||||
string result = CalculateHelper.UpdateTrigFunctions(input, CalculateEngine.TrigMode.Degrees);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("sin(90)", "sin((pi / 200) * (90))")]
|
||||
[DataRow("arcsin(0.5)", "(200 / pi) * (arcsin(0.5))")]
|
||||
[DataRow("sin(sin(30))", "sin((pi / 200) * (sin((pi / 200) * (30))))")]
|
||||
[DataRow("cos(tan(45))", "cos((pi / 200) * (tan((pi / 200) * (45))))")]
|
||||
[DataRow("arctan(sin(30))", "(200 / pi) * (arctan(sin((pi / 200) * (30))))")]
|
||||
[DataRow("sin(cos(tan(30)))", "sin((pi / 200) * (cos((pi / 200) * (tan((pi / 200) * (30))))))")]
|
||||
[DataRow("sin(arcsin(0.5))", "sin((pi / 200) * ((200 / pi) * (arcsin(0.5))))")]
|
||||
[DataRow("sin(30) + cos(60)", "sin((pi / 200) * (30)) + cos((pi / 200) * (60))")]
|
||||
[DataRow("sin(30 + 15)", "sin((pi / 200) * (30 + 15))")]
|
||||
[DataRow("sin(45) * cos(45) - tan(30)", "sin((pi / 200) * (45)) * cos((pi / 200) * (45)) - tan((pi / 200) * (30))")]
|
||||
[DataRow("arcsin(arccos(0.5))", "(200 / pi) * (arcsin((200 / pi) * (arccos(0.5))))")]
|
||||
[DataRow("sin(sin(sin(30)))", "sin((pi / 200) * (sin((pi / 200) * (sin((pi / 200) * (30))))))")]
|
||||
[DataRow("log(10)", "log(10)")]
|
||||
[DataRow("sin(30) + pi", "sin((pi / 200) * (30)) + pi")]
|
||||
[DataRow("sin(-30)", "sin((pi / 200) * (-30))")]
|
||||
[DataRow("sin((30))", "sin((pi / 200) * ((30)))")]
|
||||
[DataRow("arcsin(1) * 2", "(200 / pi) * (arcsin(1)) * 2")]
|
||||
[DataRow("cos(1/2)", "cos((pi / 200) * (1/2))")]
|
||||
[DataRow("sin ( 90 )", "sin ((pi / 200) * ( 90 ))")]
|
||||
[DataRow("cos(arcsin(sin(45)))", "cos((pi / 200) * ((200 / pi) * (arcsin(sin((pi / 200) * (45))))))")]
|
||||
public void UpdateTrigFunctions_Gradians(string input, string expectedResult)
|
||||
{
|
||||
// Call UpdateTrigFunctions in gradians mode
|
||||
string result = CalculateHelper.UpdateTrigFunctions(input, CalculateEngine.TrigMode.Gradians);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,13 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
|
||||
public const int RoundingDigits = 10;
|
||||
|
||||
public enum TrigMode
|
||||
{
|
||||
Radians,
|
||||
Degrees,
|
||||
Gradians,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpret
|
||||
/// </summary>
|
||||
@@ -52,6 +59,9 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
|
||||
input = CalculateHelper.FixHumanMultiplicationExpressions(input);
|
||||
|
||||
// Modify trig functions depending on angle unit setting
|
||||
input = CalculateHelper.UpdateTrigFunctions(input, Main.GetTrigMode());
|
||||
|
||||
var result = _magesEngine.Interpret(input);
|
||||
|
||||
// This could happen for some incorrect queries, like pi(2)
|
||||
|
||||
@@ -25,6 +25,11 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
@")+$",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private const string DegToRad = "(pi / 180) * ";
|
||||
private const string GradToRad = "(pi / 200) * ";
|
||||
private const string RadToDeg = "(180 / pi) * ";
|
||||
private const string RadToGrad = "(200 / pi) * ";
|
||||
|
||||
public static bool InputValid(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
@@ -204,5 +209,86 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Gets the index of the closing bracket of a function
|
||||
private static int FindClosingBracketIndex(string input, int start)
|
||||
{
|
||||
int bracketCount = 0; // Set count to zero
|
||||
for (int i = start; i < input.Length; i++)
|
||||
{
|
||||
if (input[i] == '(')
|
||||
{
|
||||
bracketCount++;
|
||||
}
|
||||
else if (input[i] == ')')
|
||||
{
|
||||
bracketCount--;
|
||||
if (bracketCount == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // Unmatched brackets
|
||||
}
|
||||
|
||||
private static string ModifyTrigFunction(string input, string function, string modification)
|
||||
{
|
||||
// Get the RegEx pattern to match, depending on whether the function is inverse or normal
|
||||
string pattern = function.StartsWith("arc", StringComparison.Ordinal) ? string.Empty : @"(?<!c)";
|
||||
pattern += $@"{function}\s*\(";
|
||||
|
||||
int index = 0; // Index for match to ensure that the same match is not found twice
|
||||
|
||||
Regex regex = new Regex(pattern);
|
||||
Match match;
|
||||
|
||||
while ((match = regex.Match(input, index)).Success)
|
||||
{
|
||||
index = match.Index + match.Groups[0].Length + modification.Length; // Get the next index to look from for further matches
|
||||
|
||||
int endIndex = FindClosingBracketIndex(input, match.Index + match.Groups[0].Length - 1); // Find the index of the closing bracket of the function
|
||||
|
||||
// If no valid bracket index was found, try the next match
|
||||
if (endIndex == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string argument = input.Substring(match.Index + match.Groups[0].Length, endIndex - (match.Index + match.Groups[0].Length)); // Extract the argument between the brackets
|
||||
string replaced = function.StartsWith("arc", StringComparison.Ordinal) ? $"{modification}({match.Groups[0].Value}{argument}))" : $"{match.Groups[0].Value}{modification}({argument}))"; // The string to substitute in, handles differing formats of inverse functions
|
||||
|
||||
input = input.Remove(match.Index, endIndex - match.Index + 1); // Remove the match from the input
|
||||
input = input.Insert(match.Index, replaced); // Substitute with the new string
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public static string UpdateTrigFunctions(string input, CalculateEngine.TrigMode mode)
|
||||
{
|
||||
string modifiedInput = input;
|
||||
if (mode == CalculateEngine.TrigMode.Degrees)
|
||||
{
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "sin", DegToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "cos", DegToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "tan", DegToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arcsin", RadToDeg);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arccos", RadToDeg);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arctan", RadToDeg);
|
||||
}
|
||||
else if (mode == CalculateEngine.TrigMode.Gradians)
|
||||
{
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "sin", GradToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "cos", GradToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "tan", GradToRad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arcsin", RadToGrad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arccos", RadToGrad);
|
||||
modifiedInput = ModifyTrigFunction(modifiedInput, "arctan", RadToGrad);
|
||||
}
|
||||
|
||||
return modifiedInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
private const string InputUseEnglishFormat = nameof(InputUseEnglishFormat);
|
||||
private const string OutputUseEnglishFormat = nameof(OutputUseEnglishFormat);
|
||||
private const string ReplaceInput = nameof(ReplaceInput);
|
||||
private const string TrigMode = nameof(TrigMode);
|
||||
|
||||
private static readonly CalculateEngine CalculateEngine = new CalculateEngine();
|
||||
|
||||
@@ -31,6 +32,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
private bool _inputUseEnglishFormat;
|
||||
private bool _outputUseEnglishFormat;
|
||||
private bool _replaceInput;
|
||||
private static CalculateEngine.TrigMode _trigMode;
|
||||
|
||||
public string Name => Resources.wox_plugin_calculator_plugin_name;
|
||||
|
||||
@@ -67,6 +69,20 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
DisplayDescription = Resources.wox_plugin_calculator_replace_input_description,
|
||||
Value = true,
|
||||
},
|
||||
new PluginAdditionalOption
|
||||
{
|
||||
Key = TrigMode,
|
||||
DisplayLabel = Resources.wox_plugin_calculator_trig_unit_mode,
|
||||
DisplayDescription = Resources.wox_plugin_calculator_trig_unit_mode_description,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxValue = (int)CalculateEngine.TrigMode.Radians,
|
||||
ComboBoxItems =
|
||||
[
|
||||
new KeyValuePair<string, string>(Resources.wox_plugin_calculator_trig_unit_radians, "0"),
|
||||
new KeyValuePair<string, string>(Resources.wox_plugin_calculator_trig_unit_degrees, "1"),
|
||||
new KeyValuePair<string, string>(Resources.wox_plugin_calculator_trig_unit_gradians, "2"),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
public List<Result> Query(Query query)
|
||||
@@ -183,6 +199,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
var inputUseEnglishFormat = false;
|
||||
var outputUseEnglishFormat = false;
|
||||
var replaceInput = true;
|
||||
var trigMode = CalculateEngine.TrigMode.Radians;
|
||||
|
||||
if (settings != null && settings.AdditionalOptions != null)
|
||||
{
|
||||
@@ -194,11 +211,20 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
||||
|
||||
var optionReplaceInput = settings.AdditionalOptions.FirstOrDefault(x => x.Key == ReplaceInput);
|
||||
replaceInput = optionReplaceInput?.Value ?? replaceInput;
|
||||
|
||||
var optionTrigMode = settings.AdditionalOptions.FirstOrDefault(x => x.Key == TrigMode);
|
||||
trigMode = (CalculateEngine.TrigMode)int.Parse(optionTrigMode.ComboBoxValue.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
_inputUseEnglishFormat = inputUseEnglishFormat;
|
||||
_outputUseEnglishFormat = outputUseEnglishFormat;
|
||||
_replaceInput = replaceInput;
|
||||
_trigMode = trigMode;
|
||||
}
|
||||
|
||||
public static CalculateEngine.TrigMode GetTrigMode()
|
||||
{
|
||||
return _trigMode;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -203,5 +203,50 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.Properties {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_replace_input_description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Degrees.
|
||||
/// </summary>
|
||||
public static string wox_plugin_calculator_trig_unit_degrees {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_trig_unit_degrees", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Gradians.
|
||||
/// </summary>
|
||||
public static string wox_plugin_calculator_trig_unit_gradians {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_trig_unit_gradians", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Trigonometry Unit.
|
||||
/// </summary>
|
||||
public static string wox_plugin_calculator_trig_unit_mode {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_trig_unit_mode", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Specifies the angle unit to use for trigonometry operations..
|
||||
/// </summary>
|
||||
public static string wox_plugin_calculator_trig_unit_mode_description {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_trig_unit_mode_description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Radians.
|
||||
/// </summary>
|
||||
public static string wox_plugin_calculator_trig_unit_radians {
|
||||
get {
|
||||
return ResourceManager.GetString("wox_plugin_calculator_trig_unit_radians", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,4 +167,24 @@
|
||||
<data name="wox_plugin_calculator_replace_input_description" xml:space="preserve">
|
||||
<value>When using direct activation, appending '=' to the expression will replace the input with the calculated result (e.g. '=5*3-2=' will change the query to '=13').</value>
|
||||
</data>
|
||||
<data name="wox_plugin_calculator_trig_unit_mode" xml:space="preserve">
|
||||
<value>Trigonometry Unit</value>
|
||||
<comment>Title text for trig unit mode.</comment>
|
||||
</data>
|
||||
<data name="wox_plugin_calculator_trig_unit_mode_description" xml:space="preserve">
|
||||
<value>Specifies the angle unit to use for trigonometry operations</value>
|
||||
<comment>Description text for trig mode setting.</comment>
|
||||
</data>
|
||||
<data name="wox_plugin_calculator_trig_unit_radians" xml:space="preserve">
|
||||
<value>Radians</value>
|
||||
<comment>Text for angle unit.</comment>
|
||||
</data>
|
||||
<data name="wox_plugin_calculator_trig_unit_degrees" xml:space="preserve">
|
||||
<value>Degrees</value>
|
||||
<comment>Text to use for angle unit.</comment>
|
||||
</data>
|
||||
<data name="wox_plugin_calculator_trig_unit_gradians" xml:space="preserve">
|
||||
<value>Gradians</value>
|
||||
<comment>Text for angle unit.</comment>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user