Editor/Barriers/ArchBarrierCatenary.cs

Editor helper for drawing barrier poles and hanging strands. Provides Pole to revolve a pole mesh from bottom to top and Strands to create multiple hanging wire strands along a bay shape, skipping broken strands via noise.

File Access
using System;
using System.Collections.Generic;
using Sandbox;

namespace Sunless.Architecture;

// Poles and the lines slung between them. The sag belongs to the bay's own resolve and is never re-derived
// here, so the drag preview and the built strand are the same curve.
public static class ArchBarrierCatenary
{
	const int PoleSides = 8;

	// The conductors hang on the head of the pole, so one height sets both the pole and where the lines sit.
	const float HeadBand = 0.25f;

	public static void Pole( ArchMesh canvas, ArchBarrierPost post, ArchBarrierSpec spec, ArchBarrierBrushes brushes, float bottom, float top )
	{
		var radius = MathF.Max( 1.5f, spec.PostSize ) * 0.5f;
		var rise = top - bottom;

		if ( rise < 1f )
		{
			return;
		}

		canvas.Revolve( new Vector3( post.Flat.x, post.Flat.y, bottom ), new List<Vector2>
		{
			new( radius, 0f ),
			new( radius * 0.78f, rise )
		}, PoleSides, 1f, 1f, brushes.Post );
	}

	public static void Strands( ArchMesh canvas, ArchBarrierBayShape bay, ArchBarrierSpec spec, ArchKit kit, ArchBarrierBrushes brushes )
	{
		var strands = Math.Clamp( spec.Strands, 1, 12 );
		var half = ArchBarrierShape.WireHalf( kit );
		var band = ArchDivide.Into( MathF.Max( 1f, spec.Height * HeadBand ), strands );

		for ( var strand = 0; strand < strands; strand++ )
		{
			if ( bay.Missing && ArchBarrierShape.Noise( spec.Seed, bay.Index * 31 + strand, 97 ) < spec.Broken )
			{
				continue;
			}

			var hang = spec.Height - band.At( strand );
			var path = bay.Strand( bay.SeatFrom + hang, bay.SeatTo + hang );

			for ( var chord = 0; chord < path.Count - 1; chord++ )
			{
				canvas.Strut( path[chord], path[chord + 1], half, brushes.Wire );
			}
		}
	}
}