Merge branch 'unit-formatting' of https://github.com/nothingTVatYT/FlaxEngine into nothingTVatYT-unit-formatting

# Conflicts:
#	Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
#	Source/Editor/Options/InterfaceOptions.cs
#	Source/Engine/Core/Math/Transform.h
This commit is contained in:
Wojtek Figat
2024-03-19 18:04:01 +01:00
24 changed files with 420 additions and 26 deletions

View File

@@ -46,10 +46,13 @@ namespace FlaxEditor.CustomEditors.Editors
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX; XElement.ValueBox.BorderSelectedColor = AxisColorX;
XElement.ValueBox.Category = Utils.ValueCategory.Distance;
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY; YElement.ValueBox.BorderSelectedColor = AxisColorY;
YElement.ValueBox.Category = Utils.ValueCategory.Distance;
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ; 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; var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor);
XElement.ValueBox.BorderSelectedColor = AxisColorX; XElement.ValueBox.BorderSelectedColor = AxisColorX;
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor);
YElement.ValueBox.BorderSelectedColor = AxisColorY; YElement.ValueBox.BorderSelectedColor = AxisColorY;
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor);
ZElement.ValueBox.BorderSelectedColor = AxisColorZ; ZElement.ValueBox.BorderSelectedColor = AxisColorZ;
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
} }
} }

View File

@@ -4,6 +4,7 @@ using System;
using System.Linq; using System.Linq;
using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.Elements;
using FlaxEngine; using FlaxEngine;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Editors 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 // Try get limit attribute for value min/max range setting and slider speed
var attributes = Values.GetAttributes(); var attributes = Values.GetAttributes();
var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute);
var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None;
if (attributes != null) if (attributes != null)
{ {
var limit = attributes.FirstOrDefault(x => x is LimitAttribute); var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
@@ -32,10 +35,18 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
// Use double value editor with limit // Use double value editor with limit
var doubleValue = layout.DoubleValue(); var doubleValue = layout.DoubleValue();
doubleValue.SetCategory(valueCategory);
doubleValue.SetLimits((LimitAttribute)limit); doubleValue.SetLimits((LimitAttribute)limit);
doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.ValueChanged += OnValueChanged;
doubleValue.ValueBox.SlidingEnd += ClearToken; doubleValue.ValueBox.SlidingEnd += ClearToken;
_element = doubleValue; _element = doubleValue;
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;
};
return; return;
} }
} }
@@ -43,8 +54,16 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
// Use double value editor // Use double value editor
var doubleValue = layout.DoubleValue(); var doubleValue = layout.DoubleValue();
doubleValue.SetCategory(valueCategory);
doubleValue.ValueBox.ValueChanged += OnValueChanged; doubleValue.ValueBox.ValueChanged += OnValueChanged;
doubleValue.ValueBox.SlidingEnd += ClearToken; doubleValue.ValueBox.SlidingEnd += ClearToken;
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;
};
_element = doubleValue; _element = doubleValue;
} }
} }

View File

@@ -3,7 +3,9 @@
using System; using System;
using System.Linq; using System.Linq;
using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI.ContextMenu;
using FlaxEngine; using FlaxEngine;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Editors namespace FlaxEditor.CustomEditors.Editors
{ {
@@ -30,6 +32,8 @@ namespace FlaxEditor.CustomEditors.Editors
// Try get limit attribute for value min/max range setting and slider speed // Try get limit attribute for value min/max range setting and slider speed
var attributes = Values.GetAttributes(); var attributes = Values.GetAttributes();
var categoryAttribute = attributes.FirstOrDefault(x => x is NumberCategoryAttribute);
var valueCategory = ((NumberCategoryAttribute)categoryAttribute)?.Category ?? Utils.ValueCategory.None;
if (attributes != null) if (attributes != null)
{ {
var range = attributes.FirstOrDefault(x => x is RangeAttribute); var range = attributes.FirstOrDefault(x => x is RangeAttribute);
@@ -49,9 +53,17 @@ namespace FlaxEditor.CustomEditors.Editors
// Use float value editor with limit // Use float value editor with limit
var floatValue = layout.FloatValue(); var floatValue = layout.FloatValue();
floatValue.SetLimits((LimitAttribute)limit); floatValue.SetLimits((LimitAttribute)limit);
floatValue.SetCategory(valueCategory);
floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.ValueChanged += OnValueChanged;
floatValue.ValueBox.SlidingEnd += ClearToken; floatValue.ValueBox.SlidingEnd += ClearToken;
_element = floatValue; _element = floatValue;
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;
};
return; return;
} }
} }
@@ -59,9 +71,17 @@ namespace FlaxEditor.CustomEditors.Editors
{ {
// Use float value editor // Use float value editor
var floatValue = layout.FloatValue(); var floatValue = layout.FloatValue();
floatValue.SetCategory(valueCategory);
floatValue.ValueBox.ValueChanged += OnValueChanged; floatValue.ValueBox.ValueChanged += OnValueChanged;
floatValue.ValueBox.SlidingEnd += ClearToken; floatValue.ValueBox.SlidingEnd += ClearToken;
_element = floatValue; _element = floatValue;
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;
};
} }
} }

