Editor/Stair/ArchStairEdges.cs
namespace Sunless.Architecture;
// Maps a step's four edges to along/across distances in the step's own frame.
public static class ArchStairEdges {
public static void Head( ArchStairLane lane, Vector2 local ) {
Lengthen( lane, Along( lane, local ) );
}
// Left flank = width change; right flank = origin shift.
public static void Flank( ArchStairLane lane, Vector2 local, bool left ) {
var across = Across( lane, local );
if ( left ) {
Widen( lane, across );
return;
}
var width = lane.Width - across;
Shift( lane, lane.Leftward * across );
Widen( lane, width );
}
// The head pushed OUT by a distance rather than moved to a point, which is how a platform's far edge is grown.
public static void Grow( ArchStairLane lane, float push ) {
Lengthen( lane, lane.Length + push );
}
// Set outright, so a run or a depth typed on a sheet lands on the same number a dragged edge writes. The seat
// never moves: a step grows out of the corner it was laid from, which is the corner the step below it meets.
public static void Lengthen( ArchStairLane lane, float length ) {
var reach = MathF.Max( ArchStairLanes.MinLane, length );
switch ( lane.Walk ) {
case StairWalk.Ahead:
lane.AlongTo = lane.AlongFrom + reach;
break;
case StairWalk.Back:
lane.AlongFrom = lane.AlongTo - reach;
break;
case StairWalk.Left:
lane.AcrossTo = lane.AcrossFrom + reach;
break;
default:
lane.AcrossFrom = lane.AcrossTo - reach;
break;
}
}
public static void Widen( ArchStairLane lane, float width ) {
var reach = MathF.Max( ArchStairLanes.MinLane, width );
switch ( lane.Walk ) {
case StairWalk.Ahead:
lane.AcrossTo = lane.AcrossFrom + reach;
break;
case StairWalk.Back:
lane.AcrossFrom = lane.AcrossTo - reach;
break;
case StairWalk.Left:
lane.AlongFrom = lane.AlongTo - reach;
break;
default:
lane.AlongTo = lane.AlongFrom + reach;
break;
}
}
public static void Shift( ArchStairLane lane, Vector2 delta ) {
lane.AlongFrom += delta.x;
lane.AlongTo += delta.x;
lane.AcrossFrom += delta.y;
lane.AcrossTo += delta.y;
}
// A point in the core's numbers read as how far along the step's own travel it lies, and how far across it.
public static float Along( ArchStairLane lane, Vector2 local ) => Vector2.Dot( local - lane.Seat, lane.Heading );
public static float Across( ArchStairLane lane, Vector2 local ) => Vector2.Dot( local - lane.Seat, lane.Leftward );
// Where each step STARTS in the climb, accumulated the way the resolve accumulates it - so the window's
// elevation and the generator cannot disagree about how high a step stands.
public static float[] Bases( ArchStairPart stair ) {
var lanes = stair.Lanes;
var climbs = ArchStairLanes.Climbs( stair.Core, lanes );
var bases = new float[lanes.Count];
var height = stair.BaseHeight;
for ( var index = 0; index < lanes.Count; index++ ) {
if ( !lanes[index].Climbs ) {
height += climbs[index];
bases[index] = height;
continue;
}
bases[index] = height;
height += climbs[index];
}
return bases;
}
}