Editor tool that generates a retaining wall along a drawn path: a thick face wall, an optional grounded backfill terrace, and a coping trim on top. Built on PathEditor and emits geometry through MeshKit with SurfaceMaterial and TrimMaterial.
namespace AdvancedMappingTools;
/// <summary>
/// A retaining wall along a path: a thick face wall, a coping and an optional backfill
/// terrace behind it. The path is the front face line.
/// </summary>
[ToolCategory( "Walls & Floors" )]
[Title( "Retaining Wall" ), Icon( "foundation" )]
public sealed class RetainingWallEditor( PrimitiveTool tool ) : PathEditor( tool )
{
protected override bool SnapsToLines => true;
protected override bool CanClose => true;
protected override int MinPoints => 2;
[Property, Group( "Retaining Wall" ), Range( 8, 2048, slider: false ), WideMode]
public float Height { get; set; } = 128.0f;
[Property, Group( "Retaining Wall" ), Range( 2, 256, slider: false ), WideMode]
public float Thickness { get; set; } = 20.0f;
[Property, Group( "Retaining Wall" ), Title( "Coping" ), WideMode]
public bool Coping { get; set; } = true;
[Property, ShowIf( nameof( Coping ), true ), Group( "Retaining Wall" ), Title( "Coping Overhang" ), Range( 0, 32, slider: false ), WideMode]
public float CopingOverhang { get; set; } = 4.0f;
[Property, Group( "Retaining Wall" ), Title( "Backfill" ), Range( 0, 512, slider: false ), WideMode, Description( "Terraces the ground behind the wall. Zero leaves the wall free standing." )]
public float Backfill { get; set; } = 0.0f;
protected override void BuildMesh( MeshKit kit )
{
var path = SamplePath();
if ( path.Count < 2 ) return;
var height = MathF.Max( Height, 8.0f );
var half = MathF.Max( Thickness, 2.0f ) * 0.5f;
var over = MathF.Max( CopingOverhang, 0.0f );
kit.Material = SurfaceMaterial;
SweepPath( kit, path, Closed, [new( -half, 0.0f ), new( half, 0.0f ), new( half, height ), new( -half, height )] );
if ( Backfill > 0.0f )
{
var back = MathF.Max( Backfill, 1.0f );
SweepPath( kit, path, Closed, [new( half, 0.0f ), new( half + back, 0.0f ), new( half + back, height - 2.0f ), new( half, height - 2.0f )] );
}
if ( !Coping ) return;
kit.Material = TrimMaterial;
SweepPath( kit, path, Closed, [new( -half - over, height - 4.0f ), new( half + over, height - 4.0f ), new( half + over, height ), new( -half - over, height )] );
}
}