pythonnumpymatplotlibnumbersfractals

How to set limits with mandelbrot set


I'm currently coding a Mandelbrot set using matplotlib with a zoom function to infinitely zoom in on any section of it, however when I zoom in the new data that's calculated is off-center from the graph axes.

enter image description here

This screenshot shows the graph after one zoom in - the set isn't generated correctly for the axes. The graph should be symmetrical about y=0, but it isn't - it changes whenever I zoom. I can't think of what could be causing this - I can't see any issue in the code. How do i fix this?

Here's my code:

import numpy as np
import matplotlib.pyplot as plt

def mandelbrot_set(xmin, xmax, ymin, ymax, width, height, maxiter):
    x = np.linspace(xmin, xmax, width).reshape((1, width))
    y = np.linspace(ymin, ymax, height).reshape((height, 1))
    c = x + y * 1j
    z = c
    fractal = np.zeros(z.shape, dtype=int)
    for i in range(maxiter):
        z = z**2 + c
        mask = np.abs(z) > 2
        fractal += mask
        z[mask] = 2
    return fractal

xmin, xmax, ymin, ymax = -2, 1, -1.5, 1.5
maxiter = 300
width, height = 500, 500

fractal = mandelbrot_set(xmin, xmax, ymin, ymax, width, height, maxiter)
fig, ax = plt.subplots(figsize=(10, 10))
im = ax.imshow(fractal, cmap='magma', extent=(xmin, xmax, ymin, ymax))


def update_plot(xmin, xmax, ymin, ymax):
    fractal = mandelbrot_set(xmin, xmax, ymin, ymax, width, height, maxiter)
    im.set_data(fractal)
    im.set_extent((xmin, xmax, ymin, ymax))
    fig.canvas.draw()

def on_scroll(event):
    xmin, xmax = ax.get_xlim()
    ymin, ymax = ax.get_ylim()

    x, y = event.xdata, event.ydata
    if event.button == 'up':
        scale_factor = 0.5
    else:
        scale_factor = 2.0
    xmin = x - (x - xmin)*scale_factor
    xmax = x + (xmax - x)*scale_factor
    ymin = y - (y - ymin)*scale_factor
    ymax = y + (ymax - y)*scale_factor
    print(f"xmin: {xmin}, xmax: {xmax}, ymin: {ymin}, ymax: {ymax}")

    update_plot(xmin, xmax, ymin, ymax)

fig.canvas.mpl_connect('scroll_event', on_scroll)

plt.show()

Solution

  • Your issue is that imshow by default plots with origin='upper' (https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.imshow.html#matplotlib.axes.Axes.imshow) meaning Y axis is reversed, breaking your boundary computation downstream, use

    im = ax.imshow(fractal, cmap='magma', extent=(xmin, xmax, ymin, ymax), origin='lower')
    

    and it will solve your problems.

    Also not sure if this was on purpose from your side, but this bondary update rules seem to make more sense?

    xmin, xmax = ax.get_xlim()
    ymin, ymax = ax.get_ylim()
    x_scale = xmax - xmin
    y_scale = ymax - ymin
    dx = x_scale/2*scale_factor
    dy = y_scale/2*scale_factor
    xmin = x - dx
    xmax = x + dx
    ymin = y - dy
    ymax = y + dy