View File

@@ -45,14 +45,17 @@ namespace FlaxEditor.CustomEditors.Editors
gridControl.SlotsVertically = 1; gridControl.SlotsVertically = 1;
XElement = grid.FloatValue(); XElement = grid.FloatValue();
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.ValueChanged += OnValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken; XElement.ValueBox.SlidingEnd += ClearToken;
YElement = grid.FloatValue(); YElement = grid.FloatValue();
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.ValueChanged += OnValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken; YElement.ValueBox.SlidingEnd += ClearToken;
ZElement = grid.FloatValue(); ZElement = grid.FloatValue();
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.ValueChanged += OnValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken; ZElement.ValueBox.SlidingEnd += ClearToken;

View File

@@ -4,6 +4,7 @@ using System.Linq;
using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.Elements;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Editors namespace FlaxEditor.CustomEditors.Editors
{ {
@@ -70,25 +71,44 @@ namespace FlaxEditor.CustomEditors.Editors
LimitAttribute limit = null; LimitAttribute limit = null;
var attributes = Values.GetAttributes(); var attributes = Values.GetAttributes();
var category = Utils.ValueCategory.None;
if (attributes != null) if (attributes != null)
{ {
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute);
if (categoryAttribute != null)
category = categoryAttribute.Category;
} }
XElement = grid.FloatValue(); XElement = grid.FloatValue();
XElement.SetLimits(limit); XElement.SetLimits(limit);
XElement.SetCategory(category);
XElement.ValueBox.ValueChanged += OnXValueChanged; XElement.ValueBox.ValueChanged += OnXValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken; XElement.ValueBox.SlidingEnd += ClearToken;
YElement = grid.FloatValue(); YElement = grid.FloatValue();
YElement.SetLimits(limit); YElement.SetLimits(limit);
YElement.SetCategory(category);
YElement.ValueBox.ValueChanged += OnYValueChanged; YElement.ValueBox.ValueChanged += OnYValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken; YElement.ValueBox.SlidingEnd += ClearToken;
ZElement = grid.FloatValue(); ZElement = grid.FloatValue();
ZElement.SetLimits(limit); ZElement.SetLimits(limit);
ZElement.SetCategory(category);
ZElement.ValueBox.ValueChanged += OnZValueChanged; ZElement.ValueBox.ValueChanged += OnZValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken; 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() private void OnXValueChanged()
@@ -248,26 +268,45 @@ namespace FlaxEditor.CustomEditors.Editors
gridControl.SlotsVertically = 1; gridControl.SlotsVertically = 1;
LimitAttribute limit = null; LimitAttribute limit = null;
Utils.ValueCategory category = Utils.ValueCategory.None;
var attributes = Values.GetAttributes(); var attributes = Values.GetAttributes();
if (attributes != null) if (attributes != null)
{ {
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
var categoryAttribute = (NumberCategoryAttribute)attributes.FirstOrDefault(x => x is NumberCategoryAttribute);
if (categoryAttribute != null)
category = categoryAttribute.Category;
} }
XElement = grid.DoubleValue(); XElement = grid.DoubleValue();
XElement.SetLimits(limit); XElement.SetLimits(limit);
XElement.SetCategory(category);
XElement.ValueBox.ValueChanged += OnValueChanged; XElement.ValueBox.ValueChanged += OnValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken; XElement.ValueBox.SlidingEnd += ClearToken;
YElement = grid.DoubleValue(); YElement = grid.DoubleValue();
YElement.SetLimits(limit); YElement.SetLimits(limit);
YElement.SetCategory(category);
YElement.ValueBox.ValueChanged += OnValueChanged; YElement.ValueBox.ValueChanged += OnValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken; YElement.ValueBox.SlidingEnd += ClearToken;
ZElement = grid.DoubleValue(); ZElement = grid.DoubleValue();
ZElement.SetLimits(limit); ZElement.SetLimits(limit);
ZElement.SetCategory(category);
ZElement.ValueBox.ValueChanged += OnValueChanged; ZElement.ValueBox.ValueChanged += OnValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken; 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() private void OnValueChanged()

View File

@@ -5,6 +5,7 @@ using System.Reflection;
using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Input;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Elements namespace FlaxEditor.CustomEditors.Elements
{ {
@@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements
} }
} }
/// <summary>
/// Set the value category of this float element
/// </summary>
/// <param name="category"></param>
public void SetCategory(Utils.ValueCategory category)
{
ValueBox.Category = category;
}
/// <summary> /// <summary>
/// Sets the editor limits from member <see cref="LimitAttribute"/>. /// Sets the editor limits from member <see cref="LimitAttribute"/>.
/// </summary> /// </summary>

