Editor/Services/ArchStoreyEdges.cs
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

public readonly record struct ArchSnapLoop( IReadOnlyList<Vector2> Loop, bool Above );

// Every non-wall edge on this storey a plan point can snap to.
public static class ArchStoreyEdges {
	public static List<ArchSnapLoop> On( ArchPlan plan, ArchKit kit, int level ) {
		var edges = new List<ArchSnapLoop>();

		foreach ( var building in plan?.Buildings ?? Enumerable.Empty<ArchBuilding>() ) {
			// Plinth first — on a raised foundation it is the visible edge, not the shell behind it.
			Take( edges, ArchFloorGen.FoundationOutline( plan, building, kit, level ), false );

			Take( edges, ArchRegion.Shell( ArchRegion.Storey( building, level ), kit.WallThickness ), false );

			foreach ( var room in building.Rooms.Where( standing => standing.Floor > level && ArchLayerGate.On( standing ) ) ) {
				Take( edges, ArchRegion.Shell( ArchRegion.Footprints( new[] { room }, false ), kit.WallThickness ), true );
				Take( edges, ArchOverhangGen.Unsupported( room, building, kit ), true );
			}

			foreach ( var platform in building.Platforms.Where( ArchLayerGate.On ) ) {
				Take( edges, new List<List<Vector2>> { platform.Outline() }, platform.Hung );
			}
		}

		return edges;
	}

	static void Take( List<ArchSnapLoop> edges, IEnumerable<List<Vector2>> loops, bool above ) {
		foreach ( var loop in loops.Where( standing => standing.Count >= 3 ) ) {
			edges.Add( new ArchSnapLoop( loop, above ) );
		}
	}
}