UI/GaugeArc.cs

A UI Panel that draws an arc-style gauge using an SDF arc shader. It exposes properties for value, sweep, radii, colors, segmentation and writes shader parameters to CommandList before drawing a quad with the UI box material.

Native Interop
using Sandbox.Rendering;
using Sandbox.UI;
using System;
using System.Xml.Linq;

namespace Machines.UI;

/// <summary>
/// A panel that draws an arc gauge using the SDF arc shader. Set Value (0-1) to control fill.
/// </summary>
public class GaugeArc : Panel, IPanelDraw
{
	private static Material _material;

	/// <summary>
	/// Current fill value (0 = empty, 1 = full).
	/// </summary>
	public float Value { get; set; }

	/// <summary>
	/// Total arc sweep in degrees (default 90 = 25% of circle).
	/// </summary>
	public float MaxSweepDegrees { get; set; } = 90f;

	/// <summary>
	/// Inner radius of the ring (0-1 in UV space).
	/// </summary>
	public float InnerRadius { get; set; } = 0.75f;

	/// <summary>
	/// Outer radius of the ring (0-1 in UV space).
	/// </summary>
	public float OuterRadius { get; set; } = 0.95f;

	/// <summary>
	/// Color of the filled portion.
	/// </summary>
	public Color FillColor { get; set; } = Color.Cyan;

	/// <summary>
	/// Color of the unfilled track background.
	/// </summary>
	public Color TrackColor { get; set; } = new Color( 0.15f, 0.15f, 0.15f, 0.6f );

	/// <summary>
	/// Start angle offset in degrees (0 = top).
	/// </summary>
	public float StartAngleDegrees { get; set; } = 0f;

	/// <summary>
	/// Rounded cap radius (0 = sharp, 1 = maximum rounding).
	/// </summary>
	public float CapRadius { get; set; } = 0.3f;

	/// <summary>
	/// Number of segments (0 or 1 = no gaps).
	/// </summary>
	public int SegmentCount { get; set; } = 0;

	/// <summary>
	/// Gap angle between segments in degrees.
	/// </summary>
	public float GapAngleDegrees { get; set; } = 0f;

	/// <summary>
	/// Optional fill gradient end color. Null = solid fill (no gradient).
	/// </summary>
	public Color? FillColorEnd { get; set; } = null;

	/// <summary>
	/// Optional track gradient end color. Null = solid track (no gradient).
	/// </summary>
	public Color? TrackColorEnd { get; set; } = null;

	public GaugeArc()
	{
		if ( !_material.IsValid() ) _material = Material.FromShader( "shaders/sdf_arc.shader" );
	}

	void IPanelDraw.Draw( CommandList cl )
	{
		if ( !_material.IsValid() ) return;

		var sweepRad = MaxSweepDegrees * (MathF.PI / 180f);
		var fillRad = MathF.Max( 0f, MathF.Min( Value, 1f ) ) * sweepRad;
		var startRad = StartAngleDegrees * (MathF.PI / 180f);

		var rect = Box.Rect;

		cl.Attributes.Set( "FillAngle", fillRad );
		cl.Attributes.Set( "SweepAngle", sweepRad );
		cl.Attributes.Set( "InnerRadius", InnerRadius );
		cl.Attributes.Set( "OuterRadius", OuterRadius );
		cl.Attributes.Set( "FillColor", FillColor );
		cl.Attributes.Set( "TrackColor", TrackColor );
		cl.Attributes.Set( "StartAngle", startRad );
		cl.Attributes.Set( "CapRadius", CapRadius );
		cl.Attributes.Set( "SegmentCount", (float)SegmentCount );
		cl.Attributes.Set( "GapAngle", GapAngleDegrees * (MathF.PI / 180f) );

		var sentinel = new Color( -1f, -1f, -1f, -1f );
		cl.Attributes.Set( "FillColorEnd", FillColorEnd ?? sentinel );
		cl.Attributes.Set( "TrackColorEnd", TrackColorEnd ?? sentinel );

		cl.DrawQuad( rect, Material.UI.Box, Color.White );
	}
}