View File

@@ -5,6 +5,7 @@ using System.Reflection;
using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Input;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.CustomEditors.Elements namespace FlaxEditor.CustomEditors.Elements
{ {
@@ -51,6 +52,15 @@ namespace FlaxEditor.CustomEditors.Elements
} }
} }
/// <summary>
/// Set the value category of this float element
/// </summary>
/// <param name="category"></param>
public void SetCategory(Utils.ValueCategory category)
{
ValueBox.Category = category;
}
/// <summary> /// <summary>
/// Sets the editor limits from member <see cref="LimitAttribute"/>. /// Sets the editor limits from member <see cref="LimitAttribute"/>.
/// </summary> /// </summary>

View File

@@ -3,6 +3,7 @@
using System; using System;
using FlaxEditor.Utilities; using FlaxEditor.Utilities;
using FlaxEngine; using FlaxEngine;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.GUI.Input namespace FlaxEditor.GUI.Input
{ {
@@ -129,10 +130,15 @@ namespace FlaxEditor.GUI.Input
Value = Value; Value = Value;
} }
/// <summary>
/// Get or set the category of the value. This can either be none for just a number, a distance or an angle.
/// </summary>
public Utils.ValueCategory Category = Utils.ValueCategory.None;
/// <inheritdoc /> /// <inheritdoc />
protected sealed override void UpdateText() protected sealed override void UpdateText()
{ {
SetText(Utilities.Utils.FormatFloat(_value)); SetText(Utilities.Utils.FormatFloat(_value, Category));
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System; using System;
using System.Globalization;
using FlaxEditor.Utilities; using FlaxEditor.Utilities;
using FlaxEngine; using FlaxEngine;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.GUI.Input namespace FlaxEditor.GUI.Input
{ {
@@ -137,10 +137,20 @@ namespace FlaxEditor.GUI.Input
Value = Value; Value = Value;
} }
private Utils.ValueCategory _category = Utils.ValueCategory.None;
/// <summary>
/// Get or set the category of the value. This can be none for just a number or a more specific one like a distance.
/// </summary>
public Utils.ValueCategory Category {
get => _category;
set { _category = value; UpdateText(); }
}
/// <inheritdoc /> /// <inheritdoc />
protected sealed override void UpdateText() protected sealed override void UpdateText()
{ {
SetText(Utilities.Utils.FormatFloat(_value)); SetText(Utilities.Utils.FormatFloat(_value, Category));
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -2,6 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using FlaxEditor.GUI.Docking; using FlaxEditor.GUI.Docking;
using FlaxEditor.Utilities;
using FlaxEngine; using FlaxEngine;
namespace FlaxEditor.Options namespace FlaxEditor.Options
@@ -116,6 +117,25 @@ namespace FlaxEditor.Options
BorderlessWindow, BorderlessWindow,
} }
/// <summary>
/// Options for formatting numerical values
/// </summary>
public enum ValueFormattingType
{
/// <summary>
/// No formatting
/// </summary>
None,
/// <summary>
/// Format using the base SI unit
/// </summary>
BaseUnit,
/// <summary>
/// Format using a unit that matches the value best
/// </summary>
AutoUnit
}
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
@@ -174,6 +194,38 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
private ValueFormattingType _valueFormatting = ValueFormattingType.None;
/// <summary>
/// Gets or sets the formatting option for numeric values in the editor.
/// </summary>
[DefaultValue(ValueFormattingType.None)]
[EditorDisplay("Interface"), EditorOrder(300), Tooltip("Formatting of numeric values.")]
public ValueFormattingType ValueFormatting {
get => _valueFormatting;
set
{
_valueFormatting = value;
Units.UseUnitsFormatting = _valueFormatting != ValueFormattingType.None;
Units.AutomaticUnitsFormatting = _valueFormatting == ValueFormattingType.AutoUnit;
}
}
private bool _spaceNumberAndUnits = false;
/// <summary>
/// Gets or sets the option to put a space between numbers and units for unit formatting
/// </summary>
[DefaultValue(false)]
[EditorDisplay("Interface"), EditorOrder(310), Tooltip("Put a space between numbers and units.")]
public bool SpaceNumberAndUnits { get => _spaceNumberAndUnits;
set
{
_spaceNumberAndUnits = value;
Units.SpaceNumberAndUnits = _spaceNumberAndUnits;
}
}
/// <summary> /// <summary>
/// Gets or sets the timestamps prefix mode for output log messages. /// Gets or sets the timestamps prefix mode for output log messages.
/// </summary> /// </summary>

View File

@@ -121,6 +121,39 @@ namespace FlaxEditor.Utilities
["e"] = Math.E, ["e"] = Math.E,
["infinity"] = double.MaxValue, ["infinity"] = double.MaxValue,
["-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
};
/// <summary>
/// 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.
/// </summary>
// Nm is here because these values are compared case-sensitive and we don't want to confuse
// nanometers and Newtonmeters
private static readonly IDictionary<string, double> UnitSymbols = new Dictionary<string, double>
{
["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"] = Units.Meters2Units * Units.Meters2Units
}; };
/// <summary> /// <summary>
@@ -156,7 +189,7 @@ namespace FlaxEditor.Utilities
if (Operators.ContainsKey(str)) if (Operators.ContainsKey(str))
return TokenType.Operator; return TokenType.Operator;
if (char.IsLetter(c)) if (char.IsLetter(c) || c=='²' || c=='³')
return TokenType.Variable; return TokenType.Variable;
throw new ParsingException("wrong character"); throw new ParsingException("wrong character");
@@ -170,7 +203,24 @@ namespace FlaxEditor.Utilities
public static IEnumerable<Token> Tokenize(string text) public static IEnumerable<Token> Tokenize(string text)
{ {
// Prepare 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 // Necessary to correctly parse negative numbers
var previous = TokenType.WhiteSpace; var previous = TokenType.WhiteSpace;
@@ -240,6 +290,11 @@ namespace FlaxEditor.Utilities
} }
else if (type == TokenType.Variable) 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 // Continue till the end of the variable
while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable) while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Variable)
{ {
@@ -335,7 +390,7 @@ namespace FlaxEditor.Utilities
} }
else else
{ {
throw new ParsingException("unknown variable"); throw new ParsingException($"unknown variable : {token.Value}");
} }
} }
else else
@@ -372,6 +427,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(); return stack.Pop();
} }

