Editor/Tool/ArchToolElevation.cs

Editor tool code that draws an elevation/section view for architecture editing. It computes a section plane for the edited building and draws lines for panes, profiles, sweeps and a section guide, tinting pieces by role and fading non-current floors.

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

namespace Sunless.Architecture;

// Every storey is drawn, not just the live one, because that IS the section; in plan the opposite rule holds because floors lie on top of each other.
public partial class ArchTool
{
	static readonly Color Faded = new( 1f, 1f, 1f, 0.22f );

	void DrawSection()
	{
		if ( EditedBuilding() is not { } building )
		{
			return;
		}

		var cut = Section( building );
		var plane = cut.Plane;

		DrawSectionLine( cut );

		foreach ( var piece in cut.Pieces )
		{
			Gizmo.Draw.Color = Tint( piece );
			Gizmo.Draw.LineThickness = piece.Floor == Level ? 2f : 1f;

			if ( piece.Rakes )
			{
				DrawProfile( plane, piece );

				continue;
			}

			DrawPane( plane, piece );
		}

		Gizmo.Draw.LineThickness = 2f;
	}

	// The plane's trace, so the scrubbed depth is visible while it moves.
	void DrawSectionLine( ArchElevationCut cut )
	{
		var bounds = Extent( cut.Building );

		if ( bounds.Size.Length < 1f )
		{
			return;
		}

		var plane = cut.Plane;
		var from = plane.Axis == ArchViewAxis.Front ? bounds.Mins.y : bounds.Mins.x;
		var to = plane.Axis == ArchViewAxis.Front ? bounds.Maxs.y : bounds.Maxs.x;

		Gizmo.Draw.LineThickness = 1f;
		Gizmo.Draw.Color = ArchGhost.Line.WithAlpha( 0.25f );

		Gizmo.Draw.Line( plane.At( from, bounds.Mins.z ), plane.At( to, bounds.Mins.z ) );
	}

	static void DrawPane( ArchElevationPlane plane, ArchElevationPiece piece )
	{
		var a = plane.At( piece.From, piece.Foot );
		var b = plane.At( piece.To, piece.Foot );
		var c = plane.At( piece.To, piece.Head );
		var d = plane.At( piece.From, piece.Head );

		Gizmo.Draw.Line( a, b );
		Gizmo.Draw.Line( b, c );
		Gizmo.Draw.Line( c, d );
		Gizmo.Draw.Line( d, a );

		// The blueprint cross that says hole, not block.
		if ( piece.Role is not (ArchElevationRole.Opening or ArchElevationRole.Cut) )
		{
			return;
		}

		Gizmo.Draw.Line( a, c );
		Gizmo.Draw.Line( b, d );
	}

	// Both traces as authored, so a pitch reads as a pitch and a wedge as a wedge - a constant thickness carried
	// under the top is a deck, and drawing a ramp that way put a sloping slab where a solid triangle stands.
	static void DrawProfile( ArchElevationPlane plane, ArchElevationPiece piece )
	{
		Sweep( plane, piece.Profile );
		Sweep( plane, piece.Soffit );

		foreach ( var end in new[] { 0, piece.Profile.Count - 1 } )
		{
			Gizmo.Draw.Line(
				plane.At( piece.Soffit[end].x, piece.Soffit[end].y ),
				plane.At( piece.Profile[end].x, piece.Profile[end].y ) );
		}
	}

	static void Sweep( ArchElevationPlane plane, IReadOnlyList<Vector2> samples )
	{
		for ( var index = 0; index < samples.Count - 1; index++ )
		{
			Gizmo.Draw.Line(
				plane.At( samples[index].x, samples[index].y ),
				plane.At( samples[index + 1].x, samples[index + 1].y ) );
		}
	}

	Color Tint( ArchElevationPiece piece )
	{
		if ( Picked?.Targets( piece.Item ) == true )
		{
			return Color.Yellow;
		}

		if ( piece.Floor != Level )
		{
			return Faded;
		}

		return piece.Role switch
		{
			ArchElevationRole.Wall => Color.Cyan,
			ArchElevationRole.Opening => Color.Green,
			ArchElevationRole.Floor => ArchGhost.Line.WithAlpha( 0.7f ),
			ArchElevationRole.Platform => Color.Magenta.WithAlpha( 0.7f ),
			ArchElevationRole.Cut => Color.Red.WithAlpha( 0.8f ),
			ArchElevationRole.Roof => Color.Orange.WithAlpha( 0.8f ),
			_ => Color.White
		};
	}
}