Editor/Porch/ArchPorchInteractions.cs

Editor-side handlers for porch architecture parts. Implements interaction drawing (height gizmos), picking (hit testing against porch bounds), and extent measurement (spatial extents for legs) for ArchKind.Porch.

File Access
namespace Sunless.Architecture;

public sealed class ArchPorchHandler : IArchHandler
{
	public ArchKind Kind => ArchKind.Porch;

	public bool Draw( ArchTool tool, ArchSelection picked )
	{
		if ( picked.Item is not ArchPorchPart porch )
		{
			return false;
		}

		var outline = ArchPorch.Footprint( porch );
		var middle = Middle( outline );
		var gesture = ArchGesture.On( ArchKind.Porch, porch.Id );
		var changed = false;

		if ( ArchShapeHandles.Height( tool, gesture.At( "head" ), middle, porch.BaseHeight + porch.HeadHeight, out var head, Gizmo.Colors.Roll ) )
		{
			porch.HeadHeight = MathF.Max( 24f, head - porch.BaseHeight );
			changed = true;
		}

		if ( ArchShapeHandles.Height( tool, gesture.At( "deck" ), middle, porch.BaseHeight, out var deck, Gizmo.Colors.Pitch ) )
		{
			porch.BaseHeight = deck;
			changed = true;
		}

		return changed;
	}

	static Vector2 Middle( IReadOnlyList<Vector2> outline )
	{
		if ( outline is not { Count: > 0 } )
		{
			return Vector2.Zero;
		}

		ArchFootprint.Bounds( outline, out var min, out var max );

		return (min + max) * 0.5f;
	}
}

public sealed class ArchPorchPicker : IArchPicker
{
	public ArchKind Kind => ArchKind.Porch;

	public void Under( ArchPicking picking )
	{
		if ( picking.Node.Payload is not ArchPorchPart porch )
		{
			return;
		}

		ArchPorch.Bounds( porch, out var min, out var max );

		if ( ArchFootprint.Contains( ArchFootprint.Rect( min, max ), picking.Point ) )
		{
			picking.Hit( ArchHitChannel.Volume, 0f, true );
		}
	}
}

public sealed class ArchPorchExtentProvider : IArchExtentProvider
{
	public ArchKind Kind => ArchKind.Porch;

	public ArchExtent Measure( object payload, ArchExtentContext context )
	{
		var extent = new ArchExtent();

		if ( payload is not ArchPorchPart porch )
		{
			return extent;
		}

		foreach ( var leg in porch.Legs )
		{
			extent.Add( ArchFootprint.Rect( leg.Min, leg.Max ), porch.GradeHeight - 8f, porch.BaseHeight + porch.HeadHeight + 48f );
		}

		return extent;
	}
}