Editor UI/tool code for walkway editing. Defines ArchWalkwayHandler which handles user interactions for editing walkway geometry (reshaping, moving, height adjustments) and ArchWalkwayPreview which draws a ghost/preview of a walkway (volume, plate, brackets, footprints, labels).
namespace Sunless.Architecture;
public sealed class ArchWalkwayHandler : IArchHandler
{
public ArchKind Kind => ArchKind.Walkway;
public bool Draw( ArchTool tool, ArchSelection picked )
{
if ( picked.Item is not ArchRoom room )
{
return false;
}
var outline = ArchFloorGen.Footprint( room );
if ( outline.Count < 3 )
{
return false;
}
var gesture = ArchGesture.On( ArchKind.Walkway, room.Id );
var deck = ArchWalkway.Deck( tool.Plan, room );
var top = deck?.BaseHeight ?? MathF.Max( room.BaseHeight + room.WallHeight, room.WalkwayTop );
var reshaped = ArchShapeHandles.Corners( tool, gesture, outline, top );
reshaped |= ArchShapeHandles.Edges( tool, gesture, outline, top );
if ( reshaped )
{
ArchWalkway.Reshape( tool.Plan, room, outline, tool.Kit );
}
var middle = Middle( ArchFloorGen.Footprint( room ) );
var changed = reshaped;
if ( ArchShapeHandles.Move( tool, gesture.At( "body" ), middle, out var moved ) && (moved - middle).Length >= 0.01f )
{
var shifted = ArchFloorGen.Footprint( room );
ArchRemap.Translation( moved - middle ).Loop( shifted );
ArchWalkway.Reshape( tool.Plan, room, shifted, tool.Kit );
changed = true;
}
if ( ArchShapeHandles.Height( tool, gesture.At( "deck" ), middle, top, out var head, Gizmo.Colors.Roll ) )
{
ArchWalkway.Reheight( room, deck, tool.Kit, MathF.Max( 60f, head - room.BaseHeight ) );
changed = true;
}
if ( ArchShapeHandles.Height( tool, gesture.At( "floor" ), middle, room.BaseHeight, out var floor, Gizmo.Colors.Pitch ) )
{
ArchWalkway.Raise( tool.Plan, room, tool.Kit, floor );
changed = true;
}
return changed;
}
static Vector2 Middle( IReadOnlyList<Vector2> outline )
{
if ( outline is not { Count: > 0 } )
{
return Vector2.Zero;
}
ArchFootprint.Bounds( outline, out var min, out var max );
return (min + max) * 0.5f;
}
}
public sealed class ArchWalkwayPreview : IArchPreview
{
public ArchKind Kind => ArchKind.Walkway;
public void Draw( ArchPreviewContext context )
{
if ( context.Payload is not ArchRoom link || context.Tool is null )
{
return;
}
var loop = ArchFloorGen.Footprint( link );
if ( loop.Count < 3 )
{
return;
}
ArchFootprint.Bounds( loop, out var min, out var max );
var deck = ArchWalkway.Deck( context.Tool.Plan, link );
var floor = link.BaseHeight + context.Lift;
var head = (deck?.BaseHeight ?? link.BaseHeight + link.WallHeight) + context.Lift;
ArchGhost.Volume( min, max, floor, head );
ArchGhost.Plate( min, max, floor, 8 );
ArchGhost.Brackets( min, max, head );
var alongX = max.x - min.x >= max.y - min.y;
var run = alongX ? max.x - min.x : max.y - min.y;
var across = alongX ? max.y - min.y : max.x - min.x;
Mouth( min, max, alongX, true, floor, head );
Mouth( min, max, alongX, false, floor, head );
ArchGhost.Note( new Vector3( max.x, max.y, head ), $"walkway — {run:0} x {across:0}, {head - floor:0} clear" );
}
static void Mouth( Vector2 min, Vector2 max, bool alongX, bool low, float bottom, float top )
{
var start = alongX
? new Vector2( low ? min.x : max.x, min.y )
: new Vector2( min.x, low ? min.y : max.y );
var end = alongX
? new Vector2( low ? min.x : max.x, max.y )
: new Vector2( max.x, low ? min.y : max.y );
ArchGhost.Footprint( new List<Vector2> { start, end }, bottom, ArchGhost.Accent );
ArchGhost.Footprint( new List<Vector2> { start, end }, top, ArchGhost.Accent );
Gizmo.Draw.Color = ArchGhost.Accent;
Gizmo.Draw.Line( new Vector3( start.x, start.y, bottom ), new Vector3( start.x, start.y, top ) );
Gizmo.Draw.Line( new Vector3( end.x, end.y, bottom ), new Vector3( end.x, end.y, top ) );
}
}