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:
@@ -121,6 +121,39 @@ 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
|
||||
};
|
||||
|
||||
/// <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>
|
||||
@@ -156,7 +189,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 +203,24 @@ namespace FlaxEditor.Utilities
|
||||
public static IEnumerable<Token> 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 +290,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 +390,7 @@ namespace FlaxEditor.Utilities
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ParsingException("unknown variable");
|
||||
throw new ParsingException($"unknown variable : {token.Value}");
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
36
Source/Editor/Utilities/Units.cs
Normal file
36
Source/Editor/Utilities/Units.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1244,6 +1244,72 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Formats the floating point value (double precision) into the readable text representation.
|
||||
/// </summary>
|
||||
@@ -1255,7 +1321,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 +1336,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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user