// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Misc/AssertionMacros.h" #include "Math/IntPoint.h" #include "Math/UnrealMathUtility.h" #include "Math/IntRect.h" namespace UE::Math { /** * Enumerates the sides of a margin set by index. * Used for indexed access to individual sides (Left, Right, Top, Bottom). */ enum class EMarginSetSideIndex : uint8 { Left = 0, Right = 1, Top = 2, Bottom = 3, }; /** * TMarginSet is a template struct that stores a set of four directional values: * Left, Right, Top, and Bottom. */ template struct alignas(16) TMarginSet { public: /* * +--------------------------+ * | Top | * | +-------+ | * | Left | | Right | * | +-------+ | * | Bottom | * +--------------------------+ */ T Left; T Right; T Top; T Bottom; private: static constexpr int32 NumSides = 4; public: /** * Default constructor. * * The default margin size is zero on all four sides. */ TMarginSet() : Left(T(0)) , Right(T(0)) , Top(T(0)) , Bottom(T(0)) { } /** * Constructs a floating-point TMarginSet from a single arithmetic value U. * * Enabled only when T is floating-point and U is a built-in numeric type. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_floating_point_v && std::is_arithmetic_v>) explicit (!std::is_convertible_v) TMarginSet(const U InAll) : Left(static_cast(InAll)) , Right(static_cast(InAll)) , Top(static_cast(InAll)) , Bottom(static_cast(InAll)) { } /** * Constructs an integral TMarginSet from a single integral value U. * * Enabled only when both T and U are integral types. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_integral_v && std::is_integral_v) explicit(!std::is_convertible_v) TMarginSet(const IntType InAll) : Left(static_cast(InAll)) , Right(static_cast(InAll)) , Top(static_cast(InAll)) , Bottom(static_cast(InAll)) { } /** * Constructs a floating-point TMarginSet from four values of type U. * * Enabled only when T is floating-point and U is a built-in numeric type. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_floating_point_v && std::is_arithmetic_v>) explicit (!std::is_convertible_v) TMarginSet( const U InLeft, const U InRight, const U InTop, const U InBottom) : Left (static_cast(InLeft)) , Right (static_cast(InRight)) , Top (static_cast(InTop)) , Bottom (static_cast(InBottom)) { } /** * Constructs an integral TMarginSet from four integral values of type U. * * Enabled only when both T and U are integral types. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_integral_v && std::is_integral_v) explicit (!std::is_convertible_v) TMarginSet( const IntType InLeft, const IntType InRight, const IntType InTop, const IntType InBottom) : Left (static_cast(InLeft)) , Right (static_cast(InRight)) , Top (static_cast(InTop)) , Bottom (static_cast(InBottom)) { } /** * Constructs a floating-point TMarginSet from another TMarginSet. * * Enabled only when T is floating-point and U is a built-in numeric type. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_floating_point_v && std::is_arithmetic_v>) explicit (!std::is_convertible_v) TMarginSet(const TMarginSet& Other) : Left (static_cast(Other.Left)) , Right (static_cast(Other.Right)) , Top (static_cast(Other.Top)) , Bottom (static_cast(Other.Bottom)) { } /** * Constructs an integral TMarginSet from another TMarginSet. * * Enabled only when both T and U are integral types. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_integral_v && std::is_integral_v) explicit(!std::is_convertible_v) TMarginSet(const TMarginSet& Other) : Left (static_cast(Other.Left)) , Right (static_cast(Other.Right)) , Top (static_cast(Other.Top)) , Bottom (static_cast(Other.Bottom)) { } /** * Constructs a floating-point TMarginSet from a TVector4. * * Enabled only when T is floating-point and U is a built-in numeric type. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_floating_point_v && std::is_arithmetic_v>) explicit (!std::is_convertible_v) TMarginSet(const TVector4& Vec) : Left (static_cast(Vec.X)) , Right (static_cast(Vec.Y)) , Top (static_cast(Vec.Z)) , Bottom (static_cast(Vec.W)) { } /** * Constructs a TMarginSet from a TIntVector4 with integral elements. * * Enabled only when U is an integral type. * Conversion is explicit if U is not implicitly convertible to T. */ template requires (std::is_integral_v) explicit (!std::is_convertible_v) TMarginSet(const TIntVector4& Vec) : Left (static_cast(Vec.X)) , Right (static_cast(Vec.Y)) , Top (static_cast(Vec.Z)) , Bottom (static_cast(Vec.W)) { } public: /** * Converts a floating-point TMarginSet to TVector4. * * Enabled only when T is floating-point. * Marked [[nodiscard]] to warn if the result is ignored. */ template requires (std::is_floating_point_v) [[nodiscard]] inline operator TVector4() const { return TVector4( Left, Right, Top, Bottom); } /** * Converts this integral TMarginSet to a TIntVector4. * * Enabled only when both T (this type) and U (defaults to T) are integral. * [[nodiscard]] warns if the result is unused. */ template requires (std::is_integral_v && std::is_integral_v) [[nodiscard]] inline operator TIntVector4() const { return TIntVector4( Left, Right, Top, Bottom); } public: /** * Assigns margins from a TVector4 to a floating-point TMarginSet. * * Enabled only when: * - T is floating-point * - U is a built-in numeric type * - U is convertible to T */ template requires (std::is_floating_point_v && std::is_arithmetic_v> && std::is_convertible_v) inline TMarginSet& AssignMargins(const TVector4& InVector) { Left = static_cast(InVector.X); Right = static_cast(InVector.Y); Top = static_cast(InVector.Z); Bottom = static_cast(InVector.W); return *this; } /** * Assigns margins from a TIntVector4 to this TMarginSet. * * Enabled only when: * - IntType is an integral type * - IntType is convertible to T * * @param InVector The TIntVector4 containing the new margin values. * @return Reference to this TMarginSet after assignment. */ template requires (std::is_integral_v && std::is_convertible_v) inline TMarginSet& AssignMargins(const TIntVector4& InVector) { Left = static_cast(InVector.X); Right = static_cast(InVector.Y); Top = static_cast(InVector.Z); Bottom = static_cast(InVector.W); return *this; } /** * Assigns margins from another TMarginSet to a floating-point TMarginSet. * * Enabled only when: * - T is floating-point * - U is a built-in numeric type * - U is convertible to T */ template requires (std::is_floating_point_v && std::is_arithmetic_v> && std::is_convertible_v) inline TMarginSet& AssignMargins(const TMarginSet& Other) { Left = static_cast(Other.Left); Right = static_cast(Other.Right); Top = static_cast(Other.Top); Bottom = static_cast(Other.Bottom); return *this; } /** * Assigns margins from another TMarginSet to an integral TMarginSet. * * Enabled only when: * - T and IntType are integral types * - IntType is convertible to T */ template requires (std::is_integral_v && std::is_integral_v && std::is_convertible_v) inline TMarginSet& AssignMargins(const TMarginSet& Other) { Left = static_cast(Other.Left); Right = static_cast(Other.Right); Top = static_cast(Other.Top); Bottom = static_cast(Other.Bottom); return *this; } /** * Assigns margins from four numeric values of type U to a floating-point TMarginSet. * * Enabled only when: * - T is floating-point * - U is a built-in numeric type * - U is convertible to T */ template requires (std::is_floating_point_v && std::is_arithmetic_v> && std::is_convertible_v) inline TMarginSet& AssignMargins(const U InLeft, const U InRight, const U InTop, const U InBottom) { Left = static_cast(InLeft); Right = static_cast(InRight); Top = static_cast(InTop); Bottom = static_cast(InBottom); return *this; } /** * Assigns margins from four integral values of type IntType to an integral TMarginSet. * * Enabled only when: * - T and IntType are integral types * - IntType is convertible to T */ template requires (std::is_integral_v && std::is_integral_v && std::is_convertible_v) inline TMarginSet& AssignMargins( const IntType InLeft, const IntType InRight, const IntType InTop, const IntType InBottom) { Left = static_cast(InLeft); Right = static_cast(InRight); Top = static_cast(InTop); Bottom = static_cast(InBottom); return *this; } public: /** * Multiplies each margin of a TMarginSet by an integral scalar value. * * @param InScale Integral multiplier applied to all margins. * @return A TMarginSet where each margin is scaled by InScale. */ template requires (std::is_integral_v && std::is_convertible_v) [[nodiscard]] inline TMarginSet operator*(const IntType InScale) const { const T Scale = static_cast(InScale); return TMarginSet( Left * Scale, Right * Scale, Top * Scale, Bottom * Scale ); } /** * Multiplies each margin of a floating-point TMarginSet by a scalar value. * * @param InScale Scalar multiplier applied to all margins. * @return A TMarginSet where each margin is scaled by InScale. */ template requires (std::is_floating_point_v && !std::is_integral_v && std::is_arithmetic_v>) [[nodiscard]] inline TMarginSet operator*(const U InScale) const { const T Scale = static_cast(InScale); return TMarginSet( Left * Scale, Right * Scale, Top * Scale, Bottom * Scale ); } /** * Multiplies each margin of a TMarginSet by the corresponding axis of a TIntPoint. * - Left and Right are multiplied by Scale.X * - Top and Bottom are multiplied by Scale.Y * * @param Scale The TIntPoint scale factors (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is multiplied by the corresponding factor. */ template requires (std::is_integral_v&& std::is_convertible_v) [[nodiscard]] inline TMarginSet operator*(const TIntPoint& Scale) const { const T ScaleX = static_cast(Scale.X); const T ScaleY = static_cast(Scale.Y); return TMarginSet( Left * ScaleX, Right * ScaleX, Top * ScaleY, Bottom * ScaleY ); } /** * Multiplies each margin of a TMarginSet by the corresponding axis of a TIntVector2. * - Left and Right are multiplied by Scale.X * - Top and Bottom are multiplied by Scale.Y * * @param Scale The TIntVector2 scale factors (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is scaled by the corresponding factor. */ template requires (std::is_integral_v && std::is_convertible_v) [[nodiscard]] inline TMarginSet operator*(const TIntVector2& Scale) const { const T ScaleX = static_cast(Scale.X); const T ScaleY = static_cast(Scale.Y); return TMarginSet( Left * ScaleX, Right * ScaleX, Top * ScaleY, Bottom * ScaleY ); } /** * Multiplies each margin of a TMarginSet by the corresponding axis of a TVector2. * - Left and Right are multiplied by Scale.X * - Top and Bottom are multiplied by Scale.Y * * @param Scale The TVector2 scale factors (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is scaled by the corresponding factor. */ template requires (std::is_floating_point_v && std::is_floating_point_v && std::is_convertible_v) [[nodiscard]] inline TMarginSet operator*(const TVector2& Scale) const { const T ScaleX = static_cast(Scale.X); const T ScaleY = static_cast(Scale.Y); return TMarginSet( Left * ScaleX, Right * ScaleX, Top * ScaleY, Bottom * ScaleY ); } public: /** * Divides each margin of a floating-point TMarginSet by a scalar value. * * @param InDivisor Scalar divisor applied to all margins. * @return A TMarginSet where each margin is divided by InDivisor. */ template requires (std::is_floating_point_v && std::is_arithmetic_v>) [[nodiscard]] inline TMarginSet operator/(const U InDivisor) const { const T Divisor = static_cast(InDivisor); // Assumes caller ensures Divisor are nonzero check(Divisor); return UE::Math::TMarginSet( Left / Divisor, Right / Divisor, Top / Divisor, Bottom / Divisor ); } /** * Divides each margin of a TMarginSet by the corresponding axis of a TIntPoint. * - Left and Right are divided by Divisor.X * - Top and Bottom are divided by Divisor.Y * * @param Divisor The TIntPoint divisors (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is divided by the corresponding factor. */ template requires (std::is_floating_point_v&& std::is_integral_v&& std::is_convertible_v) [[nodiscard]] inline TMarginSet operator/(const TIntPoint& Divisor) const { const T DivisorX = static_cast(Divisor.X); const T DivisorY = static_cast(Divisor.Y); // Assumes caller ensures DivisorX and DivisorY are nonzero check(DivisorX && DivisorY); return TMarginSet( Left / DivisorX, Right / DivisorX, Top / DivisorY, Bottom / DivisorY ); } /** * Divides each margin of a TMarginSet by the corresponding axis of an TIntVector2. * - Left/Right are divided by Divisor.X * - Top/Bottom are divided by Divisor.Y * * @param Divisor The TIntVector2 divisor (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is divided by the corresponding divisor. */ template requires (std::is_floating_point_v && std::is_integral_v && std::is_convertible_v) [[nodiscard]] inline TMarginSet operator/(const TIntVector2& Divisor) const { const T DivisorX = static_cast(Divisor.X); const T DivisorY = static_cast(Divisor.Y); // Assumes caller ensures DivisorX and DivisorY are nonzero check(DivisorX && DivisorY); return TMarginSet( Left / DivisorX, Right / DivisorX, Top / DivisorY, Bottom / DivisorY ); } /** * Divides each margin of a TMarginSet by the corresponding axis of an TVector2. * - Left/Right are divided by Divisor.X * - Top/Bottom are divided by Divisor.Y * * @param Divisor The TVector2 divisor (X for horizontal, Y for vertical). * @return A TMarginSet where each margin is divided by the corresponding divisor. */ template requires (std::is_floating_point_v&& std::is_floating_point_v&& std::is_convertible_v) [[nodiscard]] inline TMarginSet operator/(const TVector2& Divisor) { const T DivisorX = static_cast(Divisor.X); const T DivisorY = static_cast(Divisor.Y); // Assumes caller ensures Divisor.X and Divisor.Y are nonzero check(DivisorX && DivisorY); return UE::Math::TMarginSet( Left / DivisorX, Right / DivisorX, Top / DivisorY, Bottom / DivisorY ); } public: /** * Expands the given rectangle in-place by the values in this margin set. * * Each margin value increases the corresponding edge outward: * - Left decreases Min.X * - Right increases Max.X * - Top decreases Min.Y * - Bottom increases Max.Y * * This operation can result in negative-sized rectangles if large margins are used. * * @param InOutRect [in,out] The rectangle to expand. Modified in place. */ template requires (std::is_integral_v && std::is_integral_v && std::is_convertible_v) void ApplyExpandToRect(TIntRect& InOutRect) const { InOutRect.Min.X -= Left; InOutRect.Max.X += Right; InOutRect.Min.Y -= Top; InOutRect.Max.Y += Bottom; // Clamp to prevent negative size (Min cannot exceed Max). InOutRect.Min.X = FMath::Min(InOutRect.Min.X, InOutRect.Max.X); InOutRect.Min.Y = FMath::Min(InOutRect.Min.Y, InOutRect.Max.Y); } /** * Shrinks the given rectangle in-place by the values in this margin set. * Each margin value moves the corresponding edge inward: * - Left increases Min.X * - Right decreases Max.X * - Top increases Min.Y * - Bottom decreases Max.Y * * If the total margin for any axis exceeds the rectangle's size, the rectangle will collapse to a line or point. * Margin values are clamped as needed to prevent the rectangle from inverting or collapsing to negative size. * * @param InOutRect [in,out] The rectangle to be contracted. Modified in place. */ template requires (std::is_integral_v&& std::is_integral_v&& std::is_convertible_v) void ApplyClampedInsetToRect(TIntRect& InOutRect) const { const FIntPoint RectSize = InOutRect.Size(); // Clamp margins to prevent invalid rectangle T ClampedLeft = FMath::Clamp(Left, 0, RectSize.X); T ClampedRight = FMath::Clamp(Right, 0, RectSize.X - ClampedLeft); T ClampedTop = FMath::Clamp(Top, 0, RectSize.Y); T ClampedBottom = FMath::Clamp(Bottom, 0, RectSize.Y - ClampedTop); // Shrink rect by clamped margins InOutRect.Min.X += ClampedLeft; InOutRect.Max.X -= ClampedRight; InOutRect.Min.Y += ClampedTop; InOutRect.Max.Y -= ClampedBottom; // Ensure Min does not surpass Max (should already be safe) InOutRect.Min.X = FMath::Min(InOutRect.Min.X, InOutRect.Max.X); InOutRect.Min.Y = FMath::Min(InOutRect.Min.Y, InOutRect.Max.Y); } public: /** * Provides indexed access to a margin side value. * * @param SideIndex Enum value specifying which margin side to access. * @return Reference to the corresponding side value. */ [[nodiscard]] inline T& operator[](EMarginSetSideIndex SideIndex) { switch (SideIndex) { case EMarginSetSideIndex::Left: return Left; case EMarginSetSideIndex::Right: return Right; case EMarginSetSideIndex::Top: return Top; case EMarginSetSideIndex::Bottom: return Bottom; default: break; } // Unreachable checkNoEntry(); return Left; } /** * Provides read-only indexed access to a margin side value. * * @param SideIndex Enum value specifying which margin side to access. * @return Const reference to the corresponding side value. */ [[nodiscard]] inline const T& operator[](EMarginSetSideIndex SideIndex) const { switch (SideIndex) { case EMarginSetSideIndex::Left: return Left; case EMarginSetSideIndex::Right: return Right; case EMarginSetSideIndex::Top: return Top; case EMarginSetSideIndex::Bottom: return Bottom; default: break; } // Unreachable checkNoEntry(); return Left; } /** * Provides indexed access to a margin side value by numeric index. * * @param Index Zero-based index of the side to access: * 0 = Left, 1 = Right, 2 = Top, 3 = Bottom. * @return Reference to the corresponding side value. */ [[nodiscard]] inline T& operator[](int32 Index) { check(Index >= 0 && Index < NumSides); const EMarginSetSideIndex SideIndex = static_cast(Index); return operator[](SideIndex); } /** * Provides read-only indexed access to a margin side value by numeric index. * * @param Index Zero-based index of the side to access: * 0 = Left, 1 = Right, 2 = Top, 3 = Bottom. * @return Const reference to the corresponding side value. */ [[nodiscard]] inline const T& operator[](int32 Index) const { check(Index >= 0 && Index < NumSides); const EMarginSetSideIndex SideIndex = static_cast(Index); return operator[](SideIndex); } /** Equality operator */ [[nodiscard]] inline bool operator==(const TMarginSet& Other) const { return Left == Other.Left && Right == Other.Right && Top == Other.Top && Bottom == Other.Bottom; } /** Inequality operator */ [[nodiscard]] inline bool operator!=(const TMarginSet& Other) const { return !(*this == Other); } public: /** * Returns a copy of this margin set where all margins floors to the nearest lower int32. * * @return A TMarginSet with each value floored. */ [[nodiscard]] inline TMarginSet GetFloorToInt() const { return TMarginSet( FMath::FloorToInt32(Left), FMath::FloorToInt32(Right), FMath::FloorToInt32(Top), FMath::FloorToInt32(Bottom) ); } /** * Returns a copy of this margin set where all margins rounds to the nearest int32. * * @return A TMarginSet with all values rounded. */ [[nodiscard]] inline TMarginSet GetRoundToInt() const { return TMarginSet( FMath::RoundToInt32(Left), FMath::RoundToInt32(Right), FMath::RoundToInt32(Top), FMath::RoundToInt32(Bottom) ); } /** * Returns a copy of this margin set where all margins ceils to the nearest greater or equal int32. * * @return A TMarginSet with each value ceiled. */ [[nodiscard]] inline TMarginSet GetCeilToInt() const { return TMarginSet( FMath::CeilToInt32(Left), FMath::CeilToInt32(Right), FMath::CeilToInt32(Top), FMath::CeilToInt32(Bottom) ); } public: /** * Returns a copy of this margin set where all margins clamped the specified [Min, Max] range. * * @param Min The minimum allowed value for each margin. * @param Max The maximum allowed value for each margin. * @return A clamped TMarginSet with all values between Min and Max. */ [[nodiscard]] inline TMarginSet GetClamped(const T Min, const T Max) const { return TMarginSet( FMath::Clamp(Left, Min, Max), FMath::Clamp(Right, Min, Max), FMath::Clamp(Top, Min, Max), FMath::Clamp(Bottom, Min, Max) ); } /** * Returns a copy of this margin set with each side individually clamped * between the corresponding sides of the provided minimum and maximum margins. * * For each side: * - Left = Clamp(Left, MinMargins.Left, MaxMargins.Left) * - Right = Clamp(Right, MinMargins.Right, MaxMargins.Right) * - Top = Clamp(Top, MinMargins.Top, MaxMargins.Top) * - Bottom = Clamp(Bottom, MinMargins.Bottom, MaxMargins.Bottom) * * @param MinMargins Margin set defining the minimum values per side. * @param MaxMargins Margin set defining the maximum values per side. * @return A new TMarginSet with each side clamped to its respective range. */ [[nodiscard]] inline TMarginSet GetClampedPerSide(const TMarginSet& MinMargins, const TMarginSet& MaxMargins) const { return TMarginSet( FMath::Clamp(Left, MinMargins.Left, MaxMargins.Left), FMath::Clamp(Right, MinMargins.Right, MaxMargins.Right), FMath::Clamp(Top, MinMargins.Top, MaxMargins.Top), FMath::Clamp(Bottom, MinMargins.Bottom, MaxMargins.Bottom) ); } /** * Returns true if all margins are zero. */ [[nodiscard]] inline bool IsZero() const { return Left == T(0) && Right == T(0) && Top == T(0) && Bottom == T(0); } /** * Returns the minimum value among all four margins (Left, Right, Top, Bottom). * * @return Smallest margin value. */ [[nodiscard]] inline T GetMin() const { const T Value1 = FMath::Min(Left, Right); const T Value2 = FMath::Min(Top, Bottom); return FMath::Min(Value1, Value2); } /** * Returns the maximum value among all four margins (Left, Right, Top, Bottom). * * @return Largest margin value. */ [[nodiscard]] inline T GetMax() const { const T Value1 = FMath::Max(Left, Right); const T Value2 = FMath::Max(Top, Bottom); return FMath::Max(Value1, Value2); } /** * Returns a copy of this margin set where all margins is the minimum of this margin and another. * * @param In The margin set to compare against * @return A new TMarginSet with the smallest values for each side */ [[nodiscard]] inline TMarginSet GetMinPerSide(const TMarginSet& In) const { return TMarginSet( FMath::Min(Left, In.Left), // Minimum of Left sides FMath::Min(Right, In.Right), // Minimum of Right sides FMath::Min(Top, In.Top), // Minimum of Top sides FMath::Min(Bottom, In.Bottom) // Minimum of Bottom sides ); } /** * Returns a copy of this margin set where all margins is the maximum of this margin and another. * * @param In The margin set to compare against * @return A new TMarginSet with the largest values for each side */ [[nodiscard]] inline TMarginSet GetMaxPerSide(const TMarginSet& In) const { return TMarginSet( FMath::Max(Left, In.Left), // Maximum of Left sides FMath::Max(Right, In.Right), // Maximum of Right sides FMath::Max(Top, In.Top), // Maximum of Top sides FMath::Max(Bottom, In.Bottom) // Maximum of Bottom sides ); } /** * Returns a new margin set where each side is the maximum of: * - the current value, or * - the input value, if it is non-zero (i.e., treated as defined). * * This allows selectively overriding margins while skipping undefined (zero) inputs. * * @param In The input margin set, where zero values are treated as "undefined". * @return A new margin set with per-side conditional maximums. */ [[nodiscard]] inline TMarginSet GetMaxPerSideIfDefined(const TMarginSet& In) const { return TMarginSet( In.Left != static_cast(0) ? FMath::Max(Left, In.Left) : Left, In.Right != static_cast(0) ? FMath::Max(Right, In.Right) : Right, In.Top != static_cast(0) ? FMath::Max(Top, In.Top) : Top, In.Bottom != static_cast(0) ? FMath::Max(Bottom, In.Bottom) : Bottom ); } /** * Returns a new margin set where each side is the minimum of: * - the current value, or * - the input value, if it is non-zero (i.e., treated as defined). * * This allows selectively overriding margins while skipping undefined (zero) inputs. * * @param In The input margin set, where zero values are treated as "undefined". * @return A new margin set with per-side conditional minimum. */ [[nodiscard]] inline TMarginSet GetMinPerSideIfDefined(const TMarginSet& In) const { return TMarginSet( In.Left != static_cast(0) ? FMath::Min(Left, In.Left) : Left, In.Right != static_cast(0) ? FMath::Min(Right, In.Right) : Right, In.Top != static_cast(0) ? FMath::Min(Top, In.Top) : Top, In.Bottom != static_cast(0) ? FMath::Min(Bottom, In.Bottom) : Bottom ); } }; } // namespace UE::Math UE_DECLARE_LWC_TYPE(MarginSet, 4); using FIntMarginSet = UE::Math::TMarginSet;