View File

@@ -0,0 +1,36 @@
namespace FlaxEditor.Utilities;
public class Units
{
/// <summary>
/// Factor of units per meter.
/// </summary>
public static readonly float Meters2Units = 100f;
/// <summary>
/// Set it to false to always show game units without any postfix.
/// </summary>
public static bool UseUnitsFormatting = true;
/// <summary>
/// Add a space between numbers and units for readability.
/// </summary>
public static bool SpaceNumberAndUnits = true;
/// <summary>
/// If set to true, the distance unit is chosen on the magnitude, otherwise it's meters.
/// </summary>
public static bool AutomaticUnitsFormatting = true;
/// <summary>
/// Return the unit according to user settings.
/// </summary>
/// <param name="unit"></param>
/// <returns></returns>
public static string Unit(string unit)
{
if (SpaceNumberAndUnits)
return $" {unit}";
return unit;
}
}

View File

@@ -1244,6 +1244,72 @@ namespace FlaxEditor.Utilities
return StringUtils.GetPathWithoutExtension(path); 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);
}
}
/// <summary>
/// Format a float value either as-is, with a distance unit or with a degree sign
/// </summary>
/// <param name="value">the value to format</param>
/// <param name="category">the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign</param>
/// <returns>the formatted string</returns>
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);
}
/// <summary>
/// Format a double value either as-is, with a distance unit or with a degree sign
/// </summary>
/// <param name="value">the value to format</param>
/// <param name="category">the value type: none means just a number, distance will format in cm/m/km, angle with an appended degree sign</param>
/// <returns>the formatted string</returns>
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);
}
/// <summary> /// <summary>
/// Formats the floating point value (double precision) into the readable text representation. /// Formats the floating point value (double precision) into the readable text representation.
/// </summary> /// </summary>
@@ -1255,7 +1321,7 @@ namespace FlaxEditor.Utilities
return "Infinity"; return "Infinity";
if (float.IsNegativeInfinity(value) || value == float.MinValue) if (float.IsNegativeInfinity(value) || value == float.MinValue)
return "-Infinity"; return "-Infinity";
string str = value.ToString("r", CultureInfo.InvariantCulture); string str = value.ToString("R", CultureInfo.InvariantCulture);
return FormatFloat(str, value < 0); return FormatFloat(str, value < 0);
} }
@@ -1270,7 +1336,7 @@ namespace FlaxEditor.Utilities
return "Infinity"; return "Infinity";
if (double.IsNegativeInfinity(value) || value == double.MinValue) if (double.IsNegativeInfinity(value) || value == double.MinValue)
return "-Infinity"; return "-Infinity";
string str = value.ToString("r", CultureInfo.InvariantCulture); string str = value.ToString("R", CultureInfo.InvariantCulture);
return FormatFloat(str, value < 0); return FormatFloat(str, value < 0);
} }

