Editor/Stair/ArchApproachExtentProvider.cs

Editor provider that measures the 2D footprint and vertical extent of an "approach" arch piece. It handles spline and straight approaches, sampling spline frames or computing resolved ramp/foot/wall positions and returns an ArchExtent covering the polygon and height range.

Reflection
using System;
using System.Collections.Generic;
using Sandbox;

namespace Sunless.Architecture;

public sealed class ArchApproachExtentProvider : IArchExtentProvider
{
	public ArchKind Kind => ArchKind.Approach;

	// Reads the resolved run: a drive clamped to the verge would otherwise read as overshoot.
	public ArchExtent Measure( object payload, ArchExtentContext context )
	{
		if ( payload is not ArchApproachPart approach || context.Room is null )
		{
			return new ArchExtent();
		}

		var room = context.Room;
		var building = context.Building;
		var kit = context.Kit;
		var plan = context.Plan;

		if ( approach.IsSpline )
		{
			var curved = new ArchExtent();

			foreach ( var frame in ArchApproachGen.SplineCurve( approach, kit ).Walk( MathF.Max( 8f, kit.GridSize * 0.5f ) ) )
			{
				var half = MathF.Max( 12f, approach.Width * frame.WidthScale ) * 0.5f;
				curved.Add( frame.Side( -half ) );
				curved.Add( frame.Side( half ) );
			}

			return curved.Grown( 8f );
		}

		var rise = ArchApproachGen.Rise( room, building, kit, plan );
		var across = ArchApproachAxes.Across( approach ) * ArchApproachAxes.HalfWidth( approach );
		var resolved = ArchApproachGen.Resolved( approach, room, building, kit, plan, out var ramp );

		var wall = resolved ? ramp.Wall : approach.Origin;
		var foot = resolved
			? ramp.Foot
			: approach.Origin + ArchApproachAxes.Outward( approach ) * (approach.Run > 1f
				? approach.Run
				: rise / MathF.Max( 0.02f, kit.StepRise / MathF.Max( 1f, kit.StepGoing ) ));

		var extent = new ArchExtent();

		extent.Add(
			new List<Vector2> { wall + across, foot + across, foot - across, wall - across },
			room.BaseHeight - rise - 8f,
			room.BaseHeight + 48f );

		return extent;
	}
}