├── unknown-pleasures.gif ├── README.md ├── LICENSE └── unknown-pleasures.py /unknown-pleasures.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rougier/unknown-pleasures/HEAD/unknown-pleasures.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Unknown pleasures 3 | 4 | Animated lines using matplotlib. 5 | 6 | ![](unknown-pleasures.gif) 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2021, Nicolas P. Rougier 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /unknown-pleasures.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.animation as animation 4 | 5 | 6 | np.random.seed(1) 7 | fig = plt.figure(figsize=(4, 4), facecolor='white') 8 | ax = fig.add_axes([0,0,1,1], frameon=False) 9 | 10 | # Generate random data 11 | data = np.random.uniform(0, 1, (64, 100)) 12 | X = np.linspace(-1, 1, data.shape[-1]) 13 | G = 1.5 * np.exp(-4 * X ** 2) 14 | 15 | # Generate line plots 16 | lines = [] 17 | for i in range(len(data)): 18 | # Small reduction of the X extents to get a cheap perspective effect 19 | xscale = 1 - i / 200. 20 | # Same for linewidth (thicker strokes on bottom) 21 | lw = 1. - i / 100.0 22 | line, = ax.plot(xscale * X, i + G * data[i], color="black", lw=lw) 23 | lines.append(line) 24 | 25 | # Set y limit (to avoid cropping because of thickness) 26 | ax.set_ylim(-2, 65) 27 | ax.set_xticks([]) 28 | ax.set_yticks([]) 29 | 30 | def update(*args): 31 | # Shift all data to the right 32 | data[:, 1:] = data[:, :-1] 33 | 34 | # Fill-in new values 35 | data[:, 0] = np.random.uniform(0, 1, len(data)) 36 | 37 | # Update data 38 | for i in range(len(data)): 39 | lines[i].set_ydata(i + G * data[i]) 40 | 41 | # Return modified artists 42 | return lines 43 | 44 | anim = animation.FuncAnimation(fig, update, frames=100, interval=20) 45 | anim.save('unknown-pleasures.gif', writer='imagemagick', fps=60) 46 | plt.show() 47 | --------------------------------------------------------------------------------