Editor/Roof/ArchRoofRegionService.cs
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
public sealed class ArchRoofRegion {
public List<List<Vector2>> Loops { get; init; } = new();
public List<Vector2> Outer => Loops.FirstOrDefault();
}
public sealed class ArchRoofRegionService {
readonly ArchBuilding building;
readonly ArchRoofPart roof;
public ArchRoofRegionService( ArchBuilding building, ArchRoofPart roof ) {
this.building = building;
this.roof = roof;
}
public List<ArchRoofRegion> Resolve() {
var outline = ArchFootprint.Wind( roof.Outline() );
var rooms = ArchRegion.Footprints( building?.Rooms?.Where( room => room.Floor == roof.Level ), false );
if ( rooms.Count == 0 ) {
return new List<ArchRoofRegion> { new() { Loops = new List<List<Vector2>> { outline } } };
}
var occupied = ArchFootprint.Union( rooms );
// INTERSECTED with the roof's own outline, not clamped to its bounds: a box clamp fills an L's notch
// straight back in, and a cell straddling the outline either spills the deck past it or throws away a
// lap the author drew - a walkway's ceiling stopping short of the wall it runs into.
var loops = ArchFootprint.Intersect( occupied, outline )
.Where( ArchFootprint.IsSimple )
.ToList();
// Standing over no room at all is a FREE section - a porch canopy, whose apron is outside every room
// by construction - and it covers its own outline, exactly as one on a building with no rooms does.
// Returning nothing here is why a porch roof could be authored, seated and reported and still build
// not one face: the clip threw the whole deck away and said nothing.
if ( loops.Count == 0 ) {
return new List<ArchRoofRegion> { new() { Loops = new List<List<Vector2>> { outline } } };
}
var outers = ArchFootprint.Outer( loops ).ToList();
var holes = loops.Where( loop => ArchFootprint.SignedArea( loop ) < 0f ).ToList();
var regions = outers
.Select( outer => new ArchRoofRegion {
Loops = new[] { outer }
.Concat( holes.Where( hole => ArchFloorGen.Contains( outer, hole[0] ) ) )
.ToList()
} )
.ToList();
return regions;
}
}