// Copyright 2020 Phyronnaz #pragma once #include #include "CoreMinimal.h" #include "VoxelValue.h" #include "VoxelMinimal.h" #include "VoxelUtilities/VoxelBaseUtilities.h" #include "VoxelRange.generated.h" class VOXEL_API FVoxelRangeFailStatus : private TThreadSingleton { public: static FVoxelRangeFailStatus& Get(); bool HasFailed() const { return bHasFailed; } bool HasWarning() const { return bHasWarning; } const TCHAR* GetMessage() const { return Message; } public: void Fail(const TCHAR* InError) { // Note: bHasFailed might be true already if the generated graph has scoped ifs that failed if (!HasFailed()) { bHasFailed = true; Message = InError; } } void Warning(const TCHAR* InError) { if (!HasFailed() && !HasWarning()) { bHasWarning = true; Message = InError; } } void Reset() { bHasFailed = false; bHasWarning = false; Message = nullptr; } private: bool bHasFailed = false; bool bHasWarning = false; const TCHAR* Message = nullptr; friend TThreadSingleton; }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// struct FVoxelBoolRange { bool bCanBeTrue = true; bool bCanBeFalse = true; FVoxelBoolRange() = default; FVoxelBoolRange(bool bValue) { if (bValue) { bCanBeTrue = true; bCanBeFalse = false; } else { bCanBeTrue = false; bCanBeFalse = true; } } FVoxelBoolRange(bool bCanBeTrue, bool bCanBeFalse) : bCanBeTrue(bCanBeTrue) , bCanBeFalse(bCanBeFalse) { check(bCanBeTrue || bCanBeFalse); } FString ToString() const { return bCanBeTrue && bCanBeFalse ? "true, false" : bCanBeTrue ? "true" : "false"; } FVoxelBoolRange operator!() const { return { bCanBeFalse, bCanBeTrue }; } FVoxelBoolRange operator&&(const FVoxelBoolRange& Other) const { if (!bCanBeFalse && !Other.bCanBeFalse) { return FVoxelBoolRange::True(); } else if (!bCanBeTrue || !Other.bCanBeTrue) { return FVoxelBoolRange::False(); } else { return FVoxelBoolRange::TrueOrFalse(); } } FVoxelBoolRange operator||(const FVoxelBoolRange& Other) const { if (!bCanBeFalse || !Other.bCanBeFalse) { return FVoxelBoolRange::True(); } else if (!bCanBeTrue && !Other.bCanBeTrue) { return FVoxelBoolRange::False(); } else { return FVoxelBoolRange::TrueOrFalse(); } } operator bool() const { if (bCanBeTrue && !bCanBeFalse) { return true; } else if (!bCanBeTrue && bCanBeFalse) { return false; } else { checkVoxelSlow(bCanBeTrue && bCanBeFalse); FVoxelRangeFailStatus::Get().Fail(TEXT("condition can be true or false")); return false; } } static FVoxelBoolRange True() { return { true, false }; } static FVoxelBoolRange False() { return { false, true }; } static FVoxelBoolRange TrueOrFalse() { return { true, true }; } static bool If(const FVoxelBoolRange& Condition, bool bDefaultValue) { auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); if (RangeFailStatus.HasFailed()) { return true; // If already failed do nothing } const bool bCondition = Condition; if (RangeFailStatus.HasFailed()) { RangeFailStatus.Reset(); return bDefaultValue; } else { return bCondition; } } }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template T NegativeInfinity(); template T PositiveInfinity(); template<> inline constexpr float NegativeInfinity() { return -std::numeric_limits::infinity(); } template<> inline constexpr float PositiveInfinity() { return std::numeric_limits::infinity(); } template<> inline constexpr double NegativeInfinity() { return -std::numeric_limits::infinity(); } template<> inline constexpr double PositiveInfinity() { return std::numeric_limits::infinity(); } template<> inline constexpr int32 NegativeInfinity() { return MIN_int32; } template<> inline constexpr int32 PositiveInfinity() { return MAX_int32; } template<> inline constexpr uint16 NegativeInfinity() { return MIN_uint16; } template<> inline constexpr uint16 PositiveInfinity() { return MAX_uint16; } template<> inline constexpr FVoxelValue NegativeInfinity() { return FVoxelValue::Full(); } template<> inline constexpr FVoxelValue PositiveInfinity() { return FVoxelValue::Empty(); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template FString PrettyPrint(const T& Value) { return LexToString(Value); } template<> inline FString PrettyPrint(const float& Value) { return FString::SanitizeFloat(Value); } template<> inline FString PrettyPrint(const double& Value) { return FString::SanitizeFloat(Value); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template struct TVoxelRange { T Min; T Max; TVoxelRange() = default; TVoxelRange(T Value) : Min(Value) , Max(Value) { } TVoxelRange(T Min, T Max) : Min(Min) , Max(Max) { ensure(Min <= Max); } template explicit TVoxelRange(const TVoxelRange& Range) : Min(Range.Min) , Max(Range.Max) { } public: static constexpr bool bIsInteger = TIsIntegral::Value; public: template static TVoxelRange FromList(TArgs... Values) { return { FVoxelUtilities::VariadicMin(Values...), FVoxelUtilities::VariadicMax(Values...) }; } static TVoxelRange Union(const TVoxelRange& A, const TVoxelRange& B) { return { FMath::Min(A.Min, B.Min), FMath::Max(A.Max, B.Max) }; } static TVoxelRange Intersection(const TVoxelRange& A, const TVoxelRange& B) { const T NewMin = FMath::Max(A.Min, B.Min); const T NewMax = FMath::Min(A.Max, B.Max); if (ensure(NewMin <= NewMax)) { return { NewMin, NewMax }; } else { return Union(A, B); } } template static TVoxelRange Union(const TVoxelRange& A, const TVoxelRange& B, TArgs... Args) { return Union(Union(A, B), Args...); } template static TVoxelRange Intersection(const TVoxelRange& A, const TVoxelRange& B, TArgs... Args) { return Intersection(Intersection(A, B), Args...); } static TVoxelRange Infinite() { return { NegativeInfinity(), PositiveInfinity() }; } static TVoxelRange PositiveInfinite() { return { 0, PositiveInfinity() }; } static TVoxelRange NegativeInfinite() { return { NegativeInfinity(), 0 }; } FString ToString() const { return IsSingleValue() ? PrettyPrint(Min) : FString::Printf(TEXT("%s, %s"), *PrettyPrint(Min), *PrettyPrint(Max)); } template bool Contains(const TOther& Other) const { return Min <= Other && Other <= Max; } template bool Contains(const TVoxelRange& Other) const { return Min <= Other.Min && Other.Max <= Max; } template bool Intersects(const TVoxelRange& Other) const { return Contains(Other.Min) || Contains(Other.Max) || Other.Contains(Min) || Other.Contains(Max); } bool IsSingleValue() const { return Min == Max; } T GetSingleValue() const { ensure(IsSingleValue()); return Min; } bool IsSingleSign() const { return Min == 0 || Max == 0 || (Min < 0) == (Max < 0); } T GetSign() const { ensure(IsSingleSign()); return Min == 0 ? FMath::Sign(Max) : FMath::Sign(Min); } T GetSign_NotZero() const { ensureVoxelSlow(IsSingleSign()); ensureVoxelSlow(Min != 0 && Max != 0); ensureVoxelSlow((Min < 0) == (Max < 0)); return Min < 0 ? -1 : 1; } TVoxelRange ExtendToInfinity() const { TVoxelRange New = *this; if (Min < 0) { New.Min = NegativeInfinity(); New.Max = Max > 0 ? PositiveInfinity() : 0; } else { New.Min = 0; New.Max = PositiveInfinity(); } return New; } bool IsNegativeInfinity() const { return Min == NegativeInfinity(); } bool IsPositiveInfinity() const { return Max == PositiveInfinity(); } bool IsInfinity() const { return IsNegativeInfinity() || IsPositiveInfinity(); } template TVoxelRange& operator=(const TVoxelRange& Other) { Min = Other.Min; Max = Other.Max; return *this; } template auto Apply(F Op) const { return TVoxelRange{ Op(Min), Op(Max) }; } public: template FVoxelBoolRange operator==(const TVoxelRange& Other) const { if (IsSingleValue() && Other.IsSingleValue() && Min == Other.Min) { checkVoxelSlow(Max == Other.Max); return FVoxelBoolRange::True(); } else if (!Intersects(Other)) { return FVoxelBoolRange::False(); } else { return FVoxelBoolRange::TrueOrFalse(); } } template FVoxelBoolRange operator!=(const TVoxelRange& Other) const { return !(*this == Other); } template FVoxelBoolRange operator<(const TVoxelRange& Other) const { if (Max < Other.Min) { return FVoxelBoolRange::True(); } else if (Other.Max <= Min) { return FVoxelBoolRange::False(); } else { return FVoxelBoolRange::TrueOrFalse(); } } template FVoxelBoolRange operator>(const TVoxelRange& Other) const { if (Min > Other.Max) { return FVoxelBoolRange::True(); } else if (Other.Min >= Max) { return FVoxelBoolRange::False(); } else { return FVoxelBoolRange::TrueOrFalse(); } } template FVoxelBoolRange operator<=(const TVoxelRange& Other) const { return !(*this > Other); } template FVoxelBoolRange operator>=(const TVoxelRange& Other) const { return !(*this < Other); } public: template TVoxelRange operator+(const TVoxelRange& Other) const { return { Min + Other.Min, Max + Other.Max }; } template TVoxelRange operator-(const TVoxelRange& Other) const { return { Min - Other.Max, Max - Other.Min }; } TVoxelRange operator-() const { return { -Max, -Min }; } template TVoxelRange operator*(const TVoxelRange& Other) const { return TVoxelRange::FromList(Min * Other.Min, Min * Other.Max, Max * Other.Min, Max * Other.Max); } template TVoxelRange operator/(const TVoxelRange& Other) const { if (Other.IsSingleValue() && Other.GetSingleValue() == 0) { if (bIsInteger) { // That's how integer / 0 is handled in voxel graphs return 0; } if (IsSingleValue() && GetSingleValue() == 0) { FVoxelRangeFailStatus::Get().Warning(TEXT("0 / 0 encountered, will result in a nan")); return Infinite(); } if (0 < Min) { // Single value: +inf return PositiveInfinity(); } if (Max < 0) { // Single value: -inf return NegativeInfinity(); } return Infinite(); } if (!Other.Contains(0)) // Will also handle single value cases { if (Other.IsInfinity()) { ensureVoxelSlow(Other.IsSingleSign()); // Does not contain 0 ensureVoxelSlow(Other.GetSign() != 0); // Else wouldn't be infinity, and does not contain 0 const auto Inf = ExtendToInfinity(); return TVoxelRange::FromList(Inf.Min / Other.GetSign_NotZero(), Inf.Max / Other.GetSign_NotZero()); } return TVoxelRange::FromList(Min / Other.Min, Min / Other.Max, Max / Other.Min, Max / Other.Max); } else { if (Other.IsSingleSign()) { ensureVoxelSlow(Other.GetSign() != 0); // Else would be a single value const auto Inf = ExtendToInfinity(); return TVoxelRange::FromList(Inf.Min / Other.GetSign_NotZero(), Inf.Max / Other.GetSign_NotZero()); } return Infinite(); } } public: TVoxelRange operator+(T Other) const { return { Min + Other, Max + Other }; } TVoxelRange operator-(T Other) const { return { Min - Other, Max - Other}; } TVoxelRange operator*(T Other) const { return { FMath::Min(Min * Other, Max * Other), FMath::Max(Min * Other, Max * Other) }; } TVoxelRange operator/(T Other) const { return { FMath::Min(Min / Other, Max / Other), FMath::Max(Min / Other, Max / Other) }; } friend FVoxelBoolRange operator==(const TVoxelRange& Range, T Other) { return Range == TVoxelRange(Other); } friend FVoxelBoolRange operator<(const TVoxelRange& Range, T Other) { return Range < TVoxelRange(Other); } friend FVoxelBoolRange operator>(const TVoxelRange& Range, T Other) { return Range > TVoxelRange(Other); } friend FVoxelBoolRange operator<=(const TVoxelRange& Range, T Other) { return Range <= TVoxelRange(Other); } friend FVoxelBoolRange operator>=(const TVoxelRange& Range, T Other) { return Range >= TVoxelRange(Other); } friend TVoxelRange operator-(T Other, const TVoxelRange& Range) { return TVoxelRange(Other) - Range; } friend TVoxelRange operator+(T Other, const TVoxelRange& Range) { return TVoxelRange(Other) + Range; } friend TVoxelRange operator*(T Other, const TVoxelRange& Range) { return TVoxelRange(Other) * Range; } friend TVoxelRange operator/(T Other, const TVoxelRange& Range) { return TVoxelRange(Other) / Range; } template TVoxelRange& operator-=(const U& Other) { *this = *this - Other; return *this; } template TVoxelRange& operator+=(const U& Other) { *this = *this + Other; return *this; } template TVoxelRange& operator*=(const U& Other) { *this = *this * Other; return *this; } template TVoxelRange& operator/=(const U& Other) { *this = *this / Other; return *this; } }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// template FArchive& operator<<(FArchive& Ar, TVoxelRange& Range) { Ar << Range.Min; Ar << Range.Max; return Ar; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// struct FVoxelMaterialRange { FVoxelMaterialRange() = default; FVoxelMaterialRange(const struct FVoxelMaterial&) {} }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// struct FVoxelColorRange { FVoxelColorRange() = default; FVoxelColorRange(const struct FColor&) {} }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // For display and serialization USTRUCT() struct FVoxelRange { GENERATED_BODY() UPROPERTY(EditAnywhere, Category = "Voxel") double Min = 0; UPROPERTY(EditAnywhere, Category = "Voxel") double Max = 0; FVoxelRange() = default; FVoxelRange(float Min, float Max) : Min(Min) , Max(Max) { } FVoxelRange(const TVoxelRange& Range) : Min(Range.Min) , Max(Range.Max) { } operator TVoxelRange() const { return { v_flt(Min), v_flt(Max) }; } };