Editor/Road/ArchBridgeShape.cs

Editor-side shape description for arch bridges. Defines value types for bridge stands and bents and a container ArchBridgeShape that holds parts, frames, bents, wings and helper properties for usability, carrying range and deepest pier rise.

Reflection
using System;
using System.Collections.Generic;
using Sandbox;

namespace Sunless.Architecture;

// One line of shafts under a bent - a LIST, one per gap the deck crosses.
public readonly struct ArchBridgeStand
{
	public float From { get; init; }
	public float To { get; init; }
	public float Width { get; init; }
	// The lowest ground under the shafts, not the centreline - a cross-slope blade reaches its own low end.
	public float Ground { get; init; }
	public List<float> Shafts { get; init; }
}

// Read at the bent's own station: a valley's middle piers are long, the ends short.
public readonly struct ArchBridgeBent
{
	public ArchFrame Frame { get; init; }
	public float Bearing { get; init; }
	public float Ground { get; init; }
	public List<ArchBridgeStand> Stands { get; init; }

	public Vector2 Flat => Frame.Flat;

	public float Rise => Bearing - Ground;
}

// One resolve feeds the generator, the overlay and arch_measure - what is drawn is what is built.
public sealed class ArchBridgeShape
{
	public ArchBridgePart Part { get; init; }
	public ArchRoadPart Road { get; init; }
	public ArchRoadSpan Span { get; init; }
	public List<ArchFrame> Frames { get; init; } = new();
	public List<ArchBridgeBent> Bents { get; init; } = new();
	public List<ArchWingWall> Wings { get; init; } = new();
	public ArchBridgeBent Near { get; init; }
	public ArchBridgeBent Far { get; init; }

	public bool IsUsable => Frames.Count >= 2;

	// The STRUCTURE, not the drag, carries this station - the span moved to the lip of what it crosses.
	public bool Carries( float distance ) => distance > Span.From && distance < Span.To;

	// The tallest pier - what says whether the bridge crosses anything at all.
	public float Deepest
	{
		get
		{
			var rise = MathF.Max( Near.Rise, Far.Rise );

			foreach ( var bent in Bents )
			{
				rise = MathF.Max( rise, bent.Rise );
			}

			return rise;
		}
	}
}