Editor/Stair/ArchStairWallRakes.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sunless.Architecture;
public sealed class ArchStairWallRakes : IArchWallRakes {
public IReadOnlyList<ArchWallRake> Along(
ArchWall wall,
ArchRoom room,
ArchBuilding building,
ArchPlan plan,
ArchKit kit,
float half,
float wallHeight ) {
var rakes = new List<ArchWallRake>();
if ( building is null ) {
return rakes;
}
var kinds = ArchKinds.Load();
var board = MathF.Max( 1f, kit.BaseboardHeight );
foreach ( var candidate in building.Rooms ) {
foreach ( var stair in plan.Filed( ArchKind.Stair, candidate, kinds ).OfType<ArchStairPart>() ) {
var shape = ArchStairShape.Resolve( stair, candidate, kit, building.Rooms );
Landings( rakes, wall, shape, room, kit, half, wallHeight );
foreach ( var run in shape.Runs ) {
if ( !Against( wall, run.Axes, run.Length, run.Width, kit, half, out var foot, out var sign ) ) {
continue;
}
var slope = MathF.Max( 0.01f, run.Rise / MathF.Max( 1f, run.Going ) );
var low = run.BaseHeight - room.BaseHeight;
var start = MathF.Max( -run.Going, (-low - run.Rise - board) / slope );
var finish = MathF.Min( run.Length - run.Going, (wallHeight - low) / slope );
if ( finish - start < 1f ) {
continue;
}
var lowLift = low + start * slope;
var highLift = low + finish * slope;
rakes.Add( sign > 0f
? new ArchWallRake( foot + start, foot + finish, lowLift, highLift, run.Rise, wallHeight )
: new ArchWallRake( foot - finish, foot - start, highLift, lowLift, run.Rise, wallHeight ) );
}
}
}
return rakes;
}
static void Landings( List<ArchWallRake> rakes, ArchWall wall, ArchStairShape shape, ArchRoom room, ArchKit kit, float half, float wallHeight ) {
foreach ( var pad in shape.Pads ) {
var lift = pad.Height - room.BaseHeight;
if ( lift < 1f || lift > wallHeight ) {
continue;
}
var length = pad.AlongTo - pad.AlongFrom;
var width = pad.AcrossTo - pad.AcrossFrom;
var lengthways = new ArchStairAxes { Origin = pad.Axes.Flat( pad.AlongFrom, pad.AcrossFrom ), Yaw = pad.Axes.Yaw };
var crossways = new ArchStairAxes { Origin = pad.Axes.Flat( pad.AlongTo, pad.AcrossFrom ), Yaw = pad.Axes.Yaw + 90f };
if ( Against( wall, lengthways, length, width, kit, half, out var foot, out var sign ) ) {
rakes.Add( Level( foot, length, lift, sign, wallHeight ) );
continue;
}
if ( Against( wall, crossways, width, length, kit, half, out foot, out sign ) ) {
rakes.Add( Level( foot, width, lift, sign, wallHeight ) );
}
}
}
static ArchWallRake Level( float foot, float length, float lift, float sign, float wallHeight ) {
return sign > 0f
? new ArchWallRake( foot, foot + length, lift, lift, 0f, wallHeight )
: new ArchWallRake( foot - length, foot, lift, lift, 0f, wallHeight );
}
static bool Against( ArchWall wall, ArchStairAxes axes, float length, float width, ArchKit kit, float half, out float foot, out float sign ) {
foot = 0f;
sign = 1f;
var lean = Vector2.Dot( axes.Along, wall.Direction );
if ( MathF.Abs( lean ) < 0.98f ) {
return false;
}
foreach ( var edge in new[] { 0f, width } ) {
var middle = axes.Flat( length * 0.5f, edge );
var offset = Vector2.Dot( middle - wall.Start, wall.Normal );
if ( offset < -half - 1f || offset > half + ArchProbe.Gap( kit ) ) {
continue;
}
foot = Vector2.Dot( axes.Flat( 0f, edge ) - wall.Start, wall.Direction );
sign = lean > 0f ? 1f : -1f;
return true;
}
return false;
}
}