the mandelbrot set is the set of complex numbers c for which the series zn1 zn zn c does not diverge i e remains bounded thus a complex number c is a member of the mandelbrot set if when starting with z0 0 and applying the iteration repeatedly the absolute value of zn remains bounded for all n 0 complex numbers can be written as a bi a is the real component usually drawn on the xaxis and bi is the imaginary component usually drawn on the yaxis most visualizations of the mandelbrot set use a colorcoding to indicate after how many steps in the series the numbers outside the set diverge images of the mandelbrot set exhibit an elaborate and infinitely complicated boundary that reveals progressively everfiner recursive detail at increasing magnifications making the boundary of the mandelbrot set a fractal curve description adapted from https en wikipedia orgwikimandelbrotset see also https en wikipedia orgwikiplottingalgorithmsforthemandelbrotset return the relative distance stepmaxstep after which the complex number constituted by this xypair diverges members of the mandelbrot set do not diverge so their distance is 1 getdistance0 0 50 1 0 getdistance0 5 0 5 50 0 061224489795918366 getdistance2 0 50 0 0 divergence happens for all complex number with an absolute value greater than 4 blackwhite colorcoding that ignores the relative distance the mandelbrot set is black everything else is white getblackandwhitergb0 255 255 255 getblackandwhitergb0 5 255 255 255 getblackandwhitergb1 0 0 0 colorcoding taking the relative distance into account the mandelbrot set is black getcolorcodedrgb0 255 0 0 getcolorcodedrgb0 5 0 255 255 getcolorcodedrgb1 0 0 0 function to generate the image of the mandelbrot set two types of coordinates are used imagecoordinates that refer to the pixels and figurecoordinates that refer to the complex numbers inside and outside the mandelbrot set the figurecoordinates in the arguments of this function determine which section of the mandelbrot set is viewed the main area of the mandelbrot set is roughly between 1 5 x 0 5 and 1 y 1 in the figurecoordinates commenting out tests that slow down pytest 13 35s call fractalsmandelbrot py mandelbrot getimage getimage load0 0 255 0 0 getimageusedistancecolorcoding false load0 0 255 255 255 loop through the imagecoordinates determine the figurecoordinates based on the imagecoordinates color the corresponding pixel based on the selected coloringfunction colored version full figure uncomment for colored version different section zoomed in img getimagefigurecenterx 0 6 figurecentery 0 4 figurewidth 0 8 uncomment for black and white version full figure img getimageusedistancecolorcoding false uncomment to save the image img savemandelbrot png type ignore return the relative distance step max_step after which the complex number constituted by this x y pair diverges members of the mandelbrot set do not diverge so their distance is 1 get_distance 0 0 50 1 0 get_distance 0 5 0 5 50 0 061224489795918366 get_distance 2 0 50 0 0 noqa b007 divergence happens for all complex number with an absolute value greater than 4 black white color coding that ignores the relative distance the mandelbrot set is black everything else is white get_black_and_white_rgb 0 255 255 255 get_black_and_white_rgb 0 5 255 255 255 get_black_and_white_rgb 1 0 0 0 color coding taking the relative distance into account the mandelbrot set is black get_color_coded_rgb 0 255 0 0 get_color_coded_rgb 0 5 0 255 255 get_color_coded_rgb 1 0 0 0 function to generate the image of the mandelbrot set two types of coordinates are used image coordinates that refer to the pixels and figure coordinates that refer to the complex numbers inside and outside the mandelbrot set the figure coordinates in the arguments of this function determine which section of the mandelbrot set is viewed the main area of the mandelbrot set is roughly between 1 5 x 0 5 and 1 y 1 in the figure coordinates commenting out tests that slow down pytest 13 35s call fractals mandelbrot py mandelbrot get_image get_image load 0 0 255 0 0 get_image use_distance_color_coding false load 0 0 255 255 255 loop through the image coordinates determine the figure coordinates based on the image coordinates color the corresponding pixel based on the selected coloring function colored version full figure uncomment for colored version different section zoomed in img get_image figure_center_x 0 6 figure_center_y 0 4 figure_width 0 8 uncomment for black and white version full figure img get_image use_distance_color_coding false uncomment to save the image img save mandelbrot png
import colorsys

from PIL import Image  


def get_distance(x: float, y: float, max_step: int) -> float:
    
    a = x
    b = y
    for step in range(max_step):  
        a_new = a * a - b * b + x
        b = 2 * a * b + y
        a = a_new

        
        
        if a * a + b * b > 4:
            break
    return step / (max_step - 1)


def get_black_and_white_rgb(distance: float) -> tuple:
    
    if distance == 1:
        return (0, 0, 0)
    else:
        return (255, 255, 255)


def get_color_coded_rgb(distance: float) -> tuple:
    
    if distance == 1:
        return (0, 0, 0)
    else:
        return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(distance, 1, 1))


def get_image(
    image_width: int = 800,
    image_height: int = 600,
    figure_center_x: float = -0.6,
    figure_center_y: float = 0,
    figure_width: float = 3.2,
    max_step: int = 50,
    use_distance_color_coding: bool = True,
) -> Image.Image:
    
    img = Image.new("RGB", (image_width, image_height))
    pixels = img.load()

    
    for image_x in range(image_width):
        for image_y in range(image_height):
            
            figure_height = figure_width / image_width * image_height
            figure_x = figure_center_x + (image_x / image_width - 0.5) * figure_width
            figure_y = figure_center_y + (image_y / image_height - 0.5) * figure_height

            distance = get_distance(figure_x, figure_y, max_step)

            
            if use_distance_color_coding:
                pixels[image_x, image_y] = get_color_coded_rgb(distance)
            else:
                pixels[image_x, image_y] = get_black_and_white_rgb(distance)

    return img


if __name__ == "__main__":
    import doctest

    doctest.testmod()

    
    img = get_image()

    
    
    

    
    

    
    

    img.show()