diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
index fb33d470c..88ce53b78 100644
--- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
@@ -46,10 +46,13 @@ namespace FlaxEditor.CustomEditors.Editors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX;
+ XElement.ValueBox.Category = Utils.ValueCategory.Distance;
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY;
+ YElement.ValueBox.Category = Utils.ValueCategory.Distance;
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
+ ZElement.ValueBox.Category = Utils.ValueCategory.Distance;
}
}
@@ -68,10 +71,13 @@ namespace FlaxEditor.CustomEditors.Editors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX;
+ XElement.ValueBox.Category = Utils.ValueCategory.Angle;
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY;
+ YElement.ValueBox.Category = Utils.ValueCategory.Angle;
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
+ ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
}
}
diff --git a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs
index 6e3048eb9..2049860c6 100644
--- a/Source/Editor/CustomEditors/Editors/DoubleEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/DoubleEditor.cs
@@ -21,32 +21,28 @@ namespace FlaxEditor.CustomEditors.Editors
///
public override void Initialize(LayoutElementsContainer layout)
{
- _element = null;
-
- // Try get limit attribute for value min/max range setting and slider speed
+ var doubleValue = layout.DoubleValue();
+ doubleValue.ValueBox.ValueChanged += OnValueChanged;
+ doubleValue.ValueBox.SlidingEnd += ClearToken;
+ _element = doubleValue;
var attributes = Values.GetAttributes();
if (attributes != null)
{
- var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
- if (limit != null)
+ var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
+ doubleValue.SetLimits(limit);
+ var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None;
+ if (valueCategory != Utils.ValueCategory.None)
{
- // Use double value editor with limit
- var doubleValue = layout.DoubleValue();
- doubleValue.SetLimits((LimitAttribute)limit);
- doubleValue.ValueBox.ValueChanged += OnValueChanged;
- doubleValue.ValueBox.SlidingEnd += ClearToken;
- _element = doubleValue;
- return;
+ doubleValue.SetCategory(valueCategory);
+ LinkedLabel.SetupContextMenu += (label, menu, editor) =>
+ {
+ menu.AddSeparator();
+ var mb = menu.AddButton("Show formatted", bt => { doubleValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); });
+ mb.AutoCheck = true;
+ mb.Checked = doubleValue.ValueBox.Category != Utils.ValueCategory.None;
+ };
}
}
- if (_element == null)
- {
- // Use double value editor
- var doubleValue = layout.DoubleValue();
- doubleValue.ValueBox.ValueChanged += OnValueChanged;
- doubleValue.ValueBox.SlidingEnd += ClearToken;
- _element = doubleValue;
- }
}
private void OnValueChanged()
diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs
index 83d2c3f21..9d3ff1490 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 = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Editors
{
@@ -27,41 +28,39 @@ namespace FlaxEditor.CustomEditors.Editors
public override void Initialize(LayoutElementsContainer layout)
{
_element = null;
-
- // Try get limit attribute for value min/max range setting and slider speed
var attributes = Values.GetAttributes();
+ var range = (RangeAttribute)attributes?.FirstOrDefault(x => x is RangeAttribute);
+ if (range != null)
+ {
+ // Use slider
+ var slider = layout.Slider();
+ slider.Slider.SetLimits(range);
+ slider.Slider.ValueChanged += OnValueChanged;
+ slider.Slider.SlidingEnd += ClearToken;
+ _element = slider;
+ return;
+ }
+
+ var floatValue = layout.FloatValue();
+ floatValue.ValueBox.ValueChanged += OnValueChanged;
+ floatValue.ValueBox.SlidingEnd += ClearToken;
+ _element = floatValue;
if (attributes != null)
{
- var range = attributes.FirstOrDefault(x => x is RangeAttribute);
- if (range != null)
+ var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
+ floatValue.SetLimits(limit);
+ var valueCategory = ((ValueCategoryAttribute)attributes.FirstOrDefault(x => x is ValueCategoryAttribute))?.Category ?? Utils.ValueCategory.None;
+ if (valueCategory != Utils.ValueCategory.None)
{
- // Use slider
- var slider = layout.Slider();
- slider.SetLimits((RangeAttribute)range);
- slider.Slider.ValueChanged += OnValueChanged;
- slider.Slider.SlidingEnd += ClearToken;
- _element = slider;
- return;
+ floatValue.SetCategory(valueCategory);
+ LinkedLabel.SetupContextMenu += (label, menu, editor) =>
+ {
+ menu.AddSeparator();
+ var mb = menu.AddButton("Show formatted", bt => { floatValue.SetCategory(bt.Checked ? valueCategory : Utils.ValueCategory.None); });
+ mb.AutoCheck = true;
+ mb.Checked = floatValue.ValueBox.Category != Utils.ValueCategory.None;
+ };
}
- var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
- if (limit != null)
- {
- // Use float value editor with limit
- var floatValue = layout.FloatValue();
- floatValue.SetLimits((LimitAttribute)limit);
- floatValue.ValueBox.ValueChanged += OnValueChanged;
- floatValue.ValueBox.SlidingEnd += ClearToken;
- _element = floatValue;
- return;
- }
- }
- if (_element == null)
- {
- // Use float value editor
- var floatValue = layout.FloatValue();
- floatValue.ValueBox.ValueChanged += OnValueChanged;
- floatValue.ValueBox.SlidingEnd += ClearToken;
- _element = floatValue;
}
}
diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
index 453090d42..7712f1a08 100644
--- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
@@ -45,14 +45,17 @@ namespace FlaxEditor.CustomEditors.Editors
gridControl.SlotsVertically = 1;
XElement = grid.FloatValue();
+ XElement.ValueBox.Category = Utils.ValueCategory.Angle;
XElement.ValueBox.ValueChanged += OnValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken;
YElement = grid.FloatValue();
+ YElement.ValueBox.Category = Utils.ValueCategory.Angle;
YElement.ValueBox.ValueChanged += OnValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken;
ZElement = grid.FloatValue();
+ ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
ZElement.ValueBox.ValueChanged += OnValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken;
diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
index da0969002..e7170a22d 100644
--- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
+++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs
@@ -70,25 +70,44 @@ 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;
+ LinkedLabel.SetupContextMenu += (label, menu, editor) =>
+ {
+ menu.AddSeparator();
+ var mb = menu.AddButton("Show formatted", bt =>
+ {
+ XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ });
+ mb.AutoCheck = true;
+ mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None;
+ };
}
private void OnXValueChanged()
@@ -248,26 +267,45 @@ 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;
+ LinkedLabel.SetupContextMenu += (label, menu, editor) =>
+ {
+ menu.AddSeparator();
+ var mb = menu.AddButton("Show formatted", bt =>
+ {
+ XElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ YElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ ZElement.SetCategory(bt.Checked ? category : Utils.ValueCategory.None);
+ });
+ mb.AutoCheck = true;
+ mb.Checked = XElement.ValueBox.Category != Utils.ValueCategory.None;
+ };
}
private void OnValueChanged()
diff --git a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
index f426df605..cf16a1194 100644
--- a/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
+++ b/Source/Editor/CustomEditors/Elements/DoubleValueElement.cs
@@ -51,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Elements
}
}
+ ///
+ /// Sets the editor value category.
+ ///
+ /// The category.
+ 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 43490da7c..aabdb79e4 100644
--- a/Source/Editor/CustomEditors/Elements/FloatValueElement.cs
+++ b/Source/Editor/CustomEditors/Elements/FloatValueElement.cs
@@ -51,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Elements
}
}
+ ///
+ /// Sets the editor value category.
+ ///
+ /// The category.
+ 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 e4a8c6d3f..86acc1203 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 = FlaxEngine.Utils;
namespace FlaxEditor.GUI.Input
{
@@ -13,6 +14,8 @@ namespace FlaxEditor.GUI.Input
[HideInEditor]
public class DoubleValueBox : ValueBox
{
+ private Utils.ValueCategory _category = Utils.ValueCategory.None;
+
///
public override double Value
{
@@ -129,10 +132,25 @@ namespace FlaxEditor.GUI.Input
Value = Value;
}
+ ///
+ /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance.
+ ///
+ public Utils.ValueCategory Category
+ {
+ get => _category;
+ set
+ {
+ if (_category == value)
+ return;
+ _category = value;
+ UpdateText();
+ }
+ }
+
///
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 5fc90f260..f22ecd89c 100644
--- a/Source/Editor/GUI/Input/FloatValueBox.cs
+++ b/Source/Editor/GUI/Input/FloatValueBox.cs
@@ -1,9 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
-using System.Globalization;
using FlaxEditor.Utilities;
using FlaxEngine;
+using Utils = FlaxEngine.Utils;
namespace FlaxEditor.GUI.Input
{
@@ -14,6 +14,8 @@ namespace FlaxEditor.GUI.Input
[HideInEditor]
public class FloatValueBox : ValueBox
{
+ private Utils.ValueCategory _category = Utils.ValueCategory.None;
+
///
public override float Value
{
@@ -137,10 +139,25 @@ namespace FlaxEditor.GUI.Input
Value = Value;
}
+ ///
+ /// Gets or sets the category of the value. This can be none for just a number or a more specific one like a distance.
+ ///
+ public Utils.ValueCategory Category
+ {
+ get => _category;
+ set
+ {
+ if (_category == value)
+ return;
+ _category = value;
+ UpdateText();
+ }
+ }
+
///
protected sealed override void UpdateText()
{
- SetText(Utilities.Utils.FormatFloat(_value));
+ SetText(Utilities.Utils.FormatFloat(_value, Category));
}
///
diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs
index 05557f3c0..4cf1fe050 100644
--- a/Source/Editor/Options/InterfaceOptions.cs
+++ b/Source/Editor/Options/InterfaceOptions.cs
@@ -2,6 +2,7 @@
using System.ComponentModel;
using FlaxEditor.GUI.Docking;
+using FlaxEditor.Utilities;
using FlaxEngine;
namespace FlaxEditor.Options
@@ -116,6 +117,27 @@ namespace FlaxEditor.Options
BorderlessWindow,
}
+ ///
+ /// Options for formatting numerical values.
+ ///
+ public enum ValueFormattingType
+ {
+ ///
+ /// No formatting.
+ ///
+ None,
+
+ ///
+ /// Format using the base SI unit.
+ ///
+ BaseUnit,
+
+ ///
+ /// Format using a unit that matches the value best.
+ ///
+ AutoUnit,
+ }
+
///
/// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.
///
@@ -174,6 +196,20 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
+ ///
+ /// Gets or sets the formatting option for numeric values in the editor.
+ ///
+ [DefaultValue(ValueFormattingType.None)]
+ [EditorDisplay("Interface"), EditorOrder(300)]
+ public ValueFormattingType ValueFormatting { get; set; }
+
+ ///
+ /// Gets or sets the option to put a space between numbers and units for unit formatting.
+ ///
+ [DefaultValue(false)]
+ [EditorDisplay("Interface"), EditorOrder(310)]
+ public bool SeparateValueAndUnit { get; set; }
+
///
/// Gets or sets the timestamps prefix mode for output log messages.
///
diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs
index 2dceb3400..debc0a663 100644
--- a/Source/Editor/Options/OptionsModule.cs
+++ b/Source/Editor/Options/OptionsModule.cs
@@ -200,6 +200,27 @@ namespace FlaxEditor.Options
EditorAssets.Cache.OnEditorOptionsChanged(Options);
+ // Units formatting options
+ bool useUnitsFormatting = Options.Interface.ValueFormatting != InterfaceOptions.ValueFormattingType.None;
+ bool automaticUnitsFormatting = Options.Interface.ValueFormatting == InterfaceOptions.ValueFormattingType.AutoUnit;
+ bool separateValueAndUnit = Options.Interface.SeparateValueAndUnit;
+ if (useUnitsFormatting != Utilities.Units.UseUnitsFormatting ||
+ automaticUnitsFormatting != Utilities.Units.AutomaticUnitsFormatting ||
+ separateValueAndUnit != Utilities.Units.SeparateValueAndUnit)
+ {
+ Utilities.Units.UseUnitsFormatting = useUnitsFormatting;
+ Utilities.Units.AutomaticUnitsFormatting = automaticUnitsFormatting;
+ Utilities.Units.SeparateValueAndUnit = separateValueAndUnit;
+
+ // Refresh UI in property panels
+ Editor.Windows.PropertiesWin?.Presenter.BuildLayoutOnUpdate();
+ foreach (var window in Editor.Windows.Windows)
+ {
+ if (window is Windows.Assets.PrefabWindow prefabWindow)
+ prefabWindow.Presenter.BuildLayoutOnUpdate();
+ }
+ }
+
// Send event
OptionsChanged?.Invoke(Options);
}
diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs
index aa435416f..5ba8e969c 100644
--- a/Source/Editor/Utilities/ShuntingYardParser.cs
+++ b/Source/Editor/Utilities/ShuntingYardParser.cs
@@ -121,6 +121,37 @@ namespace FlaxEditor.Utilities
["e"] = Math.E,
["infinity"] = double.MaxValue,
["-infinity"] = -double.MaxValue,
+ ["m"] = Units.Meters2Units,
+ ["cm"] = Units.Meters2Units / 100,
+ ["km"] = Units.Meters2Units * 1000,
+ ["s"] = 1,
+ ["ms"] = 0.001,
+ ["min"] = 60,
+ ["h"] = 3600,
+ ["cm²"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100),
+ ["cm³"] = (Units.Meters2Units / 100) * (Units.Meters2Units / 100) * (Units.Meters2Units / 100),
+ ["dm²"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
+ ["dm³"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
+ ["l"] = (Units.Meters2Units / 10) * (Units.Meters2Units / 10) * (Units.Meters2Units / 10),
+ ["m²"] = Units.Meters2Units * Units.Meters2Units,
+ ["m³"] = Units.Meters2Units * Units.Meters2Units * Units.Meters2Units,
+ ["kg"] = 1,
+ ["g"] = 0.001,
+ ["n"] = Units.Meters2Units
+ };
+
+ ///
+ /// List known units which cannot be handled as a variable easily because they contain operator symbols (mostly a forward slash). The value is the factor to calculate game units.
+ ///
+ private static readonly IDictionary UnitSymbols = new Dictionary
+ {
+ ["cm/s"] = Units.Meters2Units / 100,
+ ["cm/s²"] = Units.Meters2Units / 100,
+ ["m/s"] = Units.Meters2Units,
+ ["m/s²"] = Units.Meters2Units,
+ ["km/h"] = 1 / 3.6 * Units.Meters2Units,
+ // Nm is here because these values are compared case-sensitive, and we don't want to confuse nanometers and Newtonmeters
+ ["Nm"] = Units.Meters2Units * Units.Meters2Units,
};
///
@@ -156,7 +187,7 @@ namespace FlaxEditor.Utilities
if (Operators.ContainsKey(str))
return TokenType.Operator;
- if (char.IsLetter(c))
+ if (char.IsLetter(c) || c == '²' || c == '³')
return TokenType.Variable;
throw new ParsingException("wrong character");
@@ -170,7 +201,24 @@ namespace FlaxEditor.Utilities
public static IEnumerable Tokenize(string text)
{
// Prepare text
- text = text.Replace(',', '.');
+ text = text.Replace(',', '.').Replace("°", "");
+ foreach (var kv in UnitSymbols)
+ {
+ int idx;
+ do
+ {
+ idx = text.IndexOf(kv.Key, StringComparison.InvariantCulture);
+ if (idx > 0)
+ {
+ if (DetermineType(text[idx - 1]) != TokenType.Number)
+ throw new ParsingException($"unit found without a number: {kv.Key} at {idx} in {text}");
+ if (Mathf.Abs(kv.Value - 1) < Mathf.Epsilon)
+ text = text.Remove(idx, kv.Key.Length);
+ else
+ text = text.Replace(kv.Key, "*" + kv.Value);
+ }
+ } while (idx > 0);
+ }
// Necessary to correctly parse negative numbers
var previous = TokenType.WhiteSpace;
@@ -240,6 +288,11 @@ namespace FlaxEditor.Utilities
}
else if (type == TokenType.Variable)
{
+ if (previous == TokenType.Number)
+ {
+ previous = TokenType.Operator;
+ yield return new Token(TokenType.Operator, "*");
+ }
// Continue till the end of the variable
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable)
{
@@ -335,7 +388,7 @@ namespace FlaxEditor.Utilities
}
else
{
- throw new ParsingException("unknown variable");
+ throw new ParsingException($"unknown variable : {token.Value}");
}
}
else
@@ -372,6 +425,15 @@ 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 v1 = stack.Pop();
+ 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..64977c18b
--- /dev/null
+++ b/Source/Editor/Utilities/Units.cs
@@ -0,0 +1,41 @@
+// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+
+namespace FlaxEditor.Utilities;
+
+///
+/// Units display utilities for Editor.
+///
+public class Units
+{
+ ///
+ /// Factor of units per meter.
+ ///
+ public static readonly float Meters2Units = 100f;
+
+ ///
+ /// False to always show game units without any postfix.
+ ///
+ public static bool UseUnitsFormatting = true;
+
+ ///
+ /// Add a space between numbers and units for readability.
+ ///
+ public static bool SeparateValueAndUnit = true;
+
+ ///
+ /// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters.
+ ///
+ public static bool AutomaticUnitsFormatting = true;
+
+ ///
+ /// Return the unit according to user settings.
+ ///
+ /// The unit name.
+ /// The formatted text.
+ public static string Unit(string unit)
+ {
+ if (SeparateValueAndUnit)
+ return $" {unit}";
+ return unit;
+ }
+}
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index 576c30f89..1b76de47e 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -1244,6 +1244,71 @@ namespace FlaxEditor.Utilities
return StringUtils.GetPathWithoutExtension(path);
}
+ private static string InternalFormat(double value, string format, FlaxEngine.Utils.ValueCategory category)
+ {
+ switch (category)
+ {
+ case FlaxEngine.Utils.ValueCategory.Distance:
+ if (!Units.AutomaticUnitsFormatting)
+ return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m");
+ var absValue = Mathf.Abs(value);
+ // in case a unit != cm this would be (value / Meters2Units * 100)
+ if (absValue < Units.Meters2Units)
+ return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("cm");
+ if (absValue < Units.Meters2Units * 1000)
+ return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m");
+ return (value / 1000 / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("km");
+ case FlaxEngine.Utils.ValueCategory.Angle: return value.ToString(format, CultureInfo.InvariantCulture) + "°";
+ case FlaxEngine.Utils.ValueCategory.Time: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("s");
+ // some fonts have a symbol for that: "\u33A7"
+ case FlaxEngine.Utils.ValueCategory.Speed: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s");
+ case FlaxEngine.Utils.ValueCategory.Acceleration: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m/s²");
+ case FlaxEngine.Utils.ValueCategory.Area: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m²");
+ case FlaxEngine.Utils.ValueCategory.Volume: return (value / Units.Meters2Units / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("m³");
+ case FlaxEngine.Utils.ValueCategory.Mass: return value.ToString(format, CultureInfo.InvariantCulture) + Units.Unit("kg");
+ case FlaxEngine.Utils.ValueCategory.Force: return (value / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("N");
+ case FlaxEngine.Utils.ValueCategory.Torque: return (value / Units.Meters2Units / Units.Meters2Units).ToString(format, CultureInfo.InvariantCulture) + Units.Unit("Nm");
+ case FlaxEngine.Utils.ValueCategory.None:
+ default: return FormatFloat(value);
+ }
+ }
+
+ ///
+ /// 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, FlaxEngine.Utils.ValueCategory category)
+ {
+ if (float.IsPositiveInfinity(value) || value == float.MaxValue)
+ return "Infinity";
+ if (float.IsNegativeInfinity(value) || value == float.MinValue)
+ return "-Infinity";
+ if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None)
+ return FormatFloat(value);
+ const string format = "G7";
+ return InternalFormat(value, format, category);
+ }
+
+ ///
+ /// 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, FlaxEngine.Utils.ValueCategory category)
+ {
+ if (double.IsPositiveInfinity(value) || value == double.MaxValue)
+ return "Infinity";
+ if (double.IsNegativeInfinity(value) || value == double.MinValue)
+ return "-Infinity";
+ if (!Units.UseUnitsFormatting || category == FlaxEngine.Utils.ValueCategory.None)
+ return FormatFloat(value);
+ const string format = "G15";
+ return InternalFormat(value, format, category);
+ }
+
///
/// Formats the floating point value (double precision) into the readable text representation.
///
@@ -1255,7 +1320,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("R", CultureInfo.InvariantCulture);
return FormatFloat(str, value < 0);
}
@@ -1270,7 +1335,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("R", CultureInfo.InvariantCulture);
return FormatFloat(str, value < 0);
}
diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h
index 02a9e5a79..2c51bfa4a 100644
--- a/Source/Engine/Core/Math/Transform.h
+++ b/Source/Engine/Core/Math/Transform.h
@@ -18,13 +18,13 @@ API_STRUCT() struct FLAXENGINE_API Transform
///
/// The translation vector of the transform.
///
- API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\")")
+ API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), ValueCategory(Utils.ValueCategory.Distance)")
Vector3 Translation;
///
/// The rotation of the transform.
///
- API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\")")
+ API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), ValueCategory(Utils.ValueCategory.Angle)")
Quaternion Orientation;
///
diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h
index e7c7971b7..190126e54 100644
--- a/Source/Engine/Level/Actors/Camera.h
+++ b/Source/Engine/Level/Actors/Camera.h
@@ -77,7 +77,7 @@ public:
///
/// Gets the camera's field of view (in degrees).
///
- API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective))")
+ API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(60.0f), Limit(0, 179), EditorDisplay(\"Camera\", \"Field Of View\"), VisibleIf(nameof(UsePerspective)), ValueCategory(Utils.ValueCategory.Angle)")
float GetFieldOfView() const;
///
@@ -99,7 +99,7 @@ public:
///
/// Gets camera's near plane distance.
///
- API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\")")
+ API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(10.0f), Limit(0, 1000, 0.05f), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetNearPlane() const;
///
@@ -110,7 +110,7 @@ public:
///
/// Gets camera's far plane distance.
///
- API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\")")
+ API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(40000.0f), Limit(0, float.MaxValue, 5), EditorDisplay(\"Camera\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetFarPlane() const;
///
diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h
index f005f1baa..c862a8e97 100644
--- a/Source/Engine/Physics/Actors/RigidBody.h
+++ b/Source/Engine/Physics/Actors/RigidBody.h
@@ -181,7 +181,7 @@ public:
///
/// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked).
///
- API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\")")
+ API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), ValueCategory(Utils.ValueCategory.Mass)")
float GetMass() const;
///
diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h
index f413fa042..58298ada8 100644
--- a/Source/Engine/Physics/Colliders/BoxCollider.h
+++ b/Source/Engine/Physics/Colliders/BoxCollider.h
@@ -23,7 +23,7 @@ public:
/// Gets the size of the box, measured in the object's local space.
///
/// The box size will be scaled by the actor's world scale.
- API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(typeof(Float3), \"100,100,100\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE Float3 GetSize() const
{
return _size;
diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h
index 60526750c..c225ac2a5 100644
--- a/Source/Engine/Physics/Colliders/CapsuleCollider.h
+++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h
@@ -25,7 +25,7 @@ public:
/// Gets the radius of the sphere, measured in the object's local space.
///
/// The sphere radius will be scaled by the actor's world scale.
- API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetRadius() const
{
return _radius;
@@ -41,7 +41,7 @@ public:
/// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends.
///
/// The capsule height will be scaled by the actor's world scale.
- API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetHeight() const
{
return _height;
diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h
index 386c49eb5..545f4e9d9 100644
--- a/Source/Engine/Physics/Colliders/CharacterController.h
+++ b/Source/Engine/Physics/Colliders/CharacterController.h
@@ -73,7 +73,7 @@ public:
///
/// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale.
///
- API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetRadius() const;
///
@@ -84,7 +84,7 @@ public:
///
/// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
///
- API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetHeight() const;
///
@@ -95,7 +95,7 @@ public:
///
/// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value.
///
- API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\")")
+ API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Angle)")
float GetSlopeLimit() const;
///
@@ -117,7 +117,7 @@ public:
///
/// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controller’s height or it will generate an error.
///
- API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\")")
+ API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetStepOffset() const;
///
@@ -139,7 +139,7 @@ public:
///
/// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small.
///
- API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\")")
+ API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetMinMoveDistance() const;
///
diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h
index a2e115dcc..05f2a6eb2 100644
--- a/Source/Engine/Physics/Colliders/Collider.h
+++ b/Source/Engine/Physics/Colliders/Collider.h
@@ -52,7 +52,7 @@ public:
///
/// Gets the center of the collider, measured in the object's local space.
///
- API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE Vector3 GetCenter() const
{
return _center;
@@ -66,7 +66,7 @@ public:
///
/// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated.
///
- API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(1), DefaultValue(2.0f), Limit(0, 100), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetContactOffset() const
{
return _contactOffset;
diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h
index 084dd0700..e3b295eda 100644
--- a/Source/Engine/Physics/Colliders/SphereCollider.h
+++ b/Source/Engine/Physics/Colliders/SphereCollider.h
@@ -21,7 +21,7 @@ public:
/// Gets the radius of the sphere, measured in the object's local space.
///
/// The sphere radius will be scaled by the actor's world scale.
- API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")")
+ API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetRadius() const
{
return _radius;
diff --git a/Source/Engine/Physics/Joints/DistanceJoint.h b/Source/Engine/Physics/Joints/DistanceJoint.h
index 8cb0e5fd0..0f0f59b01 100644
--- a/Source/Engine/Physics/Joints/DistanceJoint.h
+++ b/Source/Engine/Physics/Joints/DistanceJoint.h
@@ -67,7 +67,7 @@ public:
/// Gets the allowed minimum distance for the joint.
///
/// Used only when DistanceJointFlag.MinDistance flag is set. The minimum distance must be no more than the maximum distance. Default: 0, Range: [0, float.MaxValue].
- API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\")")
+ API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetMinDistance() const
{
return _minDistance;
@@ -83,7 +83,7 @@ public:
/// Gets the allowed maximum distance for the joint.
///
/// Used only when DistanceJointFlag.MaxDistance flag is set. The maximum distance must be no less than the minimum distance. Default: 0, Range: [0, float.MaxValue].
- API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\")")
+ API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(10.0f), Limit(0.0f), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetMaxDistance() const
{
return _maxDistance;
diff --git a/Source/Engine/Physics/Joints/Joint.h b/Source/Engine/Physics/Joints/Joint.h
index f4733f01d..a4584a07e 100644
--- a/Source/Engine/Physics/Joints/Joint.h
+++ b/Source/Engine/Physics/Joints/Joint.h
@@ -38,7 +38,7 @@ public:
///
/// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation.
///
- API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")")
+ API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Force)")
FORCE_INLINE float GetBreakForce() const
{
return _breakForce;
@@ -52,7 +52,7 @@ public:
///
/// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation.
///
- API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")")
+ API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), ValueCategory(Utils.ValueCategory.Torque)")
FORCE_INLINE float GetBreakTorque() const
{
return _breakTorque;
diff --git a/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs
new file mode 100644
index 000000000..4f51efbe3
--- /dev/null
+++ b/Source/Engine/Scripting/Attributes/Editor/ValueCategoryAttribute.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+
+using System;
+
+namespace FlaxEngine
+{
+ ///
+ /// Specifies 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 Utils.ValueCategory Category;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ private ValueCategoryAttribute()
+ {
+ Category = Utils.ValueCategory.None;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value category.
+ public ValueCategoryAttribute(Utils.ValueCategory category)
+ {
+ Category = category;
+ }
+ }
+}
diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs
index acfa5a1dc..0767ab8f4 100644
--- a/Source/Engine/Utilities/Utils.cs
+++ b/Source/Engine/Utilities/Utils.cs
@@ -257,6 +257,7 @@ namespace FlaxEngine
{
public static FieldInfo itemsField;
}
+
internal static T[] ExtractArrayFromList(List list)
{
if (list == null)
@@ -1038,5 +1039,66 @@ namespace FlaxEngine
parameterTypes = Array.Empty();
return parameterTypes;
}
+
+ ///
+ /// A category of number values used for formatting and input fields.
+ ///
+ public enum ValueCategory
+ {
+ ///
+ /// Nothing.
+ ///
+ None,
+
+ ///
+ /// Distance (eg. meters).
+ ///
+ Distance,
+
+ ///
+ /// Area (eg. m^2).
+ ///
+ Area,
+
+ ///
+ /// Volume (eg. m^3).
+ ///
+ Volume,
+
+ ///
+ /// Mass (eg. kilograms).
+ ///
+ Mass,
+
+ ///
+ /// Angle (eg. degrees).
+ ///
+ Angle,
+
+ ///
+ /// Speed (distance / time).
+ ///
+ Speed,
+
+ ///
+ /// Acceleration (distance^2 / time).
+ ///
+ Acceleration,
+
+ ///
+ /// Time (eg. seconds).
+ ///
+ Time,
+
+ ///
+ /// Force (mass * distance / time^2).
+ ///
+ Force,
+
+ ///
+ /// Torque (mass * distance^2 / time^2).
+ ///
+ Torque,
+ }
}
}