using System;
using System.Text.Json.Serialization;
using MessagePack;
namespace GameCult.Geometry
{
///
/// Deterministic two-dimensional float vector for cross-runtime geometry and query contracts.
///
[MessagePackObject]
public readonly struct CultVec2 : IEquatable
{
/// Creates a two-dimensional vector.
public CultVec2(float x, float y)
{
X = x;
Y = y;
}
/// Gets the x component.
[Key(0)]
public float X { get; }
/// Gets the y component.
[Key(1)]
public float Y { get; }
/// Gets the zero vector.
[IgnoreMember]
[JsonIgnore]
public static CultVec2 Zero => new(0f, 0f);
/// Gets the squared vector length.
[IgnoreMember]
[JsonIgnore]
public float LengthSquared => (X * X) + (Y * Y);
/// Returns whether this vector is equal to another vector.
public bool Equals(CultVec2 other) => X.Equals(other.X) && Y.Equals(other.Y);
///
public override bool Equals(object? obj) => obj is CultVec2 other && Equals(other);
///
public override int GetHashCode() => HashCode.Combine(X, Y);
///
public override string ToString() => $"({X}, {Y})";
public static CultVec2 operator +(CultVec2 left, CultVec2 right) => new(left.X + right.X, left.Y + right.Y);
public static CultVec2 operator -(CultVec2 left, CultVec2 right) => new(left.X - right.X, left.Y - right.Y);
public static CultVec2 operator *(CultVec2 value, float scalar) => new(value.X * scalar, value.Y * scalar);
public static CultVec2 operator /(CultVec2 value, float scalar) => new(value.X / scalar, value.Y / scalar);
public static bool operator ==(CultVec2 left, CultVec2 right) => left.Equals(right);
public static bool operator !=(CultVec2 left, CultVec2 right) => !left.Equals(right);
}
///
/// Deterministic three-dimensional float vector for cross-runtime geometry and query contracts.
///
[MessagePackObject]
public readonly struct CultVec3 : IEquatable
{
/// Creates a three-dimensional vector.
public CultVec3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
/// Gets the x component.
[Key(0)]
public float X { get; }
/// Gets the y component.
[Key(1)]
public float Y { get; }
/// Gets the z component.
[Key(2)]
public float Z { get; }
/// Gets the zero vector.
[IgnoreMember]
[JsonIgnore]
public static CultVec3 Zero => new(0f, 0f, 0f);
/// Gets the XY plane projection.
[IgnoreMember]
[JsonIgnore]
public CultVec2 Xy => new(X, Y);
/// Gets the XZ plane projection for Unity compatibility adapters.
[IgnoreMember]
[JsonIgnore]
public CultVec2 Xz => new(X, Z);
/// Gets the squared vector length.
[IgnoreMember]
[JsonIgnore]
public float LengthSquared => (X * X) + (Y * Y) + (Z * Z);
/// Returns whether this vector is equal to another vector.
public bool Equals(CultVec3 other) => X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
///
public override bool Equals(object? obj) => obj is CultVec3 other && Equals(other);
///
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
///
public override string ToString() => $"({X}, {Y}, {Z})";
public static CultVec3 operator +(CultVec3 left, CultVec3 right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static CultVec3 operator -(CultVec3 left, CultVec3 right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static CultVec3 operator *(CultVec3 value, float scalar) => new(value.X * scalar, value.Y * scalar, value.Z * scalar);
public static CultVec3 operator /(CultVec3 value, float scalar) => new(value.X / scalar, value.Y / scalar, value.Z / scalar);
public static bool operator ==(CultVec3 left, CultVec3 right) => left.Equals(right);
public static bool operator !=(CultVec3 left, CultVec3 right) => !left.Equals(right);
}
///
/// Axis-aligned two-dimensional rectangle represented by canonical min and max corners.
///
[MessagePackObject]
public readonly struct CultRect : IEquatable
{
/// Creates a rectangle and canonicalizes min/max corners.
public CultRect(CultVec2 min, CultVec2 max)
{
Min = new CultVec2(Math.Min(min.X, max.X), Math.Min(min.Y, max.Y));
Max = new CultVec2(Math.Max(min.X, max.X), Math.Max(min.Y, max.Y));
}
/// Creates a rectangle and canonicalizes min/max corners.
public CultRect(float minX, float minY, float maxX, float maxY)
: this(new CultVec2(minX, minY), new CultVec2(maxX, maxY))
{
}
/// Gets the canonical minimum corner.
[Key(0)]
public CultVec2 Min { get; }
/// Gets the canonical maximum corner.
[Key(1)]
public CultVec2 Max { get; }
/// Gets the rectangle size.
[IgnoreMember]
[JsonIgnore]
public CultVec2 Size => Max - Min;
/// Gets the rectangle center.
[IgnoreMember]
[JsonIgnore]
public CultVec2 Center => (Min + Max) * 0.5f;
/// Returns whether the point lies inside or on the rectangle edge.
public bool Contains(CultVec2 point)
{
return point.X >= Min.X &&
point.X <= Max.X &&
point.Y >= Min.Y &&
point.Y <= Max.Y;
}
/// Returns whether this rectangle intersects another rectangle.
public bool Intersects(CultRect other)
{
return Min.X <= other.Max.X &&
Max.X >= other.Min.X &&
Min.Y <= other.Max.Y &&
Max.Y >= other.Min.Y;
}
/// Returns whether this rectangle is equal to another rectangle.
public bool Equals(CultRect other) => Min == other.Min && Max == other.Max;
///
public override bool Equals(object? obj) => obj is CultRect other && Equals(other);
///
public override int GetHashCode() => HashCode.Combine(Min, Max);
///
public override string ToString() => $"[{Min}..{Max}]";
public static bool operator ==(CultRect left, CultRect right) => left.Equals(right);
public static bool operator !=(CultRect left, CultRect right) => !left.Equals(right);
}
///
/// Two-dimensional circle for spatial queries.
///
[MessagePackObject]
public readonly struct CultCircle : IEquatable
{
/// Creates a circle.
public CultCircle(CultVec2 center, float radius)
{
if (radius < 0f)
{
throw new ArgumentOutOfRangeException(nameof(radius), "Radius cannot be negative.");
}
Center = center;
Radius = radius;
}
/// Gets the circle center.
[Key(0)]
public CultVec2 Center { get; }
/// Gets the circle radius.
[Key(1)]
public float Radius { get; }
/// Gets the circle bounds.
[IgnoreMember]
[JsonIgnore]
public CultRect Bounds => new(
Center.X - Radius,
Center.Y - Radius,
Center.X + Radius,
Center.Y + Radius);
/// Returns whether the point lies inside or on the circle edge.
public bool Contains(CultVec2 point)
{
var delta = point - Center;
return delta.LengthSquared <= Radius * Radius;
}
/// Returns whether this circle intersects a rectangle.
public bool Intersects(CultRect rect)
{
var clampedX = Math.Max(rect.Min.X, Math.Min(Center.X, rect.Max.X));
var clampedY = Math.Max(rect.Min.Y, Math.Min(Center.Y, rect.Max.Y));
return Contains(new CultVec2(clampedX, clampedY));
}
/// Returns whether this circle is equal to another circle.
public bool Equals(CultCircle other) => Center == other.Center && Radius.Equals(other.Radius);
///
public override bool Equals(object? obj) => obj is CultCircle other && Equals(other);
///
public override int GetHashCode() => HashCode.Combine(Center, Radius);
public static bool operator ==(CultCircle left, CultCircle right) => left.Equals(right);
public static bool operator !=(CultCircle left, CultCircle right) => !left.Equals(right);
}
///
/// Three-dimensional sphere for physics and spatial queries.
///
[MessagePackObject]
public readonly struct CultSphere : IEquatable
{
/// Creates a sphere.
public CultSphere(CultVec3 center, float radius)
{
if (radius < 0f)
{
throw new ArgumentOutOfRangeException(nameof(radius), "Radius cannot be negative.");
}
Center = center;
Radius = radius;
}
/// Gets the sphere center.
[Key(0)]
public CultVec3 Center { get; }
/// Gets the sphere radius.
[Key(1)]
public float Radius { get; }
/// Gets the XY circle projection.
[IgnoreMember]
[JsonIgnore]
public CultCircle XyCircle => new(Center.Xy, Radius);
/// Returns whether the point lies inside or on the sphere surface.
public bool Contains(CultVec3 point)
{
var delta = point - Center;
return delta.LengthSquared <= Radius * Radius;
}
/// Returns whether this sphere is equal to another sphere.
public bool Equals(CultSphere other) => Center == other.Center && Radius.Equals(other.Radius);
///
public override bool Equals(object? obj) => obj is CultSphere other && Equals(other);
///
public override int GetHashCode() => HashCode.Combine(Center, Radius);
public static bool operator ==(CultSphere left, CultSphere right) => left.Equals(right);
public static bool operator !=(CultSphere left, CultSphere right) => !left.Equals(right);
}
}