Editor/Geometry/ArchWallSection.cs
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// What a wall MEASURES, resolved once and never re-derived at a caller: wall, then room, then kit. A handle, an
// elevation, a report, a damage bite and a fixture seat all need these numbers whether or not anything is installed
// to generate the wall itself.
public static class ArchWallSection {
public static float Height( ArchWall wall, ArchRoom room, ArchKit kit ) => Resolve( wall.Height, room?.WallHeight ?? 0f, kit.WallHeight );
public static float Thickness( ArchWall wall, ArchKit kit ) => Resolve( wall.Thickness, 0f, kit.WallThickness );
// What a wall with no height of its own stands at - the ceiling a half wall is measured against.
public static float CeilingHeight( ArchRoom room, ArchKit kit ) => Resolve( 0f, room?.WallHeight ?? 0f, kit.WallHeight );
// Whether the far face exists at all. A facade room says it of every wall it holds, so a shell drawn to be
// seen from one side stays one-sided as it is added to - the ONE answer, or a wall generator and the false
// interior pass disagree about whether there is an inside to line.
public static bool SingleSided( ArchWall wall, ArchRoom room ) => wall.SingleSided || room is { Facade: true };
// Whether there is an inside to finish at all. A skirting, a wainscot and an interior moulding are all
// geometry standing IN a room, so a wall with no far face has nowhere to put them - and the author's own
// switches still have the last word where there is an inside.
public static bool Lined( ArchWall wall, ArchRoom room ) => !SingleSided( wall, room );
// One resolution of the coping section: generator, designer form and report read the same cap.
public static float CapHeight( IArchWallTreatment wall, ArchKit kit ) => Resolve( wall.CapHeight, 0f, kit.WallCapHeight );
public static float CapOverhang( IArchWallTreatment wall, ArchKit kit ) => Resolve( wall.CapOverhang, 0f, kit.CasingDepth );
// A wall is a band, not a line: its centreline grown by its own thickness.
public static List<Vector2> Band( ArchWall wall, ArchKit kit ) {
var side = wall.Normal * (Thickness( wall, kit ) * 0.5f);
return new List<Vector2> { wall.Start + side, wall.End + side, wall.End - side, wall.Start - side };
}
// The one resolution: generator and report agree on how many.
public static IEnumerable<float> PilasterCentres( ArchPilasterRun run, float from, float to ) {
var span = to - from;
if ( run is null || !run.Stands || span < 1f ) {
yield break;
}
var strip = MathF.Min( run.Width, span );
var division = run.Count > 0 ? ArchDivide.Into( span, Math.Max( 1, run.Count - 1 ) ) : ArchDivide.AtMost( span, run.Bay );
foreach ( var node in division.Nodes ) {
yield return Math.Clamp( from + node, from + strip * 0.5f, to - strip * 0.5f );
}
}
static float Resolve( float value, float roomValue, float kitValue ) {
if ( value > 0f ) {
return value;
}
return roomValue > 0f ? roomValue : kitValue;
}
}