Part of the editor-side geometry output. Defines ArchMeshPiece (a named sub-canvas) and a partial ArchMesh that manages named pieces, splitting behavior, opening a piece scope, and tracking whether the mesh emitted geometry.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sunless.Architecture;
// A named piece of what one layer built - a fascia, a balustrade, a gutter channel. The generator says what it
// is emitting and the output service files each one as its own object, so a piece can be hidden, collided and
// argued about apart from the part that owns it.
public sealed class ArchMeshPiece
{
public string Name { get; init; }
public ArchMesh Canvas { get; init; }
}
public sealed partial class ArchMesh
{
readonly List<ArchMeshPiece> pieces = new();
// Only a canvas the output service made splits. A generator driven straight from a test or a measurement
// wants ONE mesh out the other end, and every Part scope on it has to be the no-op it reads as.
bool splits;
ArchMesh open;
public IReadOnlyList<ArchMeshPiece> Pieces => pieces;
public ArchMesh Splitting()
{
splits = true;
return this;
}
// Everything emitted inside the scope belongs to a named piece of this part, and a name reopened adds to the
// piece already standing - a generator walks its regions and comes back to the same fitting on each one.
public IDisposable Part( string name )
{
if ( open is { } inner )
{
return inner.Part( name );
}
if ( !splits || string.IsNullOrEmpty( name ) )
{
return null;
}
open = Piece( name );
return new PartScope( this );
}
ArchMesh Piece( string name )
{
if ( pieces.FirstOrDefault( piece => piece.Name == name ) is { } held )
{
return held.Canvas;
}
var canvas = new ArchMesh( projection ) { splits = true };
// A piece welds the way the part it came out of does, or a board split onto its own canvas grows the
// doubled corners the part was welded to avoid.
if ( welds is not null )
{
canvas.Welded();
}
pieces.Add( new ArchMeshPiece { Name = name, Canvas = canvas } );
return canvas;
}
readonly struct PartScope( ArchMesh canvas ) : IDisposable
{
public void Dispose() => canvas.open = null;
}
// Whether anything at all came out of this canvas, its named pieces included: a part whose every face went
// into a piece is empty itself, and the node it stands on still has to be placed.
public bool Emitted => !IsEmpty || pieces.Any( piece => piece.Canvas.Emitted );
}