UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
UnitConversion.inl
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
8#include "CoreTypes.h"
9#include "CoreFwd.h"
10#include "Misc/OptionalFwd.h"
11
12struct FMath;
13struct FUnitConversion;
14enum class EUnit : uint8;
15enum class EUnitType;
16template<typename NumericType> struct FNumericUnit;
17template<typename ValueType, typename ErrorType> class TValueOrError;
18
19namespace UnitConversion
20{
22 CORE_API double DistanceUnificationFactor(EUnit From);
24 CORE_API double AngleUnificationFactor(EUnit From);
26 CORE_API double SpeedUnificationFactor(EUnit From);
28 CORE_API double AngularSpeedUnificationFactor(EUnit From);
30 CORE_API double AccelerationUnificationFactor(EUnit From);
32 CORE_API double MassUnificationFactor(EUnit From);
34 CORE_API double DensityUnificationFactor(EUnit From);
36 CORE_API double ForceUnificationFactor(EUnit From);
38 CORE_API double TorqueUnificationFactor(EUnit From);
40 CORE_API double PositionalImpulseUnificationFactor(EUnit From);
42 CORE_API double FrequencyUnificationFactor(EUnit From);
44 CORE_API double DataSizeUnificationFactor(EUnit From);
46 CORE_API double TimeUnificationFactor(EUnit From);
48 CORE_API double MultiplierUnificationFactor(EUnit From);
50 CORE_API double StressUnificationFactor(EUnit From);
51
54
66
68 CORE_API TOptional<const TArray<FQuantizationInfo>*> GetQuantizationBounds(EUnit Unit);
69
70} // namespace UnitConversion
71
73template<typename T>
75{
76 using namespace UnitConversion;
77
78 if (!AreUnitsCompatible(From, To))
79 {
80 return InValue;
81 }
82 else if (From == EUnit::Unspecified || To == EUnit::Unspecified)
83 {
84 return InValue;
85 }
86
88 {
89 case EUnitType::Distance: return static_cast<T>(InValue * DistanceUnificationFactor(From) * (1.0 / DistanceUnificationFactor(To)));
90 case EUnitType::Angle: return static_cast<T>(InValue * AngleUnificationFactor(From) * (1.0 / AngleUnificationFactor(To)));
91 case EUnitType::Speed: return static_cast<T>(InValue * SpeedUnificationFactor(From) * (1.0 / SpeedUnificationFactor(To)));
92 case EUnitType::AngularSpeed: return static_cast<T>(InValue * AngularSpeedUnificationFactor(From) * (1.0 / AngularSpeedUnificationFactor(To)));
93 case EUnitType::Acceleration: return static_cast<T>(InValue * AccelerationUnificationFactor(From) * (1.0 / AccelerationUnificationFactor(To)));
94 case EUnitType::Mass: return static_cast<T>(InValue * MassUnificationFactor(From) * (1.0 / MassUnificationFactor(To)));
95 case EUnitType::Density: return static_cast<T>(InValue * DensityUnificationFactor(From) * (1.0 / DensityUnificationFactor(To)));
96 case EUnitType::Force: return static_cast<T>(InValue * ForceUnificationFactor(From) * (1.0 / ForceUnificationFactor(To)));
97 case EUnitType::Torque: return static_cast<T>(InValue * TorqueUnificationFactor(From) * (1.0 / TorqueUnificationFactor(To)));
98 case EUnitType::PositionalImpulse: return static_cast<T>(InValue * PositionalImpulseUnificationFactor(From) * (1.0 / PositionalImpulseUnificationFactor(To)));
99 case EUnitType::Frequency: return static_cast<T>(InValue * FrequencyUnificationFactor(From) * (1.0 / FrequencyUnificationFactor(To)));
100 case EUnitType::DataSize: return static_cast<T>(InValue * DataSizeUnificationFactor(From) * (1.0 / DataSizeUnificationFactor(To)));
101 case EUnitType::LuminousFlux: return InValue;
102 case EUnitType::Time: return static_cast<T>(InValue * TimeUnificationFactor(From) * (1.0 / TimeUnificationFactor(To)));
103 case EUnitType::Multipliers: return static_cast<T>(InValue * MultiplierUnificationFactor(From) * (1.0 / MultiplierUnificationFactor(To)));
104 case EUnitType::Stress: return static_cast<T>(InValue * StressUnificationFactor(From) * (1.0 / StressUnificationFactor(To)));
105 // Temperature conversion is not just a simple multiplication, so needs special treatment
107 {
108 double NewValue = InValue;
109 // Put it into kelvin
110 if (From == EUnit::Celsius)
111 {
112 NewValue = NewValue + 273.15;
113 }
114 else if (From == EUnit::Farenheit)
115 {
116 NewValue = (NewValue + 459.67) * 5.0/9.0;
117 }
118 // And out again
119 if (To == EUnit::Celsius)
120 {
121 return static_cast<T>(NewValue - 273.15);
122 }
123 else if (To == EUnit::Farenheit)
124 {
125 return static_cast<T>(NewValue * 9.0/5.0 - 459.67);
126 }
127 else
128 {
129 return static_cast<T>(NewValue);
130 }
131 }
132
140 default: return InValue;
141 }
142}
143
144template<typename T>
146{
147 auto OptionalBounds = UnitConversion::GetQuantizationBounds(Units);
148 if (!OptionalBounds.IsSet())
149 {
150 return FNumericUnit<T>(Value, Units);
151 }
152
153 const auto& Bounds = *OptionalBounds.GetValue();
154
155 const int32 CurrentUnitIndex = (uint8)Units - (uint8)Bounds[0].Units;
156
157 EUnit NewUnits = Units;
158 double NewValue = Value;
159
160 if (FMath::Abs(NewValue) > 1)
161 {
162 // Large number? Try larger units
163 for (int32 Index = CurrentUnitIndex; Index < Bounds.Num(); ++Index)
164 {
165 if (Bounds[Index].Factor == 0)
166 {
167 break;
168 }
169
170 const auto Tmp = NewValue / Bounds[Index].Factor;
171
172 if (FMath::Abs(Tmp) < 1)
173 {
174 break;
175 }
176
177 NewValue = Tmp;
178 NewUnits = Bounds[Index + 1].Units;
179 }
180 }
181 else if (NewValue != 0)
182 {
183 // Small number? Try smaller units
184 for (int32 Index = CurrentUnitIndex - 1; Index >= 0; --Index)
185 {
186 NewValue *= Bounds[Index].Factor;
187 NewUnits = Bounds[Index].Units;
188
189 if (FMath::Abs(NewValue) > 1)
190 {
191 break;
192 }
193 }
194 }
195
196 return FNumericUnit<T>(NewValue, NewUnits);
197}
198
199template<typename T>
201{
203 {
204 return EUnit::Unspecified;
205 }
206
207 const TArray<EUnit>& DisplayUnits = Settings().GetDisplayUnits(GetUnitType(InUnits));
208 if (DisplayUnits.Num() == 0)
209 {
210 return QuantizeUnitsToBestFit(Value, InUnits).Units;
211 }
212 if (DisplayUnits.Num() == 1)
213 {
214 return DisplayUnits[0];
215 }
216
217 // If the value we were given was 0, change it to something we can actually work with
218 if (Value == 0)
219 {
220 Value = 1;
221 }
222
223 int32 BestIndex = 0;
224 for (int32 Index = 0; Index < DisplayUnits.Num() - 1; ++Index)
225 {
226 T Best, Next;
227 if constexpr (std::is_signed_v<T>)
228 {
229 Best = FMath::Abs(Convert(Value, InUnits, DisplayUnits[BestIndex]));
230 Next = FMath::Abs(Convert(Value, InUnits, DisplayUnits[Index + 1]));
231 }
232 else
233 {
234 Best = Convert(Value, InUnits, DisplayUnits[BestIndex]);
235 Next = Convert(Value, InUnits, DisplayUnits[Index + 1]);
236 }
237
238 if (Best < 1.0 && Next >= 1.0)
239 {
240 BestIndex = Index + 1;
241 }
242 else if (Best < 1.0 && Next < 1.0)
243 {
244 if (Next > Best)
245 {
246 BestIndex = Index + 1;
247 }
248 }
249 else if (Best >= 1.0 && Next >= 1.0)
250 {
251 if (FMath::LogX(10.0f, Next) < FMath::LogX(10.0f, Best))
252 {
253 BestIndex = Index + 1;
254 }
255 }
256 }
257
258 return DisplayUnits[BestIndex];
259}
260
261template<typename NumericType>
265
266template<typename NumericType>
270
271template<typename NumericType>
277
278template<typename NumericType>
280{
281 CopyValueWithConversion(Other);
282 return *this;
283}
284
286template<typename NumericType> template<typename OtherType>
292
293template<typename NumericType> template<typename OtherType>
295{
296 CopyValueWithConversion(Other);
297 return *this;
298}
299
301template<typename NumericType>
315
316template<typename NumericType>
321
322template<typename NumericType>
324{
325 TValueOrError<FNumericUnit<double>, FText> Result = UnitConversion::TryParseExpression(InExpression, InDefaultUnit, InExistingValue);
326 if (Result.IsValid())
327 {
328 const auto& Value = Result.GetValue();
329 return MakeValue(FNumericUnit<NumericType>((NumericType)Value.Value, Value.Units));
330 }
331
332 return MakeError(Result.GetError());
333}
334
335template<typename NumericType>
337{
339 if (!InSource || !*InSource)
340 {
341 return Result;
342 }
343
344 const TCHAR* NumberEnd = nullptr;
345 if (!ExtractNumberBoundary(InSource, NumberEnd))
346 {
347 return Result;
348 }
349
350 NumericType NewValue;
351 LexFromString(NewValue, InSource);
352
353 // Now parse the units
355
356 if (*NumberEnd == '\0')
357 {
358 // No units
359 Result.Emplace(NewValue);
360 }
361 else
362 {
363 // If the string specifies units, they must map to something that exists for this function to succeed
365 if (NewUnits)
366 {
367 Result.Emplace(NewValue, NewUnits.GetValue());
368 }
369 }
370
371 return Result;
372}
373
375template<typename NumericType> template<typename OtherType>
377{
378 if (Units != EUnit::Unspecified && Other.Units != EUnit::Unspecified)
379 {
380 if (Units == Other.Units)
381 {
382 Value = Other.Value;
383 }
384 else if (FUnitConversion::AreUnitsCompatible(Units, Other.Units))
385 {
386 Value = FUnitConversion::Convert(Other.Value, Other.Units, Units);
387 }
388 else
389 {
390 // Invalid conversion - assignment invalid
391 }
392 }
393 else
394 {
395 // If our units haven't been specified, we take on the units of the rhs
396 if (Units == EUnit::Unspecified)
397 {
398 // This is the only time we ever change units. Const_cast is 'acceptible' here since the units haven't been specified yet.
399 const_cast<EUnit&>(Units) = Other.Units;
400 }
401
402 Value = Other.Value;
403 }
404}
405
406template<typename NumericType>
408{
409 while(FChar::IsWhitespace(*Start)) ++Start;
410
411 End = Start;
412 if (*End == '-' || *End == '+')
413 {
414 End++;
415 }
416
417 bool bHasDot = false;
418 while (FChar::IsDigit(*End) || *End == '.')
419 {
420 if (*End == '.')
421 {
422 if (bHasDot)
423 {
424 return false;
425 }
426 bHasDot = true;
427 }
428 ++End;
429 }
430
431 return true;
432}
433
434template <typename NumericType>
435struct TNumericLimits<FNumericUnit<NumericType>> : public TNumericLimits<NumericType>
436{ };
437
438template <typename CharType, typename T>
440{
441 Builder << NumericUnit.Value;
442 Builder << ANSITEXTVIEW(" ");
444 return Builder;
445}
446
447template<typename T>
449{
450 TStringBuilder<128> Builder;
451 Builder << NumericUnit;
452 return FString(Builder);
453}
454
455template<typename T>
464
465template<typename T>
467{
469 if (Parsed)
470 {
471 OutValue = Parsed.GetValue();
472 }
473}
474
475template<typename T>
477{
479 if (Parsed)
480 {
481 OutValue = Parsed.GetValue();
482 return true;
483 }
484
485 return false;
486}
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define ANSITEXTVIEW(str)
Definition StringView.h:555
EUnit
Definition UnitConversion.h:19
@ Farenheit
@ Unspecified
EUnitType
Definition UnitConversion.h:98
@ PositionalImpulse
@ LuminousIntensity
FString LexToSanitizedString(const FNumericUnit< T > &NumericUnit)
Definition UnitConversion.inl:456
bool LexTryParseString(FNumericUnit< T > &OutValue, const TCHAR *String)
Definition UnitConversion.inl:476
void LexFromString(FNumericUnit< T > &OutValue, const TCHAR *String)
Definition UnitConversion.inl:466
FString LexToString(const FNumericUnit< T > &NumericUnit)
Definition UnitConversion.inl:448
TStringBuilderBase< CharType > & operator<<(TStringBuilderBase< CharType > &Builder, const FNumericUnit< T > &NumericUnit)
Definition UnitConversion.inl:439
UE_REWRITE TValueOrError_ErrorProxy< ArgTypes... > MakeError(ArgTypes &&... Args UE_LIFETIMEBOUND)
Definition ValueOrError.h:41
UE_REWRITE TValueOrError_ValueProxy< ArgTypes... > MakeValue(ArgTypes &&... Args UE_LIFETIMEBOUND)
Definition ValueOrError.h:35
uint8_t uint8
Definition binka_ue_file_header.h:8
Definition Text.h:385
CORE_API const TArray< EUnit > & GetDisplayUnits(EUnitType InType) const
Definition UnitConversion.cpp:603
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
Definition StringBuilder.h:79
Definition StringBuilder.h:509
Definition ValueOrError.h:58
@ Units
Definition ObjectMacros.h:1505
Definition UnitConversion.cpp:706
U16 Index
Definition radfft.cpp:71
Definition UnrealMathUtility.h:270
Definition UnitConversion.h:185
TOptional< FNumericUnit< NumericType > > ConvertTo(EUnit ToUnits) const
Definition UnitConversion.inl:302
static TValueOrError< FNumericUnit< NumericType >, FText > TryParseExpression(const TCHAR *InExpression, EUnit InDefaultUnit, const FNumericUnit< NumericType > &InExistingValue)
Definition UnitConversion.inl:323
FNumericUnit & operator=(const FNumericUnit &Other)
Definition UnitConversion.inl:279
static TOptional< FNumericUnit< NumericType > > TryParseString(const TCHAR *InSource)
Definition UnitConversion.inl:336
FNumericUnit()
Definition UnitConversion.inl:262
FNumericUnit< NumericType > QuantizeUnitsToBestFit() const
Definition UnitConversion.inl:317
NumericType Value
Definition UnitConversion.h:187
Definition UnitConversion.h:140
static CORE_API FUnitSettings & Settings()
Definition UnitConversion.cpp:640
static CORE_API EUnitType GetUnitType(EUnit)
Definition UnitConversion.cpp:659
static EUnit CalculateDisplayUnit(T Value, EUnit InUnits)
Definition UnitConversion.inl:200
static CORE_API TOptional< EUnit > UnitFromString(const TCHAR *UnitString)
Definition UnitConversion.cpp:682
static CORE_API bool AreUnitsCompatible(EUnit From, EUnit To)
Definition UnitConversion.cpp:647
static FNumericUnit< T > QuantizeUnitsToBestFit(T Value, EUnit Units)
Definition UnitConversion.inl:145
static T Convert(T InValue, EUnit From, EUnit To)
Definition UnitConversion.inl:74
static CORE_API const TCHAR * GetUnitDisplayString(EUnit Unit)
Definition UnitConversion.cpp:669
static bool IsDigit(CharType Char)
Definition Char.h:240
static bool IsWhitespace(CharType Char)
Definition Char.h:282
Definition NumericLimits.h:41
Definition Optional.h:131
Definition UnitConversion.inl:57
EUnit Units
Definition UnitConversion.inl:59
FQuantizationInfo(EUnit InUnit, float InFactor)
Definition UnitConversion.inl:64
float Factor
Definition UnitConversion.inl:61