Editor/Wall/ArchLadderGen.cs

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.

File Access
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 };
}