PixelRect.cs
namespace Sandbox;

/// <summary>
/// Integer rectangle for pixel-space hitboxes and regions.
/// Origin is bottom-left to match the pixel coordinate system.
/// </summary>
public struct PixelRect : IEquatable<PixelRect>
{
	public int Left;
	public int Bottom;
	public int Width;
	public int Height;

	public PixelRect( int left, int bottom, int width, int height )
	{
		Left = left;
		Bottom = bottom;
		Width = width;
		Height = height;
	}

	public int XMin => Left;
	public int XMax => Left + Width;
	public int YMin => Bottom;
	public int YMax => Bottom + Height;

	/// <summary>
	/// Returns true if the point is inside the rectangle (inclusive min, exclusive max).
	/// </summary>
	public bool Contains( PixelPoint point )
	{
		return point.X >= XMin && point.X < XMax
			&& point.Y >= YMin && point.Y < YMax;
	}

	/// <summary>
	/// AABB overlap test between two rectangles.
	/// </summary>
	public bool Overlaps( PixelRect other )
	{
		return XMin < other.XMax && XMax > other.XMin
			&& YMin < other.YMax && YMax > other.YMin;
	}

	public static PixelRect operator +( PixelRect rect, PixelPoint offset )
	{
		return new PixelRect( rect.Left + offset.X, rect.Bottom + offset.Y, rect.Width, rect.Height );
	}

	public bool Equals( PixelRect other ) =>
		Left == other.Left && Bottom == other.Bottom && Width == other.Width && Height == other.Height;
	public override bool Equals( object obj ) => obj is PixelRect other && Equals( other );
	public override int GetHashCode() => HashCode.Combine( Left, Bottom, Width, Height );
	public override string ToString() => $"({Left}, {Bottom}, {Width}x{Height})";
}