Editor/Designers/ArchCorniceDesigner.cs
using System;
using System.Linq;
using Editor;
using Sandbox;
namespace Sunless.Architecture;
// Save writes back to .cornice.json, so what's tuned here becomes the shared style every elevation naming it wears.
public sealed class ArchCorniceDesigner : ArchDesignerDialog {
readonly ArchKit kit;
readonly Action<ArchCorniceStyle> applied;
readonly ArchCorniceStyle draft;
public ArchCorniceDesigner( Widget parent, ArchKit kit, ArchCorniceStyle initial, Action<ArchCorniceStyle> applied )
: base( parent, "Cornice Designer", "horizontal_split" ) {
this.kit = kit;
this.applied = applied;
draft = (initial ?? new ArchCorniceStyle()).Copy();
Assemble(
kit,
"Live generated block",
"Drag the preview to orbit. A crowned parapet through the same courses the plan builds with — a cornice is "
+ "judged at a corner, where the mitre closes and a pier breaks the line.",
$"Cornice styles: Assets/{ArchCorniceCatalog.Directory}" );
}
protected override string SaveLabel => "Save Style";
protected override void Populate() {
var form = Form.Layout;
form.Add( Heading( "Identity" ) );
form.Add( Text( "Name", draft.Name, value => draft.Name = value ) );
form.Add( Text( "Detail", draft.Detail, value => draft.Detail = value ) );
var options = new ToolSidebarWidget();
ArchCorniceUi.Build( options, kit, draft, Repopulate, Stage.Restage );
form.Add( options );
}
protected override ArchStaged Staged() => ArchCorniceStage.Of( draft, kit );
protected override string Diagnose() {
var head = kit.WallHeight;
var layers = ArchCorniceGen.Layers( draft, head );
return string.Join( "\n",
$"style: {draft.Name} ({draft.Detail})",
$"rise: {draft.Rise:0.##} hung from {head:0.##}, widest projection {draft.Projection:0.##}",
$"courses: {layers.Count} of {draft.Courses.Count} standing",
string.Join( "\n", layers.Select( layer =>
$" {layer.Course.Kind} {layer.Bottom:0.##} to {layer.Top:0.##}, "
+ $"{(layer.Course.Straddles ? "oversails" : "proud")} {layer.Projection:0.##}"
+ $"{(string.IsNullOrWhiteSpace( layer.Course.Profile ) ? "" : $", section {layer.Course.Profile}")}"
+ $"{(layer.Course.Kind is CorniceCourse.Dentils or CorniceCourse.Modillions ? $", pitch {Pitch( layer.Course ):0.##}" : "")}"
+ $", {layer.Course.Field}" ) ),
draft.Pier.Stands
? $"pier: {draft.Pier.Width:0.##} wide rising {draft.Pier.Rise:0.##} proud {draft.Pier.Projection:0.##}, "
+ $"cap {draft.Pier.Cap:0.##} over {draft.Pier.CapOversail:0.##}"
: "pier: none" );
}
float Pitch( ArchCorniceCourse course ) {
return (course.Block > 0.05f ? course.Block : kit.DentilWidth) + (course.Gap > 0.05f ? course.Gap : kit.DentilGap);
}
protected override void Apply() {
applied?.Invoke( draft.Copy() );
Close();
}
protected override void SaveTemplate() {
if ( string.IsNullOrWhiteSpace( draft.Name ) || ArchCorniceStage.Hidden( draft ) ) {
Log.Warning( "Architecture: the cornice style needs a name of its own before it can be saved." );
return;
}
var saved = draft.Copy();
if ( !ArchShelved.Save( ArchShelf.Cornices, saved ) ) {
Log.Warning( $"Architecture: could not write the cornice style '{saved.Name}'." );
return;
}
// Onto the shelf under its own name, or the style saves to disk and the picker keeps offering the old stack
// until the kit is reloaded.
kit.Cornices.RemoveAll( style => string.Equals( style.Name, saved.Name, StringComparison.OrdinalIgnoreCase ) );
kit.Cornices.Add( saved );
Log.Info( $"Architecture: saved the cornice style '{saved.Name}'." );
applied?.Invoke( saved );
Close();
}
}