Editor utility that generates ladder geometry for architectural walls. It computes ladder shape from parameters and emits beams for stiles, rungs, returns, wall brackets and a protective cage into an ArchMesh using style brushes and kit settings.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
public static class ArchLadderGen
{
public static void Build( ArchMesh canvas, ArchLadderPart ladder, ArchBuilding building, ArchKit kit, ArchStyle style )
{
var shape = ArchLadder.Resolve( ladder, kit );
if ( shape.Head - shape.Foot < shape.Bar * 2f )
{
return;
}
var brush = style.Brush( ArchSurface.Railing, new[] { ladder.Palette, building.Palette } );
// The stiles, rungs and top return ARE the ladder; the hoops and the wall brackets are hung on it.
Stiles( canvas, shape, brush );
Rungs( canvas, shape, brush );
Return( canvas, shape, brush );
using ( canvas.Part( ArchPieces.Fittings ) )
{
Brackets( canvas, shape, kit, brush );
}
using ( canvas.Part( ArchPieces.Enclosure ) )
{
Cage( canvas, shape, kit, brush );
}
}
static void Stiles( ArchMesh canvas, ArchLadderShape shape, ArchBrush brush )
{
foreach ( var side in Sides )
{
var stile = shape.Stile( side );
canvas.Beam(
stile + shape.Facing * shape.Back,
stile + shape.Facing * shape.Front,
-shape.Bar * 0.5f, shape.Bar * 0.5f,
shape.Foot, shape.Top, brush );
}
}
// Centre to centre, so a rung end is buried in the stile rather than coplanar with its face.
static void Rungs( ArchMesh canvas, ArchLadderShape shape, ArchBrush brush )
{
var depth = (shape.Back + shape.Front) * 0.5f;
foreach ( var height in shape.Rungs )
{
canvas.Beam(
shape.Stile( -1f ) + shape.Facing * depth,
shape.Stile( 1f ) + shape.Facing * depth,
-shape.Bar * 0.5f, shape.Bar * 0.5f,
height - shape.Bar * 0.5f, height + shape.Bar * 0.5f, brush );
}
}
static void Brackets( ArchMesh canvas, ArchLadderShape shape, ArchKit kit, ArchBrush brush )
{
var bite = ArchContact.Bite( kit );
foreach ( var side in Sides )
{
var stile = shape.Stile( side );
foreach ( var height in shape.Brackets )
{
canvas.Beam(
stile - shape.Facing * bite,
stile + shape.Facing * shape.Back,
-shape.Bar * 0.5f, shape.Bar * 0.5f,
height - shape.Bar * 0.5f, height + shape.Bar * 0.5f, brush );
}
}
}
// Through ArchRun so the U mitres its own turns; straps sit on that same path.
static void Cage( ArchMesh canvas, ArchLadderShape shape, ArchKit kit, ArchBrush brush )
{
if ( shape.Hoops.Count == 0 )
{
return;
}
var hoop = shape.Hoop();
var half = shape.Bar * 0.5f;
foreach ( var height in shape.Hoops )
{
ArchRun.Along( hoop )
.Level( height )
.Emit( canvas, ArchBandSection.Between( -half, half, height - half, height + half ), brush );
}
var path = ArchRunPath.Of( hoop );
foreach ( var offset in ArchDivide.AtMost( path.Length, kit.LadderCageSpacing ).Nodes )
{
if ( !path.Station( offset, out var station ) )
{
continue;
}
canvas.Beam(
station.Point - station.Along * half,
station.Point + station.Along * half,
-half, half,
shape.Hoops[0], shape.Hoops[^1], brush );
}
}
// The stiles already stand past the head; this is the arm turning in and the leg dropping.
static void Return( ArchMesh canvas, ArchLadderShape shape, ArchBrush brush )
{
if ( shape.ReturnRise < 0.05f )
{
return;
}
var half = shape.Bar * 0.5f;
var depth = (shape.Back + shape.Front) * 0.5f;
foreach ( var side in Sides )
{
var stile = shape.Stile( side );
var inboard = stile - shape.Facing * shape.ReturnReach;
canvas.Beam(
stile + shape.Facing * depth,
inboard,
-half, half,
shape.Top - shape.Bar, shape.Top, brush );
canvas.Beam(
inboard - shape.Along * half * side,
inboard + shape.Along * half * side,
-half, half,
shape.Head, shape.Top, brush );
}
}
static readonly float[] Sides = { -1f, 1f };
}