Code/FractalMandelbrotGenerator.cs
namespace FractalGen;

[Icon( "donut_small" )]
[Title( "Fractal - Mandelbrot" )]
[ClassName( "mandelbrotgenerator" )]
public class FractalMandelbrotGenerator : Sandbox.Resources.TextureGenerator
{
    public int MaxSize { get; set; } = 1024;

    [KeyProperty]
    public Color Color { get; set; } = Color.Orange;

    public Color BackgroundColor { get; set; } = Color.Black;

    [KeyProperty]
    public int MaxIterations { get; set; } = 50;

    public float Zoom { get; set; } = 1;

    public Vector2 Offset { get; set; }

    public bool InvertColor { get; set; }

    [Hide, JsonIgnore]
    public override bool CacheToDisk => true;

    protected override ValueTask<Texture> CreateTexture( Options options, CancellationToken ct )
    {
        var bitmap = new Bitmap( MaxSize, MaxSize );
        bitmap.Clear( BackgroundColor );

        Sandbox.Utility.Parallel.For( 0, bitmap.Height, y =>
        {
            for ( int x = 0; x < bitmap.Width; x++ )
            {
                float a = ((float)x).Remap( 0, bitmap.Width, -2 / Zoom + Offset.x, 2 / Zoom + Offset.x );
                float b = ((float)y).Remap( 0, bitmap.Height, -2 / Zoom + Offset.y, 2 / Zoom + Offset.y );

                int n = GetMandelbrotIterations( a, b );
                float brightness = ((float)n).Remap( 0, MaxIterations, 0, 1 );

                Color pixelColor = Color.Lerp( BackgroundColor, Color, brightness );

                lock ( bitmap )
                {
                    bitmap.SetPixel( x, y, pixelColor );
                }
            }
        } );

        if ( InvertColor )
            bitmap.InvertColor();

        return ValueTask.FromResult( bitmap.ToTexture() );
    }

    private int GetMandelbrotIterations( float a, float b )
    {
        float ca = a;
        float cb = b;
        int n = 0;

        while ( n < MaxIterations )
        {
            float aa = a * a - b * b;
            float bb = 2 * a * b;
            a = aa + ca;
            b = bb + cb;

            if ( a * a + b * b > 16 )
                break;

            n++;
        }
        return n;
    }
}