Editor tool that stamps roof vents (box, mushroom, turbine, pipe stack, cowl) into a map using MeshKit primitives, with size, height, sink, sides and flashing options.
namespace AdvancedMappingTools;
public enum RoofVentStyle
{
[Title( "Box Vent" )] Box,
Mushroom,
Turbine,
[Title( "Pipe Stack" )] PipeStack,
Cowl
}
/// <summary>
/// Vents on a roof: box vents, mushroom caps, turbines, pipe stacks with a rain cap and cowls.
/// Each click sets one. It stands upright and reaches below the click, so on a pitched roof
/// the base sits in the roof instead of floating on one corner.
/// </summary>
[ToolCategory( "Roofs" )]
[Title( "Roof Ventilation" ), Icon( "air" )]
public sealed class RoofVentEditor( PrimitiveTool tool ) : StampEditor( tool )
{
[Property, Group( "Vent" ), WideMode]
public RoofVentStyle Style { get; set; } = RoofVentStyle.Mushroom;
[Property, Group( "Vent" ), Range( 2, 256, slider: false ), WideMode, Description( "Across the vent." )]
public float Size { get; set; } = 16.0f;
[Property, Group( "Vent" ), Range( 2, 512, slider: false ), WideMode, Description( "From the roof up to the top." )]
public float Height { get; set; } = 18.0f;
[Property, Group( "Vent" ), Range( 0, 128, slider: false ), WideMode, Description( "How far the vent reaches into the roof. A steeper roof needs more." )]
public float Sink { get; set; } = 6.0f;
[Property, Group( "Vent" ), Range( 6, 48, slider: false ), WideMode, Description( "Sides of round parts." )]
public int Sides { get; set; } = 16;
[Property, Group( "Vent" ), Title( "Flashing" ), WideMode, Description( "A flat plate around the foot, in the trim material." )]
public bool Flashing { get; set; } = true;
protected override void Stamp( MeshKit kit, Transform frame, Random random )
{
var r = MathF.Max( Size, 2.0f ) * 0.5f;
var h = MathF.Max( Height, 2.0f );
var low = -MathF.Max( Sink, 0.0f );
var sides = Math.Clamp( Sides, 6, 48 );
var surface = kit.Material;
switch ( Style )
{
case RoofVentStyle.Box:
kit.Box( new Vector3( -r * 0.8f, -r * 0.8f, low ), new Vector3( r * 0.8f, r * 0.8f, h * 0.6f ) );
kit.Material = TrimMaterial;
// The hood leans out over the opening on the low side.
kit.Box( new Vector3( -r, -r, h * 0.6f ), new Vector3( r, r, h ) );
break;
case RoofVentStyle.Mushroom:
kit.Cylinder( Vector3.Zero, r * 0.45f, r * 0.45f, low, h * 0.6f, sides );
kit.Material = TrimMaterial;
kit.Lathe( Vector3.Up * (h * 0.55f), [new( 0.0f, 0.0f ), new( r, 0.0f ), new( r, h * 0.15f ), new( r * 0.6f, h * 0.4f ), new( 0.0f, h * 0.45f )], sides );
break;
case RoofVentStyle.Turbine:
kit.Cylinder( Vector3.Zero, r * 0.55f, r * 0.55f, low, h * 0.35f, sides );
kit.Material = TrimMaterial;
// The vanes as a ribbed ball: a lathe with a waist, then a cap on top.
kit.Lathe( Vector3.Up * (h * 0.35f), [new( 0.0f, 0.0f ), new( r * 0.7f, 0.0f ), new( r, h * 0.2f ), new( r * 0.95f, h * 0.45f ), new( r * 0.5f, h * 0.62f ), new( 0.0f, h * 0.65f )], sides );
break;
case RoofVentStyle.PipeStack:
kit.Cylinder( Vector3.Zero, r * 0.35f, r * 0.35f, low, h * 0.85f, sides );
kit.Material = TrimMaterial;
kit.Cylinder( Vector3.Zero, r * 0.75f, 0.0f, h * 0.85f, h, sides );
break;
case RoofVentStyle.Cowl:
kit.Cylinder( Vector3.Zero, r * 0.5f, r * 0.5f, low, h * 0.55f, sides );
kit.Material = TrimMaterial;
// The head turns its mouth to one side, here forward along the yaw of the stamp.
kit.Beam( Vector3.Up * (h * 0.75f), new Vector3( r, 0.0f, h * 0.75f ), r * 1.1f, h * 0.45f );
kit.Cylinder( Vector3.Zero, r * 0.55f, r * 0.55f, h * 0.55f, h, sides );
break;
}
if ( !Flashing ) return;
kit.Material = TrimMaterial;
kit.Box( new Vector3( -r * 1.25f, -r * 1.25f, low ), new Vector3( r * 1.25f, r * 1.25f, low + 0.5f ) );
kit.Material = surface;
}
}