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;
}
}