View File

@@ -18,13 +18,13 @@ API_STRUCT() struct FLAXENGINE_API Transform
/// <summary> /// <summary>
/// The translation vector of the transform. /// The translation vector of the transform.
/// </summary> /// </summary>
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\")") API_FIELD(Attributes="EditorOrder(10), EditorDisplay(null, \"Position\"), NumberCategory(Utils.ValueCategory.Distance)")
Vector3 Translation; Vector3 Translation;
/// <summary> /// <summary>
/// The rotation of the transform. /// The rotation of the transform.
/// </summary> /// </summary>
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\")") API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Rotation\"), NumberCategory(Utils.ValueCategory.Angle)")
Quaternion Orientation; Quaternion Orientation;
/// <summary> /// <summary>

View File

@@ -181,7 +181,7 @@ public:
/// <summary> /// <summary>
/// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked). /// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked).
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\")") API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\"), NumberCategory(Utils.ValueCategory.Mass)")
float GetMass() const; float GetMass() const;
/// <summary> /// <summary>

View File

@@ -23,7 +23,7 @@ public:
/// Gets the size of the box, measured in the object's local space. /// Gets the size of the box, measured in the object's local space.
/// </summary> /// </summary>
/// <remarks>The box size will be scaled by the actor's world scale. </remarks> /// <remarks>The box size will be scaled by the actor's world scale. </remarks>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE Float3 GetSize() const FORCE_INLINE Float3 GetSize() const
{ {
return _size; return _size;

View File

@@ -25,7 +25,7 @@ public:
/// Gets the radius of the sphere, measured in the object's local space. /// Gets the radius of the sphere, measured in the object's local space.
/// </summary> /// </summary>
/// <remarks>The sphere radius will be scaled by the actor's world scale.</remarks> /// <remarks>The sphere radius will be scaled by the actor's world scale.</remarks>
API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\")") API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(20.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetRadius() const FORCE_INLINE float GetRadius() const
{ {
return _radius; 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. /// Gets the height of the capsule, measured in the object's local space between the centers of the hemispherical ends.
/// </summary> /// </summary>
/// <remarks>The capsule height will be scaled by the actor's world scale.</remarks> /// <remarks>The capsule height will be scaled by the actor's world scale.</remarks>
API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\")") API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(100.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetHeight() const FORCE_INLINE float GetHeight() const
{ {
return _height; return _height;

View File

@@ -73,7 +73,7 @@ public:
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)")
float GetRadius() const; float GetRadius() const;
/// <summary> /// <summary>
@@ -84,7 +84,7 @@ public:
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\")") API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)")
float GetHeight() const; float GetHeight() const;
/// <summary> /// <summary>
@@ -95,7 +95,7 @@ public:
/// <summary> /// <summary>
/// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value.
/// </summary> /// </summary>
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\"), NumberCategory(Utils.ValueCategory.Angle)")
float GetSlopeLimit() const; float GetSlopeLimit() const;
/// <summary> /// <summary>
@@ -117,7 +117,7 @@ public:
/// <summary> /// <summary>
/// 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 Controllers height or it will generate an error. /// 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 Controllers height or it will generate an error.
/// </summary> /// </summary>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
float GetStepOffset() const; float GetStepOffset() const;
/// <summary> /// <summary>
@@ -139,7 +139,7 @@ public:
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
float GetMinMoveDistance() const; float GetMinMoveDistance() const;
/// <summary> /// <summary>

View File

@@ -52,7 +52,7 @@ public:
/// <summary> /// <summary>
/// Gets the center of the collider, measured in the object's local space. /// Gets the center of the collider, measured in the object's local space.
/// </summary> /// </summary>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE Vector3 GetCenter() const FORCE_INLINE Vector3 GetCenter() const
{ {
return _center; return _center;
@@ -66,7 +66,7 @@ public:
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetContactOffset() const FORCE_INLINE float GetContactOffset() const
{ {
return _contactOffset; return _contactOffset;

View File

@@ -21,7 +21,7 @@ public:
/// Gets the radius of the sphere, measured in the object's local space. /// Gets the radius of the sphere, measured in the object's local space.
/// </summary> /// </summary>
/// <remarks>The sphere radius will be scaled by the actor's world scale.</remarks> /// <remarks>The sphere radius will be scaled by the actor's world scale.</remarks>
API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetRadius() const FORCE_INLINE float GetRadius() const
{ {
return _radius; return _radius;

View File

@@ -67,7 +67,7 @@ public:
/// Gets the allowed minimum distance for the joint. /// Gets the allowed minimum distance for the joint.
/// </summary> /// </summary>
/// <remarks>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].</remarks> /// <remarks>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].</remarks>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetMinDistance() const FORCE_INLINE float GetMinDistance() const
{ {
return _minDistance; return _minDistance;
@@ -83,7 +83,7 @@ public:
/// Gets the allowed maximum distance for the joint. /// Gets the allowed maximum distance for the joint.
/// </summary> /// </summary>
/// <remarks>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].</remarks> /// <remarks>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].</remarks>
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\"), NumberCategory(Utils.ValueCategory.Distance)")
FORCE_INLINE float GetMaxDistance() const FORCE_INLINE float GetMaxDistance() const
{ {
return _maxDistance; return _maxDistance;

View File

@@ -38,7 +38,7 @@ public:
/// <summary> /// <summary>
/// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// Gets the break force. Determines the maximum force the joint can apply before breaking. Broken joints no longer participate in physics simulation.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Force)")
FORCE_INLINE float GetBreakForce() const FORCE_INLINE float GetBreakForce() const
{ {
return _breakForce; return _breakForce;
@@ -52,7 +52,7 @@ public:
/// <summary> /// <summary>
/// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation. /// Gets the break torque. Determines the maximum torque the joint can apply before breaking. Broken joints no longer participate in physics simulation.
/// </summary> /// </summary>
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\")") API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(float.MaxValue), EditorDisplay(\"Joint\"), NumberCategory(Utils.ValueCategory.Torque)")
FORCE_INLINE float GetBreakTorque() const FORCE_INLINE float GetBreakTorque() const
{ {
return _breakTorque; return _breakTorque;

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
namespace FlaxEngine
{
/// <summary>
/// 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).
/// </summary>
/// <seealso cref="System.Attribute" />
[Serializable]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class NumberCategoryAttribute : Attribute
{
/// <summary>
/// The value category used for formatting.
/// </summary>
public Utils.ValueCategory Category;
private NumberCategoryAttribute()
{
Category = Utils.ValueCategory.None;
}
/// <summary>
/// Initializes a new instance of the <see cref="NumberCategoryAttribute"/> class.
/// </summary>
/// <param name="category">The value category.</param>
public NumberCategoryAttribute(Utils.ValueCategory category)
{
Category = category;
}
}
}

View File

@@ -1038,5 +1038,23 @@ namespace FlaxEngine
parameterTypes = Array.Empty<Type>(); parameterTypes = Array.Empty<Type>();
return parameterTypes; return parameterTypes;
} }
/// <summary>
/// A category of number values used for formatting and input boxes
/// </summary>
public enum ValueCategory
{
None,
Distance,
Area,
Volume,
Mass,
Angle,
Speed,
Acceleration,
Time,
Force,
Torque
}
} }
} }