Editor/Services/ArchClearance.cs

Utility for determining how an arch (span) interacts with a horizontal ceiling and for clamping fit values. ArchSplit is a small readonly struct that records whether a span fully clears or is fully blocked and a fractional At value. ArchClearance.Cross returns whether a segment is clears, blocked, or split and computes the interpolation At. ArchClearance.Fits clamps a requested size against limits and logs when a large clamp occurred.

using System;
using Sandbox;

namespace Sunless.Architecture;

// Clears and Blocked are explicit - the three cases build different geometry.
public readonly struct ArchSplit
{
	public bool Clears { get; init; }
	public bool Blocked { get; init; }
	public float At { get; init; }

	public bool Splits => !Clears && !Blocked;
}

// One question that used to carry seven names - a SPLIT, not a truncation: stopping short leaves bare wall.
public static class ArchClearance
{
	// Order-agnostic - a rake read from its high end answers the same as from its low.
	public static ArchSplit Cross( float from, float to, float ceiling )
	{
		if ( from <= ceiling && to <= ceiling )
		{
			return new ArchSplit { Clears = true, At = 1f };
		}

		if ( from >= ceiling && to >= ceiling )
		{
			return new ArchSplit { Blocked = true, At = 0f };
		}

		return new ArchSplit { At = Math.Clamp( (ceiling - from) / (to - from), 0f, 1f ) };
	}

	// It says so when it bites - a silent clamp reads as the setting not working.
	public static float Fits( float requested, float limit, float floor, string what, string why )
	{
		var fitted = MathF.Min( MathF.Max( 0f, requested ), MathF.Max( floor, limit ) );

		if ( requested - fitted > 1f )
		{
			Log.Info( $"Architecture: {what} cut from {requested:0} to {fitted:0} - {why}." );
		}

		return fitted;
	}
}