editor/AdvancedMappingTools/Geometry/StreetLightingEditor.cs

Editor tool that generates street lighting geometry along a path. Places poles at a set spacing, adds arms and lamp head boxes on one or both sides, and builds the mesh through MeshKit.

namespace AdvancedMappingTools;

/// <summary>
/// A run of street lights along a path: pole, arm and lamp head on one or both sides.
/// </summary>
[ToolCategory( "Street" )]
[Title( "Street Lighting Run" ), Icon( "light" )]
public sealed class StreetLightingEditor( PrimitiveTool tool ) : PathEditor( tool )
{
	protected override int MinPoints => 2;

	[Property, Group( "Lights" ), Title( "Spacing" ), Range( 16, 2048, slider: false ), WideMode]
	public float Spacing { get; set; } = 256.0f;

	[Property, Group( "Lights" ), Range( 16, 1024, slider: false ), WideMode]
	public float Height { get; set; } = 192.0f;

	[Property, Group( "Lights" ), Title( "Pole Radius" ), Range( 1, 24, slider: false ), WideMode]
	public float PoleRadius { get; set; } = 4.0f;

	[Property, Group( "Lights" ), Title( "Arm" ), Range( 0, 256, slider: false ), WideMode]
	public float Arm { get; set; } = 48.0f;

	[Property, Group( "Lights" ), Title( "Both Sides" ), WideMode]
	public bool BothSides { get; set; } = false;

	[Property, Group( "Lights" ), Title( "Lamp Size" ), Range( 4, 64, slider: false ), WideMode]
	public float LampSize { get; set; } = 12.0f;

	protected override void BuildMesh( MeshKit kit )
	{
		var path = SamplePath();

		if ( path.Count < 2 ) return;

		var height = MathF.Max( Height, 16.0f );
		var radius = MathF.Max( PoleRadius, 1.0f );
		var arm = MathF.Max( Arm, 0.0f );
		var lamp = MathF.Max( LampSize, 4.0f );

		foreach ( var (position, direction) in WalkPath( path, Closed, MathF.Max( Spacing, 16.0f ) ) )
		{
			kit.Material = SurfaceMaterial;
			kit.Cylinder( position, radius, radius, 0.0f, height, 8 );

			var normal = new Vector3( -direction.y, direction.x, 0.0f ).Normal;
			var sides = BothSides ? new[] { 1.0f, -1.0f } : new[] { 1.0f };

			kit.Material = TrimMaterial;

			foreach ( var side in sides )
			{
				var tip = position + normal * arm * side + Vector3.Up * height;

				if ( arm > 0.0f )
					kit.Beam( position + Vector3.Up * height, tip, 3.0f, 3.0f );

				kit.Box( tip + new Vector3( -lamp * 0.5f, -lamp * 0.5f, -lamp * 0.5f ), tip + new Vector3( lamp * 0.5f, lamp * 0.5f, lamp * 0.2f ) );
			}
		}
	}
}