Editor tool that generates a sidewalk and curb mesh along a drawn path. Sweeps a cross-section profile for the slab and one or two curb strips, with side, width, height and curb dimensions as properties.
namespace AdvancedMappingTools;
public enum SidewalkSide
{
Left,
Right,
Both
}
/// <summary>
/// A sidewalk with a raised curb along a path. The path is the road edge.
/// </summary>
[ToolCategory( "Roads & Paths" )]
[Title( "Sidewalk & Curb" ), Icon( "directions_walk" )]
public sealed class SidewalkEditor( PrimitiveTool tool ) : PathEditor( tool )
{
protected override bool SnapsToLines => true;
protected override bool CanClose => true;
protected override int MinPoints => 2;
[Property, Group( "Sidewalk" ), WideMode]
public SidewalkSide Side { get; set; } = SidewalkSide.Left;
[Property, Group( "Sidewalk" ), Range( 8, 512, slider: false ), WideMode]
public float Width { get; set; } = 48.0f;
[Property, Group( "Sidewalk" ), Range( 1, 64, slider: false ), WideMode]
public float Height { get; set; } = 6.0f;
[Property, Group( "Sidewalk" ), Title( "Curb Height" ), Range( 1, 32, slider: false ), WideMode]
public float CurbHeight { get; set; } = 8.0f;
[Property, Group( "Sidewalk" ), Title( "Curb Width" ), Range( 1, 32, slider: false ), WideMode]
public float CurbWidth { get; set; } = 4.0f;
protected override void BuildMesh( MeshKit kit )
{
var path = SamplePath();
if ( path.Count < 2 ) return;
var width = MathF.Max( Width, 8.0f );
var height = MathF.Max( Height, 1.0f );
var curb = MathF.Max( CurbHeight, 1.0f );
var curbWidth = MathF.Max( CurbWidth, 1.0f );
kit.Material = SurfaceMaterial;
if ( Side == SidewalkSide.Both )
{
// One slab across, so the two halves do not share a face in the middle.
SweepPath( kit, path, Closed, [new( -width, 0.0f ), new( width, 0.0f ), new( width, height ), new( -width, height )] );
}
else if ( Side is SidewalkSide.Left )
{
SweepPath( kit, path, Closed, [new( 0.0f, 0.0f ), new( width, 0.0f ), new( width, height ), new( 0.0f, height )] );
}
else
{
SweepPath( kit, path, Closed, [new( -width, 0.0f ), new( 0.0f, 0.0f ), new( 0.0f, height ), new( -width, height )] );
}
kit.Material = TrimMaterial;
if ( Side == SidewalkSide.Both )
{
// Curbs on the outer edges, not in the middle.
SweepPath( kit, path, Closed, [new( -width - curbWidth, 0.0f ), new( -width, 0.0f ), new( -width, curb ), new( -width - curbWidth, curb )] );
SweepPath( kit, path, Closed, [new( width, 0.0f ), new( width + curbWidth, 0.0f ), new( width + curbWidth, curb ), new( width, curb )] );
}
else if ( Side is SidewalkSide.Left )
{
SweepPath( kit, path, Closed, [new( -curbWidth, 0.0f ), new( 0.0f, 0.0f ), new( 0.0f, curb ), new( -curbWidth, curb )] );
}
else
{
SweepPath( kit, path, Closed, [new( 0.0f, 0.0f ), new( curbWidth, 0.0f ), new( curbWidth, curb ), new( 0.0f, curb )] );
}
}
}