Utilities to compute spatial extents for architecture parts. Defines functions that produce ArchExtent bounds for buildings, rooms, walls, openings, roofs, platforms, beams, cuts, downpipes, fences, trims and helper RoadYaw.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
public static partial class ArchLocate
{
static ArchExtent BuildingExtent( ArchBuilding building, ArchKit kit, int? level = null )
{
var extent = new ArchExtent();
foreach ( var room in building.Rooms.Where( room => !level.HasValue || room.Floor == level ) )
{
extent.Add( RoomExtent( room, kit ) );
}
foreach ( var roof in building.Roofs.Where( roof => !level.HasValue || roof.Level == level ) )
{
extent.Add( RoofExtent( roof ) );
}
foreach ( var fence in building.Fences )
{
extent.Add( FenceExtent( fence ) );
}
return extent;
}
public static ArchExtent RoomExtent( ArchRoom room, ArchKit kit )
{
var extent = new ArchExtent();
var providers = ArchExtents.Load();
var top = room.BaseHeight + ArchFloorGen.WallHeight( room, kit );
extent.Add( ArchFloorGen.Footprint( room ), room.BaseHeight - kit.FloorThickness, top );
foreach ( var wall in room.Walls )
{
extent.Add( WallExtent( wall, room, kit ) );
}
foreach ( var stair in room.Stairs )
{
extent.Add( providers.Measure( ArchKind.Stair, stair,
new ArchExtentContext( null, kit, room, null ) ) );
}
foreach ( var porch in room.Porches )
{
extent.Add( providers.Measure( ArchKind.Porch, porch,
new ArchExtentContext( null, kit, room, null ) ) );
}
return extent;
}
static ArchExtent WallExtent( ArchWall wall, ArchRoom room, ArchKit kit )
{
var extent = new ArchExtent();
var half = WallThickness( wall, kit ) * 0.5f;
var side = wall.Normal * half;
var top = room.BaseHeight + WallHeight( wall, room, kit );
extent.Add( wall.Start + side, room.BaseHeight, top );
extent.Add( wall.Start - side, room.BaseHeight, top );
extent.Add( wall.End + side, room.BaseHeight, top );
extent.Add( wall.End - side, room.BaseHeight, top );
return extent;
}
static ArchExtent OpeningExtent( ArchOpening opening, ArchWall wall, ArchRoom room, ArchKit kit )
{
var extent = new ArchExtent();
var half = WallThickness( wall, kit ) * 0.5f;
var side = wall.Normal * half;
var bottom = room.BaseHeight + opening.SillHeight;
var top = room.BaseHeight + opening.Top;
foreach ( var along in new[] { opening.Left, opening.Right } )
{
var point = wall.PointAt( along );
extent.Add( point + side, bottom, top );
extent.Add( point - side, bottom, top );
}
return extent;
}
static ArchExtent RoofExtent( ArchRoofPart roof )
{
var extent = new ArchExtent();
var outline = roof.Outline();
var span = MathF.Max( roof.Size.x, roof.Size.y ) * 0.5f;
var rise = roof.Style is RoofStyle.Flat ? roof.ParapetHeight : span * ArchPitch.Slope( roof.Pitch );
extent.Add( ArchFootprint.Grow( new List<List<Vector2>> { ArchFootprint.Wind( outline ) }, roof.Overhang )
.SelectMany( loop => loop ), roof.BaseHeight - 24f, roof.BaseHeight + rise + roof.Thickness );
return extent;
}
static ArchExtent PlatformExtent( ArchPlatformPart platform )
{
var extent = new ArchExtent();
// A ramp wears no coping, so counting one in leaves the audit reporting an envelope nothing ever fills.
var band = ArchRamp.Rakes( platform ) ? 0f : platform.CopingHeight;
extent.Add( platform.Outline(), platform.GradeHeight, platform.TopHeight + band );
return extent;
}
static ArchExtent BeamExtent( ArchBeamPart beam )
{
var extent = new ArchExtent();
extent.Add( beam.Outline(), beam.Soffit, beam.TopHeight );
return extent;
}
static ArchExtent CutExtent( ArchCutPart cut )
{
var extent = new ArchExtent();
foreach ( var segment in cut.Segments.Where( leg => leg.Length > 1f ) )
{
extent.Add( segment.Outline(), segment.BaseHeight, segment.TopHeight );
}
return extent;
}
static ArchExtent DownpipeExtent( ArchDownpipePart pipe )
{
var extent = new ArchExtent();
extent.Add( pipe.Outlet, pipe.BaseHeight, pipe.TopHeight );
extent.Add( pipe.Wall, pipe.BaseHeight, pipe.TopHeight );
return extent.Grown( 6f );
}
// The line between the ends - the elevation a road is actually read from.
static float RoadYaw( ArchRoadPart road )
{
if ( road.Nodes.Count < 2 )
{
return 0f;
}
var run = road.Nodes[^1].Position - road.Nodes[0].Position;
return MathF.Atan2( run.y, run.x ).RadianToDegree();
}
static ArchExtent FenceExtent( ArchFencePart fence )
{
var extent = new ArchExtent();
var top = fence.Barrier.Height + fence.Barrier.PostOverhang + fence.Barrier.DropperLength;
foreach ( var node in fence.Nodes )
{
extent.Add( node.Position );
extent.Add( node.Position + Vector3.Up * top );
}
return extent.Grown( 4f );
}
static ArchExtent TrimExtent( ArchTrimPart trim )
{
var extent = new ArchExtent();
foreach ( var point in trim.Path )
{
extent.Add( point );
}
return extent.Grown( 8f );
}
}