{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Shaded & power normalized rendering\n\nThe Mandelbrot set rendering can be improved by using a normalized recount\nassociated with a power normalized colormap (gamma=0.3). Rendering can be\nfurther enhanced thanks to shading.\n\nThe ``maxiter`` gives the precision of the computation. ``maxiter=200`` should\ntake a few seconds on most modern laptops.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\n\n\ndef mandelbrot_set(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon=2.0):\n    X = np.linspace(xmin, xmax, xn).astype(np.float32)\n    Y = np.linspace(ymin, ymax, yn).astype(np.float32)\n    C = X + Y[:, None] * 1j\n    N = np.zeros_like(C, dtype=int)\n    Z = np.zeros_like(C)\n    for n in range(maxiter):\n        I = abs(Z) < horizon\n        N[I] = n\n        Z[I] = Z[I]**2 + C[I]\n    N[N == maxiter-1] = 0\n    return Z, N\n\n\nif __name__ == '__main__':\n    import time\n\n    import matplotlib.pyplot as plt\n\n    import matplotlib\n    from matplotlib import colors\n\n    xmin, xmax, xn = -2.25, +0.75, 3000 // 2\n    ymin, ymax, yn = -1.25, +1.25, 2500 // 2\n    maxiter = 200\n    horizon = 2.0 ** 40\n    log_horizon = np.log2(np.log(horizon))\n    Z, N = mandelbrot_set(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon)\n\n    # Normalized recount as explained in:\n    # https://linas.org/art-gallery/escape/smooth.html\n    # https://web.archive.org/web/20160331171238/https://www.ibm.com/developerworks/community/blogs/jfp/entry/My_Christmas_Gift?lang=en\n\n    # This line will generate warnings for null values, but it is faster to\n    # process them afterwards using the nan_to_num\n    with np.errstate(invalid='ignore'):\n        M = np.nan_to_num(N + 1 - np.log2(np.log(abs(Z))) + log_horizon)\n\n    dpi = 72\n    width = 10\n    height = 10*yn/xn\n    fig = plt.figure(figsize=(width, height), dpi=dpi)\n    ax = fig.add_axes((0, 0, 1, 1), frameon=False, aspect=1)\n\n    # Shaded rendering\n    light = colors.LightSource(azdeg=315, altdeg=10)\n    M = light.shade(M, cmap=plt.colormaps[\"hot\"], vert_exag=1.5,\n                    norm=colors.PowerNorm(0.3), blend_mode='hsv')\n    ax.imshow(M, extent=[xmin, xmax, ymin, ymax], interpolation=\"bicubic\")\n    ax.set_xticks([])\n    ax.set_yticks([])\n\n    # Some advertisement for matplotlib\n    year = time.strftime(\"%Y\")\n    text = (\"The Mandelbrot fractal set\\n\"\n            \"Rendered with matplotlib %s, %s - https://matplotlib.org\"\n            % (matplotlib.__version__, year))\n    ax.text(xmin+.025, ymin+.025, text, color=\"white\", fontsize=12, alpha=0.5)\n\n    plt.show()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}