├── .gitignore ├── LICENSE ├── README.md ├── mandlebrot.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.gif 2 | _temp/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jaime Liew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mandelbrot 2 | A Mandelbrot animation generator using Matplotlib and MoviePy. Example animations [can be found here](https://imgur.com/a/TlMVHm6). 3 | -------------------------------------------------------------------------------- /mandlebrot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Nov 7 19:37:33 2018 4 | 5 | @author: Jaime Liew 6 | """ 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | from moviepy.editor import VideoClip 10 | from moviepy.video.io.bindings import mplfig_to_npimage 11 | 12 | 13 | def mandelbrot(X, Y, width, nx): 14 | """ 15 | Yields the Mandelbrot set at each iteration, centered at X, Y. 16 | 17 | args: 18 | X (float): X (real) coordinate of interest. 19 | Y (float): Y (imaginary) coordinate of interest. 20 | width (float): Frame width. 21 | nx (int): X and Y discretization. 22 | Yields: 23 | Mand (nx x nx array): Mandelbrot set for the next iteration. 24 | """ 25 | 26 | x = np.linspace(X - width, X + width, nx) 27 | y = np.linspace(Y - width, Y + width, nx) 28 | c = x[:, np.newaxis] + 1j * y[np.newaxis, :] 29 | z = c 30 | 31 | z = z ** 2 + c 32 | mand = abs(z) < 2 33 | 34 | while mand.all(): 35 | z = z ** 2 + c 36 | mand = abs(z) < 2 37 | 38 | yield mand 39 | 40 | while True: 41 | z = z ** 2 + c 42 | mand = abs(z) < 2 43 | yield mand 44 | 45 | 46 | class MandelbrotImage(object): 47 | """ 48 | Class that creates an animated gif of the mandelbrot set at a given 49 | location. 50 | """ 51 | 52 | def __init__(self, x, y, width, duration, figsize=4, nx=500, fps=20): 53 | """ 54 | args: 55 | X (float): X (real) coordinate of interest. 56 | Y (float): Y (imaginary) coordinate of interest. 57 | width (float): Frame width. 58 | duration (float): GIF duration in seconds. 59 | figsize (float): figure side length (default=4). 60 | nx (int): X and Y discretisation (default=500). 61 | fps (int): frames per second. 62 | """ 63 | self.mandelbrot_gen = mandelbrot(x, y, width, nx=nx) 64 | self.duration = duration 65 | self.figsize = figsize 66 | self.fps = fps 67 | self.image = np.zeros((nx, nx)) 68 | 69 | def iterate(self, t): 70 | """ 71 | Performs one iteration of the Mandelbrot calculation and returns the 72 | figure plotting the cumulative iterations. 73 | """ 74 | mand = next(self.mandelbrot_gen) 75 | self.image += np.array(mand, dtype=float) * t / self.duration 76 | 77 | fig = plt.figure(figsize=(self.figsize, self.figsize)) 78 | plt.imshow(self.image.T, 79 | cmap='nipy_spectral_r', 80 | interpolation='bilinear') 81 | plt.axis('off') 82 | return fig 83 | 84 | def make_gif(self, file_name): 85 | def make_frame(t): 86 | fig = self.iterate(t) 87 | img = mplfig_to_npimage(fig) 88 | plt.close(fig) 89 | return img 90 | 91 | animation = VideoClip(make_frame, duration=self.duration) 92 | animation.write_gif(file_name, fps=self.fps) 93 | 94 | 95 | def parse_args(): 96 | import argparse 97 | parser = argparse.ArgumentParser() 98 | demo_or_manual = parser.add_mutually_exclusive_group() 99 | 100 | demo_or_manual.add_argument("--demo", action="store_true") 101 | 102 | manual = demo_or_manual.add_argument_group() 103 | 104 | manual.add_argument("-f", "--file_name", dest="file_name", default="mandelbrot.gif", 105 | help="Output file name.") 106 | manual.add_argument("-d", "--dims", dest="dims", type=int, default=500, 107 | help="Output image width (and height).") 108 | manual.add_argument("-x", dest="x", default=-0.235125, type=float, 109 | help="Center of Mandlebrot image in x.") 110 | manual.add_argument("-y", dest="y", default=0.827215, type=float, 111 | help="Center of Mandlebrot image in y.") 112 | manual.add_argument("-w", "--width", dest="width", default=4.0E-5, type=float, 113 | help="Width of Mandelbrot image in xy plane.") 114 | manual.add_argument("-s", "--seconds", dest="seconds", default=10, type=float, 115 | help="Duration of gif in seconds.") 116 | manual.add_argument("--fps", dest="fps", default=20, type=int, 117 | help="Frames per second for the output gif.") 118 | 119 | return parser.parse_args() 120 | 121 | 122 | if __name__ == '__main__': 123 | args = parse_args() 124 | 125 | if args.demo: 126 | params = { # filename: (X, Y, R, duration) 127 | 'out1.gif': (-0.235125, 0.827215, 4.0E-5, 10), 128 | 'out2.gif': (-0.925, -0.266, 0.032, 10), 129 | 'out3.gif': (-0.745428, 0.113009, 3e-5, 10), 130 | 'out4.gif': (-0.748, 0.1, 0.0014, 20), 131 | } 132 | 133 | for filename, (X, Y, R, duration) in params.items(): 134 | mandelbrot_gif = MandelbrotImage(X, Y, R, duration) 135 | mandelbrot_gif.make_gif(filename) 136 | else: 137 | mandelbrot_gif = MandelbrotImage(args.x, args.y, args.width, args.seconds, 4, args.dims, args.fps) 138 | mandelbrot_gif.make_gif(args.file_name) 139 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.18.0 2 | moviepy==1.0.1 3 | matplotlib==3.1.2 4 | --------------------------------------------------------------------------------