Editor/Data/ArchPlot.cs

A readonly struct that represents a rectangular plot in architecture editor coordinates and converts plot-relative rects and sides into world-space coordinates, taking optional X/Y flipping into account. It computes size, resolves an ArchPlotRect to concrete min/max Vector2 positions, and maps ArchSide values when flipped.

using System;
using Sandbox;

namespace Sunless.Architecture;

// The one place a plot-relative rect turns into world inches - mirror is one flag here.
public readonly struct ArchPlot
{
	public Vector2 Min { get; init; }
	public Vector2 Max { get; init; }
	public bool FlipX { get; init; }
	public bool FlipY { get; init; }

	public Vector2 Size => Max - Min;

	public void Resolve( ArchPlotRect rect, out Vector2 min, out Vector2 max )
	{
		var lo = new Vector2( X( rect.Min.x, rect.MinInset.x ), Y( rect.Min.y, rect.MinInset.y ) );
		var hi = new Vector2( X( rect.Max.x, rect.MaxInset.x ), Y( rect.Max.y, rect.MaxInset.y ) );

		min = new Vector2( MathF.Min( lo.x, hi.x ), MathF.Min( lo.y, hi.y ) );
		max = new Vector2( MathF.Max( lo.x, hi.x ), MathF.Max( lo.y, hi.y ) );
	}

	// Sides mirror too, or a door on the entrance ends up round the back.
	public ArchSide Resolve( ArchSide side ) => side switch
	{
		ArchSide.MinX => FlipX ? ArchSide.MaxX : ArchSide.MinX,
		ArchSide.MaxX => FlipX ? ArchSide.MinX : ArchSide.MaxX,
		ArchSide.MinY => FlipY ? ArchSide.MaxY : ArchSide.MinY,
		_ => FlipY ? ArchSide.MinY : ArchSide.MaxY
	};

	float X( float fraction, float inset )
	{
		var along = FlipX ? 1f - fraction : fraction;
		var reach = FlipX ? -inset : inset;

		return Min.x + Size.x * along + reach;
	}

	float Y( float fraction, float inset )
	{
		var along = FlipY ? 1f - fraction : fraction;
		var reach = FlipY ? -inset : inset;

		return Min.y + Size.y * along + reach;
	}
}