Utility class for pillar soffit calculations in the editor architecture code. Computes the overhead soffit height above a pillar part by checking room ceiling, platforms and beams that overlap the pillar footprint.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
// What a column with no authored height stands up TO. The ceiling's own UNDERSIDE is where the answer starts -
// the plate is the top of that body, and a capital measured off it finishes as deep inside the slab as the slab
// is thick. A beam or a hung platform crossing the same footprint is lower again, and a column that ignored one
// would stand straight through the thing it is there to carry.
public static class ArchPillarSoffit
{
// Anything nearer than this to the column's own foot is something it stands ON rather than under.
const float Clear = 8f;
public static float Over( ArchPlan plan, ArchKit kit, ArchRoom room, ArchPillarPart pillar )
{
var host = plan?.OwnerOf( room );
var ceiling = host is null
? room.BaseHeight + ArchFloorGen.WallHeight( room, kit )
: ArchFloorGen.Overhead( host, room, kit );
if ( plan is null )
{
return ceiling;
}
var footprint = Footprint( pillar );
var floor = pillar.BaseHeight;
var soffit = ceiling;
foreach ( var building in plan.Buildings )
{
foreach ( var platform in building.Platforms.Where( standing => standing.Hung && ArchLayerGate.On( standing ) ) )
{
soffit = Lower( soffit, platform.GradeHeight, floor, footprint, platform.Outline() );
}
foreach ( var beam in building.Rooms.SelectMany( standing => standing.Beams ).Where( ArchLayerGate.On ) )
{
soffit = Lower( soffit, beam.Soffit, floor, footprint, beam.Outline() );
}
}
return soffit;
}
// The bounds of every column in the part, section included: ONE head for the whole grid, the way a roof
// stands one parapet height for every one of its edges. Two columns under one arcade arriving at different
// heights spring it from nowhere.
public static List<Vector2> Footprint( ArchPillarPart pillar )
{
var half = pillar.Half;
var min = new Vector2( float.MaxValue, float.MaxValue );
var max = new Vector2( float.MinValue, float.MinValue );
foreach ( var column in ArchPillarGen.Columns( pillar ) )
{
var at = new Vector2( column.At.x, column.At.y );
min = Vector2.Min( min, at - half );
max = Vector2.Max( max, at + half );
}
return ArchFootprint.Rect( min, max );
}
static float Lower( float soffit, float candidate, float floor, IReadOnlyList<Vector2> footprint, IReadOnlyList<Vector2> outline )
{
if ( candidate <= floor + Clear || candidate >= soffit || !ArchFootprint.Overlaps( outline, footprint ) )
{
return soffit;
}
return candidate;
}
}