Editor/Bands/ArchCorniceStyle.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sunless.Architecture;
// What one course of a cornice IS. Append only: a kind inserted mid-list renumbers every authored style
// after it.
public enum CorniceCourse {
// A swept kit section standing proud of the exterior face.
Moulding,
// A plain projecting box - a corona, a frieze.
Band,
// A row of small blocks, counted off the run.
Dentils,
// The same row, each block cut from a section instead of squared.
Modillions,
// A cap straddling the wall, oversailing BOTH faces - two of them stepped is a coping.
Coping
}
// One course, stated bottom up. Rise is the band it takes out of the head it hangs from; Projection is how far
// proud of the face it stands, or how far a coping oversails each face.
public sealed class ArchCorniceCourse {
public CorniceCourse Kind { get; set; } = CorniceCourse.Band;
// Authored with +x standing OUT of the wall and +y up, whichever host sweeps it.
public string Profile { get; set; } = "";
public float Rise { get; set; } = 4f;
public float Projection { get; set; } = 3f;
// A block row's pitch, never its count - the count comes off the run. 0 takes the kit's own dentil stock.
public float Block { get; set; }
public float Gap { get; set; }
public ArchSurface Field { get; set; } = ArchSurface.Trim;
public bool Stands => Rise > 0.05f;
public bool Straddles => Kind == CorniceCourse.Coping;
public ArchCorniceCourse Copy() {
return new ArchCorniceCourse {
Kind = Kind,
Profile = Profile,
Rise = Rise,
Projection = Projection,
Block = Block,
Gap = Gap,
Field = Field
};
}
}
// The blocks that step ABOVE the cornice head - the piers a main-street parapet breaks its line with. They ride
// the wall's own column line where it has one, so a pilastered elevation needs no second rhythm authored here.
public sealed class ArchCornicePier {
public float Width { get; set; } = 16f;
public float Rise { get; set; }
public float Projection { get; set; } = 2f;
public float Cap { get; set; } = 3f;
public float CapOversail { get; set; } = 1.5f;
public ArchSurface Field { get; set; } = ArchSurface.WallExterior;
public bool Stands => Rise > 0.5f && Width > 0.5f;
public ArchCornicePier Copy() {
return new ArchCornicePier {
Width = Width,
Rise = Rise,
Projection = Projection,
Cap = Cap,
CapOversail = CapOversail,
Field = Field
};
}
}
// The corbel that carries the TURN. A block row is held off every mitre, so a corner has no dentil on it; this is
// what stands there instead - one per vertex of the ring, deeper than the blocks, hung under the corona.
public sealed class ArchCorniceBracket {
public float Width { get; set; }
public float Drop { get; set; }
public float Projection { get; set; }
public ArchSurface Field { get; set; } = ArchSurface.Trim;
public bool Stands => Width > 0.5f && Drop > 0.5f;
public ArchCorniceBracket Copy() {
return new ArchCorniceBracket {
Width = Width,
Drop = Drop,
Projection = Projection,
Field = Field
};
}
}
// A cornice is a STACK, never one moulding, and the stack is what an author picks. Every dimension that would
// otherwise land on the wall sheet lives here instead: one name on the elevation, the whole entablature in the kit.
public sealed class ArchCorniceStyle {
public string Name { get; set; } = "cornice";
public string Detail { get; set; } = "";
public List<ArchCorniceCourse> Courses { get; set; } = new();
public ArchCornicePier Pier { get; set; } = new();
public ArchCorniceBracket Bracket { get; set; } = new();
public bool Stands => Courses.Any( course => course.Stands );
public float Rise => Courses.Where( course => course.Stands ).Sum( course => course.Rise );
public float Projection => Courses.Count == 0 ? 0f : Courses.Max( course => MathF.Max( 0f, course.Projection ) );
public ArchCorniceStyle Copy() {
return new ArchCorniceStyle {
Name = Name,
Detail = Detail,
Courses = Courses.Select( course => course.Copy() ).ToList(),
Pier = Pier.Copy(),
Bracket = Bracket.Copy()
};
}
}