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;
}
}