Code/Core/PanelShape.cs
using System;
using Sandbox;
using Sandbox.UI;
namespace Goo;
public enum PanelShapeKind
{
None,
Circle,
Polygon,
}
public readonly struct PanelShapePoint : IEquatable<PanelShapePoint>
{
public Length X { get; }
public Length Y { get; }
public PanelShapePoint(Length x, Length y)
{
UiCssSerializer.ValidateLength(x, nameof(x));
UiCssSerializer.ValidateLength(y, nameof(y));
X = x;
Y = y;
}
public static PanelShapePoint Percent(float x, float y)
=> new(
new Length { Value = x, Unit = LengthUnit.Percentage },
new Length { Value = y, Unit = LengthUnit.Percentage });
public static PanelShapePoint Pixels(float x, float y)
=> new(
new Length { Value = x, Unit = LengthUnit.Pixels },
new Length { Value = y, Unit = LengthUnit.Pixels });
public static implicit operator PanelShapePoint(Vector2 unitPoint)
=> Percent(unitPoint.x * 100f, unitPoint.y * 100f);
public bool Equals(PanelShapePoint other) => X == other.X && Y == other.Y;
public override bool Equals(object? obj) => obj is PanelShapePoint other && Equals(other);
public override int GetHashCode() => HashCode.Combine(X, Y);
public static bool operator ==(PanelShapePoint left, PanelShapePoint right) => left.Equals(right);
public static bool operator !=(PanelShapePoint left, PanelShapePoint right) => !left.Equals(right);
}
public readonly struct PanelShape : IEquatable<PanelShape>
{
const int MaxPoints = 8;
readonly PanelShapePoint[]? _points;
public static readonly PanelShape None = default;
public PanelShapeKind Kind { get; }
public Length? CircleRadius { get; }
public Length CircleCenterX { get; }
public Length CircleCenterY { get; }
public int Count => _points?.Length ?? 0;
public PanelShapePoint this[int index] => _points![index];
PanelShape(
PanelShapeKind kind,
Length? circleRadius,
Length circleCenterX,
Length circleCenterY,
PanelShapePoint[]? points)
{
Kind = kind;
CircleRadius = circleRadius;
CircleCenterX = circleCenterX;
CircleCenterY = circleCenterY;
_points = points;
}
public static PanelShape Circle(Length? radius = null, Length? centerX = null, Length? centerY = null)
{
if (radius.HasValue)
{
UiCssSerializer.ValidateLength(radius.Value, nameof(radius));
if (radius.Value.Value < 0f)
throw new ArgumentOutOfRangeException(nameof(radius));
}
var center = new Length { Value = 50f, Unit = LengthUnit.Percentage };
var x = centerX ?? center;
var y = centerY ?? center;
UiCssSerializer.ValidateLength(x, nameof(centerX));
UiCssSerializer.ValidateLength(y, nameof(centerY));
return new PanelShape(PanelShapeKind.Circle, radius, x, y, null);
}
public static PanelShape Polygon(params Vector2[] points)
{
if (points is null)
throw new ArgumentNullException(nameof(points));
var typed = new PanelShapePoint[points.Length];
for (int i = 0; i < points.Length; i++) typed[i] = points[i];
return Polygon(typed);
}
public static PanelShape Polygon(params PanelShapePoint[] points)
{
if (points is null || points.Length < 3 || points.Length > MaxPoints)
throw new ArgumentException($"Panel polygons require 3 to {MaxPoints} points.", nameof(points));
var copy = new PanelShapePoint[points.Length];
for (int i = 0; i < points.Length; i++)
{
UiCssSerializer.ValidateLength(points[i].X, nameof(points));
UiCssSerializer.ValidateLength(points[i].Y, nameof(points));
copy[i] = points[i];
}
return new PanelShape(PanelShapeKind.Polygon, null, default, default, copy);
}
internal string ToCss()
{
if (Kind == PanelShapeKind.None) return "none";
if (Kind == PanelShapeKind.Circle)
{
var radius = CircleRadius.HasValue ? UiCssSerializer.Length(CircleRadius.Value) + " " : "";
return $"circle({radius}at {UiCssSerializer.Length(CircleCenterX)} {UiCssSerializer.Length(CircleCenterY)})";
}
var css = new System.Text.StringBuilder("polygon(");
for (int i = 0; i < Count; i++)
{
if (i > 0) css.Append(", ");
css.Append(UiCssSerializer.Length(this[i].X)).Append(' ').Append(UiCssSerializer.Length(this[i].Y));
}
return css.Append(')').ToString();
}
public bool Equals(PanelShape other)
{
if (Kind != other.Kind) return false;
if (Kind == PanelShapeKind.Circle)
return CircleRadius == other.CircleRadius
&& CircleCenterX == other.CircleCenterX
&& CircleCenterY == other.CircleCenterY;
if (Count != other.Count) return false;
for (int i = 0; i < Count; i++)
if (this[i] != other[i]) return false;
return true;
}
public override bool Equals(object? obj) => obj is PanelShape other && Equals(other);
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(Kind);
if (Kind == PanelShapeKind.Circle)
{
hash.Add(CircleRadius);
hash.Add(CircleCenterX);
hash.Add(CircleCenterY);
}
else
{
for (int i = 0; i < Count; i++) hash.Add(this[i]);
}
return hash.ToHashCode();
}
public static bool operator ==(PanelShape left, PanelShape right) => left.Equals(right);
public static bool operator !=(PanelShape left, PanelShape right) => !left.Equals(right);
}