Shapes.cs
using System;
using Sandbox;
using Sandbox.UI;
namespace Goo;
/// <summary>Factories for composite shape subtrees. Rotation-applying helpers default <c>PointerEvents.None</c> so rotated descendants do not drift parent hit dispatch.</summary>
public static class Shapes
{
/// <summary>N <see cref="Sector"/> wedges clockwise from 12 o'clock; the subtree is <c>PointerEvents.None</c> so parent dispatchers see the unrotated frame.</summary>
public static Container Ring(int segments, float innerRadius, ReadOnlySpan<Color> colors,
float outerRadius = 1.0f, string? key = null)
{
if (segments <= 0)
throw new ArgumentException("segments must be > 0", nameof(segments));
if (colors.Length != segments)
throw new ArgumentException($"colors.Length ({colors.Length}) must equal segments ({segments})", nameof(colors));
var outer = new Container
{
Key = key,
Position = PositionMode.Relative,
Width = Length.Percent(100),
Height = Length.Percent(100),
PointerEvents = PointerEvents.None,
};
float wedge = 360f / segments;
float halfWedge = 0.5f * wedge;
for (int i = 0; i < segments; i++)
{
float rot = i * wedge - halfWedge;
var wrapper = new Container
{
Key = $"ring-slot-{i}",
Position = PositionMode.Absolute,
Top = 0,
Left = 0,
Width = Length.Percent(100),
Height = Length.Percent(100),
PointerEvents = PointerEvents.None,
Transform = Goo.PanelTransform.Rotate(rot),
};
wrapper.Children.Add(new Sector
{
Width = Length.Percent(100),
Height = Length.Percent(100),
StartAngle = 0f,
EndAngle = wedge,
InnerRadius = innerRadius,
OuterRadius = outerRadius,
BackgroundColor = colors[i],
});
outer.Children.Add(wrapper);
}
return outer;
}
/// <summary>Color-array overload; forwards to the <see cref="ReadOnlySpan{T}"/> form. Prefer the span overload from animated <c>Build()</c> bodies to avoid per-frame allocation.</summary>
public static Container Ring(int segments, float innerRadius, Color[] colors,
float outerRadius = 1.0f, string? key = null)
{
if (colors is null)
throw new ArgumentException("colors must not be null", nameof(colors));
return Ring(segments, innerRadius, (ReadOnlySpan<Color>)colors, outerRadius, key);
}
/// <summary>A pre-rounded circular <see cref="Container"/> (50% radius, 100% size) that drops into a flex slot.</summary>
public static Container Disc(string? key = null)
=> new Container
{
Key = key,
Width = Length.Percent(100),
Height = Length.Percent(100),
BorderRadius = Length.Percent(50),
};
}