Files
2026-01-20 23:59:28 +07:00

345 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace BrewMonster.Scripts
{
/// <summary>
/// A rectangular area in 2D space.
/// </summary>
/// <remarks>
/// WARNING: This class contains many boxing and unboxing operations.
/// We have to go back and optimize this later.
/// </remarks>
public class ARect<T> where T : IComparable<T>
{
public T left, top, right, bottom;
public ARect()
{
left = default(T);
top = default(T);
right = default(T);
bottom = default(T);
}
public ARect(ARect<T> rc)
{
left = rc.left; top = rc.top; right = rc.right; bottom = rc.bottom;
}
public ARect(T left, T top, T right, T bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
// helpers for int/float only
static int Cmp(T a, T b) => Comparer<T>.Default.Compare(a, b);
static T Min(T a, T b) => Cmp(a, b) <= 0 ? a : b;
static T Max(T a, T b) => Cmp(a, b) >= 0 ? a : b;
static T Add(T a, T b)
{
if (typeof(T) == typeof(int)) return (T)(object)((int)(object)a + (int)(object)b);
if (typeof(T) == typeof(float)) return (T)(object)((float)(object)a + (float)(object)b);
throw new NotSupportedException("ARect<T> supports only int and float");
}
static T Sub(T a, T b)
{
if (typeof(T) == typeof(int)) return (T)(object)((int)(object)a - (int)(object)b);
if (typeof(T) == typeof(float)) return (T)(object)((float)(object)a - (float)(object)b);
throw new NotSupportedException("ARect<T> supports only int and float");
}
static T Divide(T a, T b)
{
if (typeof(T) == typeof(int)) return (T)(object)((int)(object)a / (int)(object)b);
if (typeof(T) == typeof(float)) return (T)(object)((float)(object)a / (float)(object)b);
throw new NotSupportedException("ARect<T> supports only int and float");
}
static T Neg(T a)
{
if (typeof(T) == typeof(int)) return (T)(object)(-(int)(object)a);
if (typeof(T) == typeof(float)) return (T)(object)(-(float)(object)a);
throw new NotSupportedException("ARect<T> supports only int and float");
}
static bool IsZero(T v)
{
if (typeof(T) == typeof(int)) return (int)(object)v == 0;
if (typeof(T) == typeof(float)) return (float)(object)v == 0f;
throw new NotSupportedException("ARect<T> supports only int and float");
}
static T FromInt(int v)
{
if (typeof(T) == typeof(int)) return (T)(object)v;
if (typeof(T) == typeof(float)) return (T)(object)(float)v;
throw new NotSupportedException("ARect<T> supports only int and float");
}
// == and != operator
public static bool operator !=(ARect<T> rc1, ARect<T> rc2)
{
return !(rc1 == rc2);
}
public static bool operator ==(ARect<T> rc1, ARect<T> rc2)
{
return EqualityComparer<T>.Default.Equals(rc1.left, rc2.left) &&
EqualityComparer<T>.Default.Equals(rc1.top, rc2.top) &&
EqualityComparer<T>.Default.Equals(rc1.right, rc2.right) &&
EqualityComparer<T>.Default.Equals(rc1.bottom, rc2.bottom);
}
// + and - operator
public static ARect<T> operator +(ARect<T> rc1, ARect<T> rc2)
{
return new ARect<T>(
Add(rc1.left, rc2.left),
Add(rc1.top, rc2.top),
Add(rc1.right, rc2.right),
Add(rc1.bottom, rc2.bottom)
);
}
public static ARect<T> operator -(ARect<T> rc1, ARect<T> rc2)
{
return new ARect<T>(
Sub(rc1.left, rc2.left),
Sub(rc1.top, rc2.top),
Sub(rc1.right, rc2.right),
Sub(rc1.bottom, rc2.bottom)
);
}
//public static ARect<T> operator +(ARect<T> rc1, APoint<T> pt)
//{
// return new ARect<T>((dynamic)rc1.left + (dynamic)pt.x,
// (dynamic)rc1.top + (dynamic)pt.y,
// (dynamic)rc1.right + (dynamic)pt.x,
// (dynamic)rc1.bottom + (dynamic)pt.y);
//}
//public static ARect<T> operator -(ARect<T> rc1, APoint<T> pt)
//{
// return new ARect<T>((dynamic)rc1.left - (dynamic)pt.x,
// (dynamic)rc1.top - (dynamic)pt.y,
// (dynamic)rc1.right - (dynamic)pt.x,
// (dynamic)rc1.bottom - (dynamic)pt.y);
//}
// &= and |= operator
public ARect<T> AndAssign(ARect<T> rc) => this & rc;
public ARect<T> OrAssign(ARect<T> rc) => this | rc;
public static ARect<T> operator &(ARect<T> rc1, ARect<T> rc2)
{
if (rc1.IsEmpty() || rc2.IsEmpty())
return new ARect<T>(default, default, default, default);
var l1 = rc1.left; var r1 = rc1.right; var t1 = rc1.top; var b1 = rc1.bottom;
var l2 = rc2.left; var r2 = rc2.right; var t2 = rc2.top; var b2 = rc2.bottom;
if (Cmp(l1, r2) >= 0 || Cmp(l2, r1) >= 0 ||
Cmp(t1, b2) >= 0 || Cmp(t2, b1) >= 0)
return new ARect<T>(default, default, default, default);
return new ARect<T>(
Max(l1, l2),
Max(t1, t2),
Min(r1, r2),
Min(b1, b2)
);
}
public static ARect<T> operator |(ARect<T> rc1, ARect<T> rc2)
{
if (rc1.IsEmpty())
return rc2;
if (rc2.IsEmpty())
return rc1;
var l1 = rc1.left; var r1 = rc1.right; var t1 = rc1.top; var b1 = rc1.bottom;
var l2 = rc2.left; var r2 = rc2.right; var t2 = rc2.top; var b2 = rc2.bottom;
return new ARect<T>(
Min(l1, l2),
Min(t1, t2),
Max(r1, r2),
Max(b1, b2)
);
}
public static ARect<T> operator +(ARect<T> rc) { return rc; }
public static ARect<T> operator -(ARect<T> rc)
{
return new ARect<T>(
Neg(rc.left),
Neg(rc.top),
Neg(rc.right),
Neg(rc.bottom)
);
}
// = operator
//public static ARect<T> operator = (ARect<T> rc) { left = rc.left; top = rc.top; right = rc.right; bottom = rc.bottom; return *this; }
// += and -= operator
public ARect<T> AdditionAssign(ARect<T> rc)
{
left = Add(left, rc.left);
top = Add(top, rc.top);
right = Add(right, rc.right);
bottom = Add(bottom, rc.bottom);
return this;
}
public ARect<T> SubtractionAssign(ARect<T> rc)
{
left = Sub(left, rc.left);
top = Sub(top, rc.top);
right = Sub(right, rc.right);
bottom = Sub(bottom, rc.bottom);
return this;
}
//public ARect<T> AdditionAssign(APoint<T> pt) { left += pt.x; top += pt.y; right += pt.x; bottom += pt.y; return this; }
//public ARect<T> SubtractionAssign(APoint<T> pt) { left -= pt.x; top -= pt.y; right -= pt.x; bottom -= pt.y; return this; }
// Get width of rectangle
public T Width()
{
return Sub(right, left);
}
// Get height of rectangle
public T Height()
{
return Sub(bottom, top);
}
// Get center point of rectangle
public APoint<T> CenterPoint() { return new APoint<T>(Divide(Add(left, right), FromInt(2)), Divide(Add(top, bottom), FromInt(2))); }
// Set rectangle value
public void SetRect(T _left, T _top, T _right, T _bottom)
{
left = _left;
top = _top;
right = _right;
bottom = _bottom;
}
// Point in rectangle
public bool PtInRect(T x, T y)
{
return Cmp(x, left) >= 0 && Cmp(x, right) < 0 && Cmp(y, top) >= 0 && Cmp(y, bottom) < 0;
}
//public bool PtInRect(APoint<T> pt) { return PtInRect(pt.x, pt.y); }
// Normalize rectangle. Note: The following CRect member functions require
// normalized rectangles in order to work properly: Height, Width, Size,
// IsEmpty, PtInRect, SetUnion, SetIntersect, operator ==, operator !=,
// operator |, operator |=, operator &, and operator &=
void Normalize()
{
if (Cmp(left, right) > 0)
a_Swap(ref left, ref right);
if (Cmp(top, bottom) > 0)
a_Swap(ref top, ref bottom);
}
private void a_Swap(ref T lhs, ref T rhs)
{
T tmp;
tmp = lhs;
lhs = rhs;
rhs = tmp;
}
// All members are 0 ?
public bool IsRectNull()
{
return IsZero(left) && IsZero(top) && IsZero(right) && IsZero(bottom);
}
// Rectangle is empty ?
public bool IsEmpty()
{
return IsZero(Width()) || IsZero(Height());
}
// Set all members to 0
public void Clear()
{
left = top = right = bottom = default(T);
}
// Deflate rectangle
public void Deflate(T x, T y)
{
left = Add(left, x);
top = Add(top, y);
right = Sub(right, x);
bottom = Sub(bottom, y);
}
public void Deflate(ARect<T> rc)
{
left = Add(left, rc.left);
top = Add(top, rc.top);
right = Sub(right, rc.right);
bottom = Sub(bottom, rc.bottom);
}
public void Deflate(T l, T t, T r, T b)
{
left = Add(left, l);
top = Add(top, t);
right = Sub(right, r);
bottom = Sub(bottom, b);
}
// Inflate rectangle
public void Inflate(T x, T y)
{
left = Sub(left, x);
top = Sub(top, y);
right = Add(right, x);
bottom = Add(bottom, y);
}
public void Inflate(ARect<T> rc)
{
left = Sub(left, rc.left);
top = Sub(top, rc.top);
right = Add(right, rc.right);
bottom = Add(bottom, rc.bottom);
}
public void Inflate(T l, T t, T r, T b)
{
left = Sub(left, l);
top = Sub(top, t);
right = Add(right, r);
bottom = Add(bottom, b);
}
// Offset rectangle
public void Offset(int x, int y)
{
var dx = FromInt(x);
var dy = FromInt(y);
left = Add(left, dx);
top = Add(top, dy);
right = Add(right, dx);
bottom = Add(bottom, dy);
}
//public void Offset(APoint<T> pt) { this += pt; }
// Set rectangle as union result
public void SetUnion(ARect<T> rc1, ARect<T> rc2)
{
var result = rc1 | rc2;
left = result.left;
right = result.right;
bottom = result.bottom;
top = result.top;
}
// Set rectangle as intersect result
public void SetIntersect(ARect<T> rc1, ARect<T> rc2)
{
var result = rc1 & rc2;
left = result.left;
right = result.right;
bottom = result.bottom;
top = result.top;
}
}
}