Code/SboxSuperuser/Engine/Vendor/ImGui/ImRect.cs

A vendored ImGui ImRect struct representing an axis-aligned rectangle with Min and Max corners. It provides constructors, computed properties (center, size, corners, area), containment/overlap checks, modification methods (Add, Expand, Translate, Clip, Floor) and conversion to engine Rect.

Native Interop
// Vendored from zeljkovranjes/sbox-imgui; see LICENSE and PROVENANCE.md in this folder.
using Sandbox;
#pragma warning disable CS0649 // Upstream reserves fields for optional widgets and features.
namespace SboxSuperuser.Engine.Vendor.ImGui;

/// <summary>
/// An axis-aligned rectangle stored as a min and max corner, in screen pixels.
/// </summary>
internal struct ImRect
{
	public Vector2 Min;
	public Vector2 Max;

	public ImRect( Vector2 min, Vector2 max )
	{
		Min = min;
		Max = max;
	}

	public ImRect( float x1, float y1, float x2, float y2 )
	{
		Min = new Vector2( x1, y1 );
		Max = new Vector2( x2, y2 );
	}

	public ImRect( Rect rect ) : this( rect.TopLeft, rect.BottomRight ) { }

	public readonly Vector2 Center => (Min + Max) * 0.5f;
	public readonly Vector2 Size => Max - Min;
	public readonly float Width => Max.x - Min.x;
	public readonly float Height => Max.y - Min.y;
	public readonly float Area => Width * Height;
	public readonly Vector2 TL => Min;
	public readonly Vector2 TR => new( Max.x, Min.y );
	public readonly Vector2 BL => new( Min.x, Max.y );
	public readonly Vector2 BR => Max;
	public readonly bool IsInverted => Min.x > Max.x || Min.y > Max.y;

	public readonly bool Contains( Vector2 p ) => p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y;
	public readonly bool Contains( ImRect r ) => r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y;
	public readonly bool ContainsWithPad( Vector2 p, Vector2 pad ) => p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y;
	public readonly bool Overlaps( ImRect r ) => r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x;

	public void Add( Vector2 p )
	{
		if ( Min.x > p.x ) Min.x = p.x;
		if ( Min.y > p.y ) Min.y = p.y;
		if ( Max.x < p.x ) Max.x = p.x;
		if ( Max.y < p.y ) Max.y = p.y;
	}

	public void Add( ImRect r )
	{
		if ( Min.x > r.Min.x ) Min.x = r.Min.x;
		if ( Min.y > r.Min.y ) Min.y = r.Min.y;
		if ( Max.x < r.Max.x ) Max.x = r.Max.x;
		if ( Max.y < r.Max.y ) Max.y = r.Max.y;
	}

	public void Expand( float amount )
	{
		Min.x -= amount; Min.y -= amount;
		Max.x += amount; Max.y += amount;
	}

	public void Expand( Vector2 amount )
	{
		Min.x -= amount.x; Min.y -= amount.y;
		Max.x += amount.x; Max.y += amount.y;
	}

	public void Translate( Vector2 d )
	{
		Min += d;
		Max += d;
	}

	public void TranslateX( float dx ) { Min.x += dx; Max.x += dx; }
	public void TranslateY( float dy ) { Min.y += dy; Max.y += dy; }

	/// <summary>Simple version, may lead to an inverted rectangle.</summary>
	public void ClipWith( ImRect r )
	{
		Min = ImGui.ImMax( Min, r.Min );
		Max = ImGui.ImMin( Max, r.Max );
	}

	/// <summary>Full version, ensure both points are fully clipped.</summary>
	public void ClipWithFull( ImRect r )
	{
		Min = ImGui.ImClamp( Min, r.Min, r.Max );
		Max = ImGui.ImClamp( Max, r.Min, r.Max );
	}

	public void Floor()
	{
		Min = new Vector2( MathF.Floor( Min.x ), MathF.Floor( Min.y ) );
		Max = new Vector2( MathF.Floor( Max.x ), MathF.Floor( Max.y ) );
	}

	public readonly Rect ToRect() => new( Min, Max - Min );

	public override readonly string ToString() => $"({Min.x:F1},{Min.y:F1})-({Max.x:F1},{Max.y:F1})";
}