From 9e38a01acc45dca23eb031c6fa7baf0c5958dca4 Mon Sep 17 00:00:00 2001 From: nothingTVatYT <34131388+nothingTVatYT@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:52:25 +0100 Subject: [PATCH] add units support in float, double and Float3 input --- .../CustomEditors/Editors/DoubleEditor.cs | 5 ++ .../CustomEditors/Editors/FloatEditor.cs | 5 ++ .../CustomEditors/Editors/Vector3Editor.cs | 15 +++++ .../Elements/DoubleValueElement.cs | 10 +++ .../Elements/FloatValueElement.cs | 10 +++ Source/Editor/GUI/Input/DoubleValueBox.cs | 8 ++- Source/Editor/GUI/Input/FloatValueBox.cs | 8 ++- Source/Editor/Utilities/ShuntingYardParser.cs | 17 ++++- Source/Editor/Utilities/Units.cs | 9 +++ Source/Editor/Utilities/Utils.cs | 64 ++++++++++++++++++- .../Editor/ValueCategoryAttribute.cs | 35 ++++++++++ .../Flax.Build.Tests/Flax.Build.Tests.csproj | 2 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 - 13 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 Source/Editor/Utilities/Units.cs create mode 100644 Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs index c490046b9..0906d22af 100644 --- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -25,6 +26,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var limit = attributes.FirstOrDefault(x => x is LimitAttribute); @@ -32,6 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor with limit var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.SetLimits((LimitAttribute)limit); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; @@ -43,6 +47,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use double value editor var doubleValue = layout.DoubleValue(); + doubleValue.SetCategory(valueCategory); doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.SlidingEnd += ClearToken; _element = doubleValue; diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 07c07030e..525541e99 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -30,6 +31,8 @@ namespace FlaxEditor.CustomEditors.Editors // Try get limit attribute for value min/max range setting and slider speed var attributes = Values.GetAttributes(); + var categoryAttribute = attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + var valueCategory = ((ValueCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None; if (attributes != null) { var range = attributes.FirstOrDefault(x => x is RangeAttribute); @@ -49,6 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors // Use float value editor with limit var floatValue = layout.FloatValue(); floatValue.SetLimits((LimitAttribute)limit); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; @@ -59,6 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors { // Use float value editor var floatValue = layout.FloatValue(); + floatValue.SetCategory(valueCategory); floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.SlidingEnd += ClearToken; _element = floatValue; diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index c3edd3913..9826c339f 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -4,6 +4,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Editors { @@ -70,23 +71,30 @@ namespace FlaxEditor.CustomEditors.Editors LimitAttribute limit = null; var attributes = Values.GetAttributes(); + var category = Utils.ValueCategory.None; if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.FloatValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnXValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.FloatValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnYValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.FloatValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } @@ -248,24 +256,31 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotsVertically = 1; LimitAttribute limit = null; + Utils.ValueCategory category = Utils.ValueCategory.None; var attributes = Values.GetAttributes(); if (attributes != null) { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + var categoryAttribute = (ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute); + if (categoryAttribute != null) + category = categoryAttribute.Category; } XElement = grid.DoubleValue(); XElement.SetLimits(limit); + XElement.SetCategory(category); XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.SlidingEnd += ClearToken; YElement = grid.DoubleValue(); YElement.SetLimits(limit); + YElement.SetCategory(category); YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.SlidingEnd += ClearToken; ZElement = grid.DoubleValue(); ZElement.SetLimits(limit); + ZElement.SetCategory(category); ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.SlidingEnd += ClearToken; } diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs index 07af5e991..460a344ff 100644 --- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs index 789d8966e..413a341c1 100644 --- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs +++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs @@ -5,6 +5,7 @@ using System.Reflection; using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.CustomEditors.Elements { @@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements } } + /// + /// Set the value category of this float element + /// + /// + public void SetCategory(Utils.ValueCategory category) + { + ValueBox.Category = category; + } + /// /// Sets the editor limits from member . /// diff --git a/Source/Editor/GUI/Input/DoubleValueBox.cs b/Source/Editor/GUI/Input/DoubleValueBox.cs index 0ceb10114..46e6402cb 100644 --- a/Source/Editor/GUI/Input/DoubleValueBox.cs +++ b/Source/Editor/GUI/Input/DoubleValueBox.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -129,10 +130,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/GUI/Input/FloatValueBox.cs b/Source/Editor/GUI/Input/FloatValueBox.cs index cdb3b08b1..64ea000fa 100644 --- a/Source/Editor/GUI/Input/FloatValueBox.cs +++ b/Source/Editor/GUI/Input/FloatValueBox.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using FlaxEditor.Utilities; using FlaxEngine; +using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.GUI.Input { @@ -137,10 +138,15 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Get or set the category of the value. This can either be none for just a number, a distance or an angle. + /// + public Utils.ValueCategory Category = Utils.ValueCategory.None; + /// protected sealed override void UpdateText() { - SetText(Utilities.Utils.FormatFloat(_value)); + SetText(Utilities.Utils.FormatFloat(_value, Category)); } /// diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index d139b003a..e7c5b5298 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -121,6 +121,9 @@ namespace FlaxEditor.Utilities ["e"] = Math.E, ["infinity"] = double.MaxValue, ["-infinity"] = -double.MaxValue, + ["m"] = Units.Meters2Units, + ["cm"] = Units.Meters2Units / 100, + ["km"] = Units.Meters2Units * 1000 }; /// @@ -170,7 +173,7 @@ namespace FlaxEditor.Utilities public static IEnumerable Tokenize(string text) { // Prepare text - text = text.Replace(',', '.'); + text = text.Replace(',', '.').Replace("°", ""); // Necessary to correctly parse negative numbers var previous = TokenType.WhiteSpace; @@ -372,6 +375,18 @@ namespace FlaxEditor.Utilities } } + // if stack has more than one item we're not finished with evaluating + // we assume the remaining values are all factors to be multiplied + if (stack.Count > 1) + { + var stackContent = string.Join(",", stack.ToList()); + Debug.Log($"parsing numbers, stack is {stackContent}"); + var v1 = stack.Pop(); + Debug.Log($"first on stack: {v1}"); + while (stack.Count > 0) + v1 *= stack.Pop(); + return v1; + } return stack.Pop(); } diff --git a/Source/Editor/Utilities/Units.cs b/Source/Editor/Utilities/Units.cs new file mode 100644 index 000000000..00d53a463 --- /dev/null +++ b/Source/Editor/Utilities/Units.cs @@ -0,0 +1,9 @@ +namespace FlaxEditor.Utilities; + +public class Units +{ + /// + /// Factor of units per meter. + /// + public static readonly float Meters2Units = 100f; +} diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 3d790ff81..0df4fe573 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -58,6 +58,16 @@ namespace FlaxEditor.Utilities /// public static readonly string FlaxEngineAssemblyName = "FlaxEngine.CSharp"; + /// + /// A category of number values used for formatting and input boxes + /// + public enum ValueCategory + { + None, + Distance, + Angle + } + /// /// Tries to parse number in the name brackets at the end of the value and then increment it to create a new name. /// Supports numbers at the end without brackets. @@ -1171,6 +1181,56 @@ namespace FlaxEditor.Utilities return StringUtils.GetPathWithoutExtension(path); } + /// + /// Format a float value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(float value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g7", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g7", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g7", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + + /// + /// Format a double value either as-is, with a distance unit or with a degree sign + /// + /// the value to format + /// the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign + /// the formatted string + public static string FormatFloat(double value, ValueCategory category) + { + switch (category) + { + case ValueCategory.Distance: + var absValue = Mathf.Abs(value); + // in case a unit != cm this would be (value / Maters2Units * 100) + if (absValue < Units.Meters2Units) + return value.ToString("g17", CultureInfo.InvariantCulture) + "cm"; + if (absValue < Units.Meters2Units * 1000) + return (value / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "m"; + return (value / 1000 / Units.Meters2Units).ToString("g17", CultureInfo.InvariantCulture) + "km"; + case ValueCategory.Angle: return value.ToString("g17", CultureInfo.InvariantCulture) + "°"; + case ValueCategory.None: + default: + return FormatFloat(value); + } + } + /// /// Formats the floating point value (double precision) into the readable text representation. /// @@ -1182,7 +1242,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (float.IsNegativeInfinity(value) || value == float.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g7", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } @@ -1197,7 +1257,7 @@ namespace FlaxEditor.Utilities return "Infinity"; if (double.IsNegativeInfinity(value) || value == double.MinValue) return "-Infinity"; - string str = value.ToString("r", CultureInfo.InvariantCulture); + string str = value.ToString("g17", CultureInfo.InvariantCulture); return FormatFloat(str, value < 0); } diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs new file mode 100644 index 000000000..3c6da2286 --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Used to specify the value category of a numeric value as either as-is (a scalar), a distance (formatted as cm/m/km) + /// or an angle (formatted with a degree sign). + /// + /// + [Serializable] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public sealed class ValueCategoryAttribute : Attribute + { + /// + /// The value category used for formatting. + /// + public FlaxEditor.Utilities.Utils.ValueCategory Category; + + private ValueCategoryAttribute() + { + Category = FlaxEditor.Utilities.Utils.ValueCategory.None; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value category. + public ValueCategoryAttribute(FlaxEditor.Utilities.Utils.ValueCategory category) + { + Category = category; + } + } +} diff --git a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj index df3b9851b..78b23ec07 100644 --- a/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj +++ b/Source/Tools/Flax.Build.Tests/Flax.Build.Tests.csproj @@ -2,7 +2,7 @@ net7.0 - 11.0 + 12 disable annotations true diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 1837bf0ec..7b189ce56 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -2,7 +2,6 @@ Exe net7.0 - 11.0 disable annotations false