Editor/Cabinet/ArchCabinetType.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sunless.Architecture;
public enum CabinetTier {
Auto,
Base,
Hanging,
Tall
}
public enum CabinetCorner {
Diagonal,
Blind,
None
}
public enum CabinetFront {
None,
Slab,
Shaker,
Panel,
Glass
}
public enum CabinetSlot {
Module,
Corner,
End,
Filler
}
public sealed class ArchCabinetCarcass : IArchAimedPrefab {
public CabinetSlot Slot { get; set; }
public string Prefab { get; set; } = "";
public bool Fits { get; set; }
public Vector3 Anchor { get; set; }
public Vector3 AnchorAlong { get; set; }
public Angles AnchorTurn { get; set; }
public bool Scales => Fits;
public bool Standing => !string.IsNullOrWhiteSpace( Prefab );
public ArchCabinetCarcass Copy() => (ArchCabinetCarcass)MemberwiseClone();
}
public enum CabinetGrip {
Bar,
Knob,
Cup
}
public sealed class ArchCabinetFitting : IArchAimedPrefab {
public string Mount { get; set; } = "";
public CabinetFront Front { get; set; } = CabinetFront.Slab;
public string Prefab { get; set; } = "";
public bool Fits { get; set; }
public bool Scales => Fits;
public Vector3 Anchor { get; set; }
public Vector3 AnchorAlong { get; set; }
public Angles AnchorTurn { get; set; }
public bool Prefabbed => !string.IsNullOrWhiteSpace( Prefab );
public bool Standing => Prefabbed || Front != CabinetFront.None;
}
public sealed class ArchCabinetType : IArchNamed {
public string Name { get; set; } = "";
public string Detail { get; set; } = "";
public CabinetTier Tier { get; set; } = CabinetTier.Base;
public const float NominalWidth = 23.625f;
public const float NominalDepth = 23.625f;
public const float HungDepth = 12.9921f;
// Ceiling, not exact — runs divide so no module exceeds this
public float Width { get; set; } = NominalWidth;
public float Depth { get; set; } = NominalDepth;
public float Height { get; set; } = 28.375f;
public float PlinthHeight { get; set; } = 5.5f;
public float PlinthRecess { get; set; } = 2f;
public float Datum { get; set; }
public float TopThickness { get; set; } = 1.5625f;
public float TopDepth { get; set; }
public float TopOversail { get; set; } = 1f;
public float TopEndOversail { get; set; }
public float TopBackOversail { get; set; }
public CabinetCorner Corner { get; set; } = CabinetCorner.Diagonal;
public float FrontSplit { get; set; }
// Exaggerated for readability at 128 px/m
public float PanelThickness { get; set; } = 1.25f;
public float FrontThickness { get; set; } = 1.25f;
public float Reveal { get; set; } = 0.25f;
public float PanelInset { get; set; } = 0.625f;
public float PanelRaise { get; set; } = 0.375f;
public float PanelMargin { get; set; } = 1.25f;
public float CorniceHeight { get; set; }
public float CorniceProjection { get; set; } = 1f;
public float CorniceOversail { get; set; } = 1f;
public int Shelves { get; set; } = 1;
public int DrawerRows { get; set; }
public float DrawerHeight { get; set; } = 7.0625f;
public bool Handles { get; set; } = true;
public CabinetGrip Grip { get; set; } = CabinetGrip.Bar;
public float HandleLength { get; set; } = 5f;
public float HandleThickness { get; set; } = 1f;
public float HandleStandoff { get; set; } = 1.5f;
public bool Interior { get; set; }
public List<ArchCabinetFitting> Fittings { get; set; } = new();
// Stated, not derived — clearing a prefab must not silently revert to boxes
public bool Prefabbed { get; set; }
public bool Hidden { get; set; }
public List<ArchCabinetCarcass> Carcasses { get; set; } = new();
public string MaterialGroup { get; set; } = "";
public float Carcass => MathF.Max( 4f, Height );
public float Plinth => Tier == CabinetTier.Hanging ? 0f : MathF.Max( 0f, PlinthHeight );
public float Counter => Tier == CabinetTier.Hanging ? 0f : MathF.Max( 0f, TopThickness );
public bool Splits => FrontSplit > 1f && FrontSplit < Carcass - 1f;
public ArchCabinetFitting On( string mount ) {
return Fittings?.FirstOrDefault( fitting => ArchCabinetMounts.Same( fitting.Mount, mount ) );
}
// Falls back to Module slot if the requested slot has no art
public ArchCabinetCarcass In( CabinetSlot slot ) {
var standing = Carcasses?.FirstOrDefault( carcass => carcass.Slot == slot && carcass.Standing );
if ( standing is not null || slot == CabinetSlot.Module ) {
return standing;
}
return Carcasses?.FirstOrDefault( carcass => carcass.Slot == CabinetSlot.Module && carcass.Standing );
}
}
public static class ArchCabinetTypes {
public static List<ArchCabinetType> Shipped() {
return new List<ArchCabinetType>
{
new()
{
Name = "short",
Detail = "Base unit with a drawer over a door, worktop over",
Tier = CabinetTier.Base,
Width = 23.625f,
Depth = 23.625f,
Height = 28.375f,
PlinthHeight = 5.5f,
PlinthRecess = 2f,
TopThickness = 1.5625f,
DrawerRows = 1,
DrawerHeight = 7.0625f,
Shelves = 1,
Fittings = new List<ArchCabinetFitting>
{
new() { Mount = ArchCabinetMounts.Front, Front = CabinetFront.Shaker },
new() { Mount = ArchCabinetMounts.Drawer, Front = CabinetFront.Slab }
}
},
new()
{
Name = "tall",
Detail = "Larder, floor to soffit, front splitting where the wall units start",
Tier = CabinetTier.Tall,
Width = 23.625f,
Depth = 23.625f,
Height = 77.1875f,
PlinthHeight = 5.5f,
PlinthRecess = 2f,
TopThickness = 0f,
FrontSplit = 47.9375f,
Shelves = 5,
Fittings = new List<ArchCabinetFitting>
{
new() { Mount = ArchCabinetMounts.Front, Front = CabinetFront.Shaker },
new() { Mount = ArchCabinetMounts.FrontUpper, Front = CabinetFront.Shaker }
}
},
new()
{
Name = "hanging",
Detail = "Wall unit, seated off the counter below it and rising to what covers it",
Tier = CabinetTier.Hanging,
Width = ArchCabinetType.NominalWidth,
Depth = ArchCabinetType.HungDepth,
Height = 28.375f,
PlinthHeight = 0f,
Datum = 53.4375f,
TopThickness = 0f,
Shelves = 2,
Fittings = new List<ArchCabinetFitting>
{
new() { Mount = ArchCabinetMounts.Front, Front = CabinetFront.Shaker }
}
},
new()
{
Name = "counter",
Detail = "An island - panelled all round under a top with an even lip, flush plinth",
Tier = CabinetTier.Base,
Width = 23.625f,
Depth = 23.625f,
Height = 28.375f,
PlinthHeight = 5.5f,
TopThickness = 1.5625f,
TopOversail = 1f,
TopEndOversail = 1f,
TopBackOversail = 1f,
PlinthRecess = 0f,
Shelves = 1,
Fittings = new List<ArchCabinetFitting>
{
new() { Mount = ArchCabinetMounts.Front, Front = CabinetFront.Panel }
}
}
};
}
public static ArchCabinetType Find( ArchKit kit, string name ) {
var offered = kit?.Cabinets;
if ( offered is null || offered.Count == 0 ) {
return Shipped().FirstOrDefault();
}
return offered.FirstOrDefault( type => string.Equals( type.Name, name, StringComparison.OrdinalIgnoreCase ) )
?? offered.FirstOrDefault();
}
public static ArchCabinetType Copy( ArchCabinetType type ) {
if ( type is null ) {
return Shipped().First();
}
return new ArchCabinetType {
Name = type.Name,
Detail = type.Detail,
Tier = type.Tier,
Width = type.Width,
Depth = type.Depth,
Height = type.Height,
PlinthHeight = type.PlinthHeight,
PlinthRecess = type.PlinthRecess,
Datum = type.Datum,
TopThickness = type.TopThickness,
TopDepth = type.TopDepth,
TopOversail = type.TopOversail,
TopEndOversail = type.TopEndOversail,
TopBackOversail = type.TopBackOversail,
Corner = type.Corner,
FrontSplit = type.FrontSplit,
PanelThickness = type.PanelThickness,
FrontThickness = type.FrontThickness,
Reveal = type.Reveal,
PanelInset = type.PanelInset,
PanelRaise = type.PanelRaise,
PanelMargin = type.PanelMargin,
CorniceHeight = type.CorniceHeight,
CorniceProjection = type.CorniceProjection,
CorniceOversail = type.CorniceOversail,
Shelves = type.Shelves,
DrawerRows = type.DrawerRows,
DrawerHeight = type.DrawerHeight,
Handles = type.Handles,
Grip = type.Grip,
HandleLength = type.HandleLength,
HandleThickness = type.HandleThickness,
HandleStandoff = type.HandleStandoff,
Interior = type.Interior,
Prefabbed = type.Prefabbed,
Hidden = type.Hidden,
MaterialGroup = type.MaterialGroup,
Fittings = type.Fittings?.Select( Copy ).ToList() ?? new List<ArchCabinetFitting>(),
Carcasses = type.Carcasses?.Select( carcass => carcass.Copy() ).ToList() ?? new List<ArchCabinetCarcass>()
};
}
static ArchCabinetFitting Copy( ArchCabinetFitting fitting ) {
return new ArchCabinetFitting {
Mount = fitting.Mount,
Front = fitting.Front,
Prefab = fitting.Prefab,
Fits = fitting.Fits,
Anchor = fitting.Anchor,
AnchorAlong = fitting.AnchorAlong,
AnchorTurn = fitting.AnchorTurn
};
}
}