Defines three classes for generating mesh sections along a path: an abstract ArchRunSection, ArchBandSection which creates rectangular band geometry along edges with miter handling, and ArchProfileSection which extrudes a profile along the path.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
// Subclass only for a shape the three below cannot make; do not re-implement the walk.
public abstract class ArchRunSection
{
public abstract void Emit( ArchMesh canvas, ArchRunPath path, ArchBrush brush );
}
// Each corner belongs to the edge LEAVING it - carried past convex turns, back at reflex, none at open ends.
public sealed class ArchBandSection : ArchRunSection
{
public float Shift { get; init; }
public float Depth { get; init; }
public float Bottom { get; init; }
public float Top { get; init; }
public static ArchBandSection Between( float near, float far, float bottom, float top )
{
return new ArchBandSection { Shift = near, Depth = far - near, Bottom = bottom, Top = top };
}
public override void Emit( ArchMesh canvas, ArchRunPath path, ArchBrush brush )
{
if ( Depth < 0.05f || Top - Bottom < 0.05f )
{
return;
}
var far = Shift + Depth;
for ( var edge = 0; edge < path.Edges; edge++ )
{
var start = path.At( edge );
var end = path.At( edge + 1 );
var span = end - start;
var length = span.Length;
if ( length < 0.05f )
{
continue;
}
var direction = span / length;
// MITRED, not lapped. Running one edge past the corner and butting the other into its side put
// three faces on the corner's edge and a coplanar pair beside it; folding both onto the bisector
// is the join a board actually has, and it is ArchBandGen's answer rather than a second one here.
// -1 because ArchRegion.Outward - the side Beam measures its offsets on - is the right-hand normal
// and ArchBandGen.Outward is the left-hand one.
var foldFrom = path.Continues( edge ) ? ArchBandGen.Fold( path.At( edge ) - path.At( edge - 1 ), direction, -1f ) : 0f;
var foldTo = path.Continues( edge + 1 ) ? ArchBandGen.Fold( direction, path.At( edge + 2 ) - path.At( edge + 1 ), -1f ) : 0f;
canvas.Beam( start, end, Shift, far, Bottom, Top, brush, foldFrom, foldTo );
}
}
}
// The extruder mitres closed runs itself; open ends are simply capped.
public sealed class ArchProfileSection : ArchRunSection
{
public ArchProfile Profile { get; init; }
public float Height { get; init; }
public float Scale { get; init; } = 1f;
public Rotation Roll { get; init; } = Rotation.Identity;
public override void Emit( ArchMesh canvas, ArchRunPath path, ArchBrush brush )
{
if ( Profile is null || !Profile.IsUsable || path.Points.Count < 2 )
{
return;
}
canvas.Extrude( path.Raised( Height ), Profile, Scale, Roll, brush, path.Closed );
}
}