Editor/Designers/ArchWallDesigner.cs
using System;
using System.Collections.Generic;
using Editor;
using Sandbox;
namespace Sunless.Architecture;
// Treatments don't read on buttons - judged against a real elevation with a door and window.
public sealed class ArchWallDesigner : ArchDesignerDialog {
const float PreviewSpan = ArchWallStage.Span;
readonly ArchKit kit;
readonly Action<ArchWallPreset> applied;
readonly ArchWallPreset draft;
public ArchWallDesigner( Widget parent, ArchKit kit, ArchWallPreset initial, Action<ArchWallPreset> applied )
: base( parent, "Wall Designer", "horizontal_rule" ) {
this.kit = kit;
this.applied = applied;
draft = ArchWallPreset.Copy( initial );
Assemble(
kit,
"Live generated elevation",
"Drag the preview to orbit. One wall with a door and a window in it, through the same panel, band, siding and trim generators the plan builds with.",
$"Wall treatments: Assets/{ArchWallCatalog.Directory}" );
}
protected override string SaveLabel => "Save Treatment";
protected override void Populate() {
var form = Form.Layout;
form.Add( Heading( "Identity" ) );
form.Add( Text( "Name", draft.Name, value => draft.Name = value ) );
var options = new ToolSidebarWidget();
ArchWallUi.Build( options, kit, draft, Repopulate, Stage.Restage, openings: false );
form.Add( options );
}
protected override ArchStaged Staged() => ArchWallStage.Of( Snapshot(), kit );
protected override string Diagnose() {
var unit = Snapshot();
var height = Storey( unit );
var bands = ArchWallTreatments.Ordered( unit.Bands, height ).ToList();
var cursor = 0f;
var fields = new List<string>();
foreach ( var band in bands ) {
var at = band.At( height );
fields.Add( $"{cursor:0.#} to {at:0.#}{(band.AtHead ? " (head)" : "")} {band.Field}"
+ $"{(band.Course ? $", {band.Seat.ToString().ToLowerInvariant()} course "
+ $"{(band.CourseHeight > 0.01f ? band.CourseHeight : kit.WallBandHeight):0.##} high x "
+ $"{(band.CourseDepth > 0.01f ? band.CourseDepth : kit.WallBandDepth):0.##} proud" : "")}" );
cursor = at;
}
fields.Add( $"{cursor:0.#} to {height:0.#} "
+ (unit.Cladding != WallCladding.None ? "Siding" : unit.Exterior ? "WallExterior" : "WallInterior") );
return string.Join( "\n",
$"treatment: {unit.Name}",
$"wall: {height:0.##} tall ({(unit.Height > 0f ? "authored" : "inherited")}), "
+ $"{(unit.Thickness > 0f ? unit.Thickness : kit.WallThickness):0.##} thick ({(unit.Thickness > 0f ? "authored" : "inherited")})",
$"bands: {bands.Count}",
"fields: " + string.Join( " | ", fields ),
$"cladding: {unit.Cladding} — " + (unit.Cladding switch {
WallCladding.Weatherboard => $"{kit.SidingCourse:0.##} courses standing {kit.SidingProud:0.##} proud",
WallCladding.Profiled => $"{kit.SheetPitch:0.##} pitch, {kit.SheetCrest:0.##} crest, {kit.SheetProud:0.##} proud",
_ => "flat"
}),
$"pilasters: " + (unit.Pilasters.Stands
? $"{unit.Pilasters.Bay:0.##} bay at most over {PreviewSpan:0.##} "
+ $"= {ArchDivide.AtMost( PreviewSpan, unit.Pilasters.Bay ).Count + 1} strips, "
+ $"{unit.Pilasters.Width:0.##} wide standing {unit.Pilasters.Depth:0.##} proud"
: "none"),
$"wainscot: {unit.Wainscot} to {kit.WainscotHeight:0.##} with a {kit.ChairRailHeight:0.##} rail",
$"trims: skirting {unit.Baseboard}, cap {unit.Cap} at {ArchWallSection.CapHeight( unit, kit ):0.##} "
+ $"oversailing {ArchWallSection.CapOverhang( unit, kit ):0.##}",
$"faces: exterior {unit.Exterior}" );
}
float Storey( ArchWallPreset unit ) => unit.Height > 1f ? unit.Height : kit.WallHeight;
protected override void Apply() {
applied?.Invoke( Snapshot() );
Close();
}
// Folded into the held kit, so the grid offers it next rebuild without a reload.
protected override void SaveTemplate() {
var saved = Snapshot();
if ( !new ArchWallCatalog().Save( saved ) ) {
Log.Warning( "Architecture: the wall treatment needs a name before it can be saved." );
return;
}
var index = kit.Walls.FindIndex( preset => string.Equals( preset.Name, saved.Name, StringComparison.OrdinalIgnoreCase ) );
if ( index >= 0 ) {
kit.Walls[index] = saved;
} else {
kit.Walls.Add( saved );
}
applied?.Invoke( ArchWallPreset.Copy( saved ) );
Close();
}
ArchWallPreset Snapshot() {
var result = ArchWallPreset.Copy( draft );
result.Name = string.IsNullOrWhiteSpace( draft.Name ) ? "wall" : draft.Name.Trim();
result.Height = ArchGridService.Least( result.Height, 0f );
result.Thickness = ArchGridService.Least( result.Thickness, 0f );
// Held under the head, not dropped: the typed height is usually one keystroke short.
foreach ( var band in result.Bands.Where( band => !band.AtHead ) ) {
band.Height = Math.Clamp( ArchGridService.Fine( band.Height ), 0f, Storey( result ) );
}
return result;
}
}