10#include "Misc/OptionalFwd.h"
14enum class EUnit : uint8;
16template<
typename NumericType>
struct FNumericUnit;
17template<
typename ValueType,
typename ErrorType>
class TValueOrError;
64T FUnitConversion::Convert(T InValue, EUnit From, EUnit To)
66 using namespace UnitConversion;
68 if (!AreUnitsCompatible(From, To))
72 else if (From == EUnit::Unspecified || To == EUnit::Unspecified)
77 switch(FUnitConversion::GetUnitType(From))
79 case EUnitType::Distance:
return InValue * DistanceUnificationFactor(From) * (1.0 / DistanceUnificationFactor(To));
80 case EUnitType::Angle:
return InValue * AngleUnificationFactor(From) * (1.0 / AngleUnificationFactor(To));
81 case EUnitType::Speed:
return InValue * SpeedUnificationFactor(From) * (1.0 / SpeedUnificationFactor(To));
82 case EUnitType::Mass:
return InValue * MassUnificationFactor(From) * (1.0 / MassUnificationFactor(To));
83 case EUnitType::Force:
return InValue * ForceUnificationFactor(From) * (1.0 / ForceUnificationFactor(To));
84 case EUnitType::Frequency:
return InValue * FrequencyUnificationFactor(From) * (1.0 / FrequencyUnificationFactor(To));
85 case EUnitType::DataSize:
return InValue * DataSizeUnificationFactor(From) * (1.0 / DataSizeUnificationFactor(To));
86 case EUnitType::LuminousFlux:
return InValue;
87 case EUnitType::Time:
return InValue * TimeUnificationFactor(From) * (1.0 / TimeUnificationFactor(To));
88 case EUnitType::Multipliers:
return InValue * MultiplierUnificationFactor(From) * (1.0 / MultiplierUnificationFactor(To));
90 case EUnitType::Temperature:
92 double NewValue = InValue;
96 case EUnit::Celsius: NewValue = NewValue + 273.15f;
break;
97 case EUnit::Farenheit: NewValue = (NewValue + 459.67f) * 5.f/9.f;
break;
103 case EUnit::Celsius:
return NewValue - 273.15f;
104 case EUnit::Farenheit:
return NewValue * 9.f/5.f - 459.67f;
105 default:
return NewValue;
109 default:
return InValue;
114FNumericUnit<T> FUnitConversion::QuantizeUnitsToBestFit(T Value, EUnit Units)
116 auto OptionalBounds = UnitConversion::GetQuantizationBounds(Units);
117 if (!OptionalBounds.IsSet())
119 return FNumericUnit<T>(Value, Units);
122 const auto& Bounds = *OptionalBounds.GetValue();
124 const int32 CurrentUnitIndex = (uint8)Units - (uint8)Bounds[0].Units;
126 EUnit NewUnits = Units;
127 double NewValue = Value;
129 if (FMath::Abs(NewValue) > 1)
132 for (int32 Index = CurrentUnitIndex; Index < Bounds.Num(); ++Index)
134 if (Bounds[Index].Factor == 0)
139 const auto Tmp = NewValue / Bounds[Index].Factor;
141 if (FMath::Abs(Tmp) < 1)
147 NewUnits = Bounds[Index + 1].Units;
150 else if (NewValue != 0)
153 for (int32 Index = CurrentUnitIndex - 1; Index >= 0; --Index)
155 NewValue *= Bounds[Index].Factor;
156 NewUnits = Bounds[Index].Units;
158 if (FMath::Abs(NewValue) > 1)
165 return FNumericUnit<T>(NewValue, NewUnits);
169EUnit FUnitConversion::CalculateDisplayUnit(T Value, EUnit InUnits)
171 if (InUnits == EUnit::Unspecified)
173 return EUnit::Unspecified;
176 const TArray<EUnit>& DisplayUnits = Settings().GetDisplayUnits(GetUnitType(InUnits));
177 if (DisplayUnits.Num() == 0)
179 return QuantizeUnitsToBestFit(Value, InUnits).Units;
181 else if (DisplayUnits.Num() == 1)
183 return DisplayUnits[0];
193 for (int32 Index = 0; Index < DisplayUnits.Num() - 1; ++Index)
195 double This = Convert(Value, InUnits, DisplayUnits[Index]);
196 double Next = Convert(Value, InUnits, DisplayUnits[Index + 1]);
198 if (FMath::Abs(FMath::LogX(10.0f, (
float)This)) < FMath::Abs(FMath::LogX(10.0f, (
float)Next)))
204 BestIndex = Index + 1;
208 return DisplayUnits[BestIndex];
211template<
typename NumericType>
212FNumericUnit<NumericType>::FNumericUnit()
213 : Units(EUnit::Unspecified)
216template<
typename NumericType>
217FNumericUnit<NumericType>::FNumericUnit(
const NumericType& InValue, EUnit InUnits)
218 : Value(InValue), Units(InUnits)
221template<
typename NumericType>
222FNumericUnit<NumericType>::FNumericUnit(
const FNumericUnit& Other)
223 : Units(EUnit::Unspecified)
228template<
typename NumericType>
229FNumericUnit<NumericType>& FNumericUnit<NumericType>::operator=(
const FNumericUnit& Other)
231 CopyValueWithConversion(Other);
236template<
typename NumericType>
template<
typename OtherType>
237FNumericUnit<NumericType>::FNumericUnit(
const FNumericUnit<OtherType>& Other)
242template<
typename NumericType>
template<
typename OtherType>
243FNumericUnit<NumericType>& FNumericUnit<NumericType>::operator=(
const FNumericUnit<OtherType>& Other)
245 CopyValueWithConversion(Other);
250template<
typename NumericType>
251TOptional<FNumericUnit<NumericType>> FNumericUnit<NumericType>::ConvertTo(EUnit ToUnits)
const
253 if (Units == EUnit::Unspecified)
255 return FNumericUnit(Value, ToUnits);
257 else if (FUnitConversion::AreUnitsCompatible(Units, ToUnits))
259 return FNumericUnit<NumericType>(FUnitConversion::Convert(Value, Units, ToUnits), ToUnits);
262 return TOptional<FNumericUnit<NumericType>>();
265template<
typename NumericType>
266FNumericUnit<NumericType> FNumericUnit<NumericType>::QuantizeUnitsToBestFit()
const
268 return FUnitConversion::QuantizeUnitsToBestFit(Value, Units);
271template<
typename NumericType>
272TValueOrError<FNumericUnit<NumericType>, FText> FNumericUnit<NumericType>::TryParseExpression(
const TCHAR* InExpression, EUnit InDefaultUnit,
const FNumericUnit<NumericType>& InExistingValue)
274 TValueOrError<FNumericUnit<
double>, FText> Result = UnitConversion::TryParseExpression(InExpression, InDefaultUnit, InExistingValue);
275 if (Result.IsValid())
277 const auto& Value = Result.GetValue();
278 return MakeValue(FNumericUnit<NumericType>((NumericType)Value.Value, Value.Units));
281 return MakeError(Result.GetError());
284template<
typename NumericType>
285TOptional<FNumericUnit<NumericType>> FNumericUnit<NumericType>::TryParseString(
const TCHAR* InSource)
287 TOptional<FNumericUnit<NumericType>> Result;
288 if (!InSource || !*InSource)
293 const TCHAR* NumberEnd =
nullptr;
294 if (!ExtractNumberBoundary(InSource, NumberEnd))
299 NumericType NewValue;
300 LexFromString(NewValue, InSource);
303 while(FChar::IsWhitespace(*NumberEnd)) ++NumberEnd;
305 if (*NumberEnd ==
'\0')
308 Result.Emplace(NewValue);
313 auto NewUnits = FUnitConversion::UnitFromString(NumberEnd);
316 Result.Emplace(NewValue, NewUnits.GetValue());
324template<
typename NumericType>
template<
typename OtherType>
325void FNumericUnit<NumericType>::CopyValueWithConversion(
const FNumericUnit<OtherType>& Other)
327 if (Units != EUnit::Unspecified && Other.Units != EUnit::Unspecified)
329 if (Units == Other.Units)
333 else if (FUnitConversion::AreUnitsCompatible(Units, Other.Units))
335 Value = FUnitConversion::Convert(Other.Value, Other.Units, Units);
345 if (Units == EUnit::Unspecified)
348 const_cast<EUnit&>(Units) = Other.Units;
355template<
typename NumericType>
356bool FNumericUnit<NumericType>::ExtractNumberBoundary(
const TCHAR* Start,
const TCHAR*& End)
358 while(FChar::IsWhitespace(*Start)) ++Start;
361 if (*End ==
'-' || *End ==
'+')
366 bool bHasDot =
false;
367 while (FChar::IsDigit(*End) || *End ==
'.')
383template <
typename NumericType>
384struct TNumericLimits<FNumericUnit<NumericType>> :
public TNumericLimits<NumericType>
390 FString String = LexToString(NumericUnit.Value);
392 String += FUnitConversion::GetUnitDisplayString(NumericUnit.Units);
400 FString String = LexToSanitizedString(NumericUnit.Value);
402 String += FUnitConversion::GetUnitDisplayString(NumericUnit.Units);
410 auto Parsed = FNumericUnit<T>::TryParseString(String);
413 OutValue = Parsed.GetValue();
420 auto Parsed = FNumericUnit<T>::TryParseString(String);
423 OutValue = Parsed.GetValue();
FString LexToSanitizedString(const FNumericUnit< T > &NumericUnit)
bool LexTryParseString(FNumericUnit< T > &OutValue, const TCHAR *String)
void LexFromString(FNumericUnit< T > &OutValue, const TCHAR *String)
FString LexToString(const FNumericUnit< T > &NumericUnit)
CORE_API double TemperatureUnificationFactor(EUnit From)
CORE_API double TimeUnificationFactor(EUnit From)
CORE_API double MassUnificationFactor(EUnit From)
CORE_API double DataSizeUnificationFactor(EUnit From)
CORE_API double AngleUnificationFactor(EUnit From)
CORE_API double FrequencyUnificationFactor(EUnit From)
CORE_API double DistanceUnificationFactor(EUnit From)
CORE_API double SpeedUnificationFactor(EUnit From)
CORE_API double MultiplierUnificationFactor(EUnit From)
CORE_API double ForceUnificationFactor(EUnit From)
FQuantizationInfo(EUnit InUnit, float InFactor)