Editor/FoliagePainter.Brush.cs
using System;
using System.Collections.Generic;
using Sandbox;
namespace Foliage;

public partial class FoliagePainter
{
	private float[]? _brushAliasProbabilities;
	private int[]? _brushAliasIndices;
	private int _brushAliasWidth;
	private int _brushAliasHeight;

	private void BuildBrushAliasTable()
	{
		_brushAliasProbabilities = null;
		_brushAliasIndices = null;
		_brushAliasWidth = 0;
		_brushAliasHeight = 0;

		if ( Settings.Brush is null || Settings.Brush.Pixmap is null )
			return;

		var pixmap = Settings.Brush.Pixmap;
		var width = pixmap.Width;
		var height = pixmap.Height;

		if ( width <= 0 || height <= 0 )
			return;

		var count = width * height;
		var weights = new float[count];

		float totalWeight = 0f;

		for ( int y = 0; y < height; y++ )
		{
			for ( int x = 0; x < width; x++ )
			{
				var index = y * width + x;
				var color = pixmap.GetPixel( x, y );
				var weight = MathF.Max( 0f, color.Luminance * color.a );
				weights[index] = weight;
				totalWeight += weight;
			}
		}

		if ( totalWeight <= 0f )
		{
			// Fallback to uniform distribution
			_brushAliasProbabilities = new float[count];
			_brushAliasIndices = new int[count];
			for ( int i = 0; i < count; i++ )
			{
				_brushAliasProbabilities[i] = 1f;
				_brushAliasIndices[i] = i;
			}

			_brushAliasWidth = width;
			_brushAliasHeight = height;
			return;
		}

		var scaledWeights = new float[count];
		var small = new Queue<int>();
		var large = new Queue<int>();

		for ( int i = 0; i < count; i++ )
		{
			var scaled = weights[i] * count / totalWeight;
			scaledWeights[i] = scaled;
			if ( scaled < 1f )
				small.Enqueue( i );
			else
				large.Enqueue( i );
		}

		var probabilities = new float[count];
		var aliases = new int[count];

		while ( small.Count > 0 && large.Count > 0 )
		{
			var less = small.Dequeue();
			var more = large.Dequeue();

			probabilities[less] = scaledWeights[less];
			aliases[less] = more;

			scaledWeights[more] = (scaledWeights[more] + scaledWeights[less]) - 1f;

			if ( scaledWeights[more] < 1f )
				small.Enqueue( more );
			else
				large.Enqueue( more );
		}

		while ( large.Count > 0 )
		{
			var index = large.Dequeue();
			probabilities[index] = 1f;
			aliases[index] = index;
		}

		while ( small.Count > 0 )
		{
			var index = small.Dequeue();
			probabilities[index] = 1f;
			aliases[index] = index;
		}

		_brushAliasProbabilities = probabilities;
		_brushAliasIndices = aliases;
		_brushAliasWidth = width;
		_brushAliasHeight = height;
	}

	private Vector2 GetRandomBrushPixel()
	{
		if ( _brushAliasProbabilities is null || _brushAliasIndices is null || _brushAliasWidth <= 0 || _brushAliasHeight <= 0 )
		{
			BuildBrushAliasTable();

			if ( _brushAliasProbabilities is null || _brushAliasIndices is null || _brushAliasWidth <= 0 || _brushAliasHeight <= 0 )
				return Vector2.Zero;
		}

		var count = _brushAliasProbabilities.Length;
		if ( count == 0 )
			return Vector2.One * 0.5f;

		var index = System.Random.Shared.Next( count );
		var threshold = System.Random.Shared.Float( 0f, 1f );
		var chosen = threshold < _brushAliasProbabilities[index] ? index : _brushAliasIndices[index];

		var x = chosen % _brushAliasWidth;
		var y = chosen / _brushAliasWidth;

		var jitterX = System.Random.Shared.Float( 0f, 1f );
		var jitterY = System.Random.Shared.Float( 0f, 1f );

		var u = (x + jitterX) / _brushAliasWidth;
		var v = (y + jitterY) / _brushAliasHeight;

		return new Vector2( u, v );
	}
}