Editor/TerrainLineTool.Guide.cs
using System;
using Sandbox;

namespace TerrainLines;

public sealed partial class TerrainLineTool
{
	internal Vector3 GuidePoint( Terrain terrain, Vector3 point )
	{
		point.z = _edit is { IsCurrent: true } ? _edit.SampleHeight( point.x, point.y ) : LineRaster.SampleHeight( terrain.Storage, point.x, point.y );
		return point + Vector3.Up * (2 / terrain.WorldScale.x);
	}

	private void DrawGuide( Terrain terrain, bool hasHit, Vector3 hit )
	{
		using var scope = Gizmo.Scope( "terrain-lines" );
		Gizmo.Transform = terrain.WorldTransform;
		Gizmo.Draw.IgnoreDepth = true;
		Gizmo.Draw.Color = Color.Cyan;
		Gizmo.Draw.LineThickness = 2;
		for ( int i = 1; i < _path.Count; i++ ) DrawSurfaceLine( terrain, _path[i - 1], _path[i] );
		if ( !hasHit || _editingPoints ) return;
		float halfWidth = Math.Max( 1, Settings.PathWidth ) * 0.5f / terrain.WorldScale.x;
		DrawSurfaceCircle( terrain, hit, halfWidth );
		DrawSurfaceCircle( terrain, hit, halfWidth + Math.Max( 0, Settings.ShoulderWidth ) / terrain.WorldScale.x );
		if ( _points.Count > 0 && (!_drawing || _straightGesture) ) DrawSurfaceLine( terrain, _points[^1], hit );
	}

	private void DrawSurfaceLine( Terrain terrain, Vector3 a, Vector3 b )
	{
		float spacing = terrain.Storage.TerrainSize / terrain.Storage.Resolution;
		int count = Math.Max( 1, (int)MathF.Ceiling( LineRaster.Distance2D( a, b ) / spacing ) );
		var previous = GuidePoint( terrain, a );
		for ( int i = 1; i <= count; i++ )
		{
			var point = GuidePoint( terrain, a + (b - a) * ((float)i / count) );
			Gizmo.Draw.Line( previous, point );
			previous = point;
		}
	}

	private void DrawSurfaceCircle( Terrain terrain, Vector3 center, float radius )
	{
		var previous = GuidePoint( terrain, center + new Vector3( radius, 0, 0 ) );
		for ( int i = 1; i <= 64; i++ )
		{
			float angle = i * MathF.PI / 32;
			var point = GuidePoint( terrain, center + new Vector3( MathF.Cos( angle ) * radius, MathF.Sin( angle ) * radius, 0 ) );
			Gizmo.Draw.Line( previous, point );
			previous = point;
		}
	}

	private void UpdatePointEditing( Terrain terrain, bool hasHit, Vector3 hit )
	{
		using var scope = Gizmo.Scope( "terrain-line-points" );
		Gizmo.Transform = terrain.WorldTransform;
		Gizmo.Draw.IgnoreDepth = true;
		for ( int i = 0; i < _points.Count; i++ )
		{
			using var pointScope = Gizmo.Scope( $"point-{i}" );
			var point = GuidePoint( terrain, _points[i] );
			float size = Math.Max( 2, Gizmo.Camera.Position.Distance( terrain.WorldTransform.PointToWorld( point ) ) * 0.004f ) / terrain.WorldScale.x;
			Gizmo.Hitbox.DepthBias = 0.01f;
			Gizmo.Hitbox.Sphere( new Sphere( point, size * 2 ) );
			if ( Gizmo.Pressed.This )
			{
				if ( _movingPoint < 0 ) { BeginGesture( terrain ); _movingPoint = i; }
				if ( _movingPoint == i && hasHit ) MovePoint( i, hit );
			}
			else if ( _movingPoint == i )
			{
				EndGesture( "Move terrain line point" );
				_movingPoint = -1;
			}
			Gizmo.Draw.Color = Gizmo.IsHovered ? Color.Yellow : Color.Cyan;
			Gizmo.Draw.SolidSphere( point, size );
		}
	}
}