Code/General/BBoxInt.cs
using Sandbox;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace ExtendedBox.General;
public struct BBoxInt : IEquatable<BBoxInt>
{
[JsonInclude]
public Vector3Int Mins;
[JsonInclude]
public Vector3Int Maxs;
[JsonIgnore]
public IEnumerable<Vector3Int> Corners
{
get
{
yield return new(Mins.x, Mins.y, Mins.z);
yield return new(Maxs.x, Mins.y, Mins.z);
yield return new(Maxs.x, Maxs.y, Mins.z);
yield return new(Mins.x, Maxs.y, Mins.z);
yield return new(Mins.x, Mins.y, Maxs.z);
yield return new(Maxs.x, Mins.y, Maxs.z);
yield return new(Maxs.x, Maxs.y, Maxs.z);
yield return new(Mins.x, Maxs.y, Maxs.z);
}
}
[JsonIgnore]
public readonly Vector3 Center => Mins + Size * 0.5f;
[JsonIgnore]
public readonly Vector3Int Size => Maxs - Mins;
[JsonIgnore]
public readonly Vector3 Extents => Size * 0.5f;
[JsonIgnore]
public readonly Vector3 RandomPointInside => Random.Shared.VectorInCube(in this);
[JsonIgnore]
public readonly Vector3 RandomPointOnEdge
{
get
{
Vector3 size = Size;
Vector3 vector = size;
vector.x *= Game.Random.Float(0f, 1f);
vector.y *= Game.Random.Float(0f, 1f);
vector.z *= Game.Random.Float(0f, 1f);
switch(Random.Shared.Int(0, 5))
{
case 0:
vector.x = 0f;
break;
case 1:
vector.y = 0f;
break;
case 2:
vector.z = 0f;
break;
case 3:
vector.x = size.x;
break;
case 4:
vector.y = size.y;
break;
case 5:
vector.z = size.z;
break;
}
return Mins + vector;
}
}
[JsonIgnore]
public readonly float Volume
{
get
{
Vector3 vector = Size.Abs();
return vector.x * vector.y * vector.z;
}
}
#region constructors
public BBoxInt(Vector3Int mins, Vector3Int maxs)
{
Mins = Vector3Int.Min(mins, maxs);
Maxs = Vector3Int.Max(mins, maxs);
}
public static BBoxInt FromHeightAndRadius(int height, Vector2Int radius)
{
var mins = new Vector3Int(-radius.x, -radius.y, 0);
var maxs = new Vector3Int(radius.x, radius.y, height);
return new(mins, maxs);
}
public static BBoxInt FromMinsAndSize(in Vector3Int mins, Vector3Int size = default)
{
BBoxInt result = default;
result.Mins = mins;
result.Maxs = mins + size;
return result;
}
public static BBoxInt FromPositionAndRadius(in Vector3Int center, Vector3Int radius = default)
{
BBoxInt result = default;
result.Mins = center - radius;
result.Maxs = center + radius;
return result;
}
public static BBoxInt FromBoxes(IEnumerable<BBoxInt> boxes)
{
using IEnumerator<BBoxInt> enumerator = boxes.GetEnumerator();
if(!enumerator.MoveNext())
{
return default;
}
BBoxInt result = enumerator.Current;
while(enumerator.MoveNext())
{
BBoxInt bbox = enumerator.Current;
result = result.AddBBox(in bbox);
}
return result;
}
public static BBoxInt FromPoints(IEnumerable<Vector3Int> points)
{
using IEnumerator<Vector3Int> enumerator = points.GetEnumerator();
if(!enumerator.MoveNext())
{
return default;
}
Vector3Int center = enumerator.Current;
BBoxInt result = new(center, center);
while(enumerator.MoveNext())
{
center = enumerator.Current;
result = result.AddPoint(center);
}
return result;
}
public static BBoxInt FromPointsAndRadius(IEnumerable<Vector3Int> points, Vector3Int radius = default)
{
using IEnumerator<Vector3Int> enumerator = points.GetEnumerator();
if(!enumerator.MoveNext())
{
return default;
}
Vector3Int center = enumerator.Current;
BBoxInt result = FromPositionAndRadius(in center, radius);
while(enumerator.MoveNext())
{
center = enumerator.Current;
BBoxInt bbox = FromPositionAndRadius(in center, radius);
result = result.AddBBox(in bbox);
}
return result;
}
#endregion
#region convertation methods
public readonly BBox Translate(in Vector3 point)
{
BBox result = this;
result.Mins += point;
result.Maxs += point;
return result;
}
public readonly BBoxInt Translate(in Vector3Int point)
{
BBoxInt result = this;
result.Mins += point;
result.Maxs += point;
return result;
}
public readonly BBox Rotate(in Rotation rotation)
{
BBox result = this;
Rotation normal = rotation.Conjugate.Normal;
Vector3 vector = Vector3.Forward * normal;
Vector3 vector2 = Vector3.Right * normal;
Vector3 vector3 = Vector3.Up * normal;
Vector3 c = 0.5f * (result.Mins + result.Maxs);
Vector3 vector4 = result.Maxs - c;
Vector3 vector5 = rotation * c;
Vector3 vector6 = new Vector3(MathF.Abs(vector4.x * vector.x) + MathF.Abs(vector4.y * vector.y) + MathF.Abs(vector4.z * vector.z), MathF.Abs(vector4.x * vector2.x) + MathF.Abs(vector4.y * vector2.y) + MathF.Abs(vector4.z * vector2.z), MathF.Abs(vector4.x * vector3.x) + MathF.Abs(vector4.y * vector3.y) + MathF.Abs(vector4.z * vector3.z));
result.Mins = vector5 - vector6;
result.Maxs = vector5 + vector6;
return result;
}
public readonly BBox Transform(in Transform transform)
{
return Scale(in transform.Scale).Rotate(in transform.Rotation).Translate(in transform.Position);
}
public readonly BBox Scale(in Vector3 scale)
{
var mins = -scale * Extents + Center;
var maxs = scale * Extents + Center;
return new BBox(mins, maxs);
}
public readonly bool Contains(in BBoxInt b, bool includeMaxs = false)
{
if(includeMaxs)
{
return b.Mins.x >= Mins.x && b.Maxs.x <= Maxs.x &&
b.Mins.y >= Mins.y && b.Maxs.y <= Maxs.y &&
b.Mins.z >= Mins.z && b.Maxs.z <= Maxs.z;
}
return b.Mins.x >= Mins.x && b.Maxs.x < Maxs.x &&
b.Mins.y >= Mins.y && b.Maxs.y < Maxs.y &&
b.Mins.z >= Mins.z && b.Maxs.z < Maxs.z;
}
public readonly bool Contains(in BBox b, bool includeMaxs = false)
{
if(includeMaxs)
{
return b.Mins.x >= Mins.x && b.Maxs.x <= Maxs.x &&
b.Mins.y >= Mins.y && b.Maxs.y <= Maxs.y &&
b.Mins.z >= Mins.z && b.Maxs.z <= Maxs.z;
}
return b.Mins.x >= Mins.x && b.Maxs.x < Maxs.x &&
b.Mins.y >= Mins.y && b.Maxs.y < Maxs.y &&
b.Mins.z >= Mins.z && b.Maxs.z < Maxs.z;
}
public readonly bool Contains(in Vector3 b, bool includeMaxs = false)
{
if(includeMaxs)
{
return b.x >= Mins.x && b.x <= Maxs.x &&
b.y >= Mins.y && b.y <= Maxs.y &&
b.z >= Mins.z && b.z <= Maxs.z;
}
return b.x >= Mins.x && b.x < Maxs.x &&
b.y >= Mins.y && b.y < Maxs.y &&
b.z >= Mins.z && b.z < Maxs.z;
}
public readonly bool Overlaps(in BBox b)
{
return Mins.x < b.Maxs.x && b.Mins.x < Maxs.x &&
Mins.y < b.Maxs.y && b.Mins.y < Maxs.y &&
Mins.z < b.Maxs.z && b.Mins.z < Maxs.z;
}
public readonly BBox GetIntersection(BBox other) => other.GetIntersection(this);
public readonly BBoxInt GetIntersection(BBoxInt other)
{
if(!Overlaps(other))
return new(Vector3Int.Zero, Vector3Int.Zero);
return new(Mins.ComponentMax(other.Mins), Maxs.ComponentMin(other.Maxs));
}
public readonly BBox AddPoint(in Vector3 point)
{
BBox result = this;
result.Mins = Vector3.Min(Mins, in point);
result.Maxs = Vector3.Max(Maxs, in point);
return result;
}
public readonly BBoxInt AddPoint(in Vector3Int point)
{
BBoxInt result = this;
result.Mins = Vector3Int.Min(Mins, point);
result.Maxs = Vector3Int.Max(Maxs, point);
return result;
}
public readonly BBox AddBBox(in BBox bbox)
{
BBox result = this;
result.Mins = Vector3.Min(Mins, in bbox.Mins);
result.Maxs = Vector3.Max(Maxs, in bbox.Maxs);
return result;
}
public readonly BBoxInt AddBBox(in BBoxInt bbox)
{
BBoxInt result = this;
result.Mins = Vector3Int.Min(Mins, bbox.Mins);
result.Maxs = Vector3Int.Max(Maxs, bbox.Maxs);
return result;
}
public readonly BBox Grow(in Vector3 skin)
{
BBox result = this;
result.Mins -= skin;
result.Maxs += skin;
return result;
}
public readonly BBoxInt Grow(in Vector3Int skin)
{
BBoxInt result = this;
result.Mins -= skin;
result.Maxs += skin;
return result;
}
public readonly Vector3 ClosestPoint(in Vector3 point)
{
return Vector3.Clamp(in point, Mins, Maxs);
}
#endregion
#region operators
public static BBox operator *(BBoxInt c1, Vector3 c2)
{
return new(c1.Mins * c2, c1.Maxs * c2);
}
public static BBoxInt operator *(BBoxInt c1, Vector3Int c2)
{
c1.Mins *= c2;
c1.Maxs *= c2;
return c1;
}
public static BBox operator +(BBoxInt c1, Vector3 c2)
{
return new(c1.Mins + c2, c1.Maxs + c2);
}
public static BBoxInt operator +(BBoxInt c1, Vector3Int c2)
{
c1.Mins += c2;
c1.Maxs += c2;
return c1;
}
public static bool operator ==(BBoxInt left, BBoxInt right)
{
return left.Equals(right);
}
public static bool operator !=(BBoxInt left, BBoxInt right)
{
return !(left == right);
}
public static implicit operator BBox(BBoxInt bBoxInt) => new(bBoxInt.Mins, bBoxInt.Maxs);
public static explicit operator BBoxInt(BBox bBoxInt) => new((Vector3Int)bBoxInt.Mins, (Vector3Int)bBoxInt.Maxs);
#endregion
public readonly bool Trace(in Ray ray, float distance, out float hitDistance)
{
return ((BBox)this).Trace(ray, distance, out hitDistance);
}
public override readonly string ToString()
{
return $"mins {Mins}, maxs {Maxs}";
}
public readonly BBox Snap(float distance)
{
return new BBox(Mins.SnapToGrid(distance), Maxs.SnapToGrid(distance));
}
public readonly BBoxInt Snap(int distance)
{
return new BBoxInt(Mins.SnapToGrid(distance), Maxs.SnapToGrid(distance));
}
public override readonly bool Equals(object? obj)
{
if(obj is BBoxInt o)
return Equals(o);
return false;
}
public readonly bool Equals(BBoxInt o)
{
return Mins == o.Mins && Maxs == o.Maxs;
}
public override readonly int GetHashCode()
{
return HashCode.Combine(Mins, Maxs);
}
}