Utility struct that remaps 2D/3D geometry from one rectangular footprint to another. It computes a per-axis scale and translation and provides methods to transform points, boxes, loops (List<Vector2>), paths (List<Vector3>) and ArchCurveNode positions.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// One answer to "the shape moved - carry everything standing on it". A move, a corner drag and an
// edge drag all reduce to the same thing: the old bounds became the new ones. Walkways, building
// blueprints and groups all reshape through this arithmetic rather than three copies of it.
public readonly struct ArchRemap
{
readonly Vector2 from;
readonly Vector2 scale;
readonly Vector2 to;
ArchRemap( Vector2 from, Vector2 scale, Vector2 to )
{
this.from = from;
this.scale = scale;
this.to = to;
}
public static ArchRemap Translation( Vector2 shift ) => new( Vector2.Zero, Vector2.One, shift );
// A degenerate axis cannot be scaled, so it translates - a flat footprint still follows its drag.
public static ArchRemap Between( Vector2 fromMin, Vector2 fromMax, Vector2 toMin, Vector2 toMax )
{
var span = fromMax - fromMin;
var wanted = toMax - toMin;
return new ArchRemap( fromMin, new Vector2( Axis( span.x, wanted.x ), Axis( span.y, wanted.y ) ), toMin );
}
static float Axis( float span, float wanted ) => MathF.Abs( span ) < 0.01f ? 1f : wanted / span;
public Vector2 Of( Vector2 point ) => to + (point - from) * scale;
public Vector3 Of( Vector3 point )
{
var flat = Of( new Vector2( point.x, point.y ) );
return new Vector3( flat.x, flat.y, point.z );
}
// Min/Max pairs must stay ordered: a negative scale would otherwise hand back an inside-out box.
public void Box( ref Vector2 min, ref Vector2 max )
{
var first = Of( min );
var second = Of( max );
min = Vector2.Min( first, second );
max = Vector2.Max( first, second );
}
public void Loop( List<Vector2> loop )
{
for ( var index = 0; index < loop.Count; index++ )
{
loop[index] = Of( loop[index] );
}
}
public void Path( List<Vector3> path )
{
for ( var index = 0; index < path.Count; index++ )
{
path[index] = Of( path[index] );
}
}
public void Nodes( List<ArchCurveNode> nodes )
{
foreach ( var node in nodes )
{
node.Position = Of( node.Position );
}
}
}