Editor/Designers/ArchPillarDesigner.cs
using System;
using System.Collections.Generic;
using Editor;
using Sandbox;
namespace Sunless.Architecture;
// Save writes back to .pillartype.json, so what's tuned here becomes the shared kind.
public sealed class ArchPillarDesigner : ArchDesignerDialog {
const int PreviewColumnsX = ArchPillarStage.ColumnsX;
const int PreviewColumnsY = ArchPillarStage.ColumnsY;
readonly ArchTool tool;
readonly Action<ArchPillarPart> applied;
readonly ArchPillarPart draft;
string name;
string title;
float height;
public ArchPillarDesigner( Widget parent, ArchTool tool, ArchPillarPart initial, ArchPillarType type, Action<ArchPillarPart> applied )
: base( parent, "Pillar Designer", "view_column" ) {
this.tool = tool;
this.applied = applied;
draft = (initial ?? new ArchPillarPart()).Section();
name = type?.Name ?? draft.Type;
title = type?.Title ?? "Pillar";
height = tool.Kit.WallHeight;
Assemble(
tool.Kit,
"Live generated colonnade",
$"Drag the preview to orbit. A {PreviewColumnsX} x {PreviewColumnsY} bay of the kind you are designing, through the same shaft, footing and span generators the plan builds with.",
$"Pillar types: Assets/{ArchPillarCatalog.Directory}" );
}
protected override string SaveLabel => "Save Type";
protected override void Populate() {
var form = Form.Layout;
form.Add( Heading( "Identity" ) );
form.Add( Text( "Name", name, value => name = value ) );
form.Add( Text( "Title", title, value => title = value ) );
form.Add( Heading( "Preview" ) );
form.Add( Number( "Storey height", height, value => height = MathF.Max( 24f, value ) ) );
form.Add( Number( "Bay x", draft.Spacing.x, value => draft.Spacing = draft.Spacing.WithX( MathF.Max( 24f, value ) ) ) );
form.Add( Number( "Bay y", draft.Spacing.y, value => draft.Spacing = draft.Spacing.WithY( MathF.Max( 24f, value ) ) ) );
var options = new ToolSidebarWidget();
ArchPillarUi.Build( options, draft, tool.FindPillarType( name ), Repopulate, Stage.Restage );
form.Add( options );
}
protected override ArchStaged Staged() => ArchPillarStage.Of( draft, height );
protected override string Diagnose() {
var column = Staged().Plan.Buildings[0].Rooms[0].Pillars[0];
var spanning = column.Span != PillarSpan.None;
var shaft = spanning ? MathF.Max( 8f, height - column.SpanBand ) : height;
var thinnest = MathF.Min( column.Width, column.Depth );
return string.Join( "\n",
$"type: {name} ({title}), placement {column.Placement}",
$"storey height: {height:0.##}",
$"section: {column.Width:0.##} x {column.Depth:0.##}, reach {column.Reach:0.##}, seat {column.Seat:0.##}",
$"slenderness: {(thinnest > 0.01f ? height / thinnest : 0f):0.##} to 1 over the storey, "
+ $"{(thinnest > 0.01f ? shaft / thinnest : 0f):0.##} to 1 over the shaft",
$"shaft: {shaft:0.##} of {height:0.##}, span band {(spanning ? column.SpanBand : 0f):0.##}",
$"footing: {column.Footing} spread {column.FootingSpread:0.##} deep {column.FootingHeight:0.##} buried {column.FootingBuried:0.##}",
$"plinth: {column.Plinth} {column.PlinthHeight:0.##} over {column.PlinthOversize:0.##}",
$"capital: {column.Capital} {column.CapitalHeight:0.##} over {column.CapitalOversize:0.##}",
$"span: {column.Span} x{column.SpanAlongX} y{column.SpanAlongY} perimeter {column.PerimeterOnly} "
+ $"beam {column.SpanDepth:0.##}x{column.SpanHeight:0.##} arch rise {column.ArchRise:0.##} ring {column.ArchRing:0.##}",
$"span thickness: {column.SpanThickness( true ):0.##} along x, {column.SpanThickness( false ):0.##} along y "
+ $"(authored {column.SpanDepth:0.##}, pier {column.Depth:0.##} x {column.Width:0.##})",
$"bays: {PreviewColumnsX} x {PreviewColumnsY} at {column.Spacing.x:0.##} x {column.Spacing.y:0.##}, "
+ $"clear span {column.Spacing.x - column.Width:0.##} x {column.Spacing.y - column.Depth:0.##}",
$"runs: {ArchAsks.PillarRunCount( column )} over {ArchAsks.PillarCarriedCount( column )} of {column.TotalCount} piers",
$"tops: {(column.Capital ? "capital" : spanning ? column.Span.ToString().ToLowerInvariant() : "bare shaft")}" );
}
protected override void Apply() {
applied?.Invoke( Snapshot() );
Close();
}
// The type is what a colonnade stands from, so the whole column goes into it.
protected override void SaveTemplate() {
var saved = Snapshot();
if ( string.IsNullOrWhiteSpace( saved.Type ) ) {
Log.Warning( "Architecture: the pillar type needs a name before it can be saved." );
return;
}
var type = new ArchPillarType {
Name = saved.Type,
Title = string.IsNullOrWhiteSpace( title ) ? saved.Type : title,
Description = tool.FindPillarType( saved.Type )?.Description ?? "",
Icon = tool.FindPillarType( saved.Type )?.Icon ?? "view_column",
Column = saved
};
if ( !ArchAsks.SavePillarType( type ) ) {
Log.Warning( $"Architecture: could not write the pillar type '{type.Name}'." );
return;
}
tool.ReloadPillarTypes();
applied?.Invoke( Snapshot() );
Close();
}
ArchPillarPart Snapshot() {
var result = draft.Section();
result.Type = string.IsNullOrWhiteSpace( name ) ? draft.Type : name.Trim();
return result;
}
}