Noisy Planet
A python implementation of noisy planet.
import numpy as np
npoints = 1000
radius = 10
r = np.sqrt(np.random.rand(npoints)) * radius
theta = np.random.rand(npoints) * 2 * np.pi
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(8, 8))
ax.plot(theta, r, "o", alpha=0.5);
Given this set of initial point, we will generate random trajectories using Perlin noise. Despite this kind of noise seems not to be coded within numpy
or scipy
, there are python
packages to do it and we will install and use the following one
!pip install perlin-noise
Let's write a function to return a bunch of trajectories cutting each of them when the next point is outside the planet.
from perlin_noise import PerlinNoise
def generate_noisy_planet(npoints=1000, radius=10, step=0.1, seed=None):
np.random.seed(seed)
noise = PerlinNoise(octaves=4, seed=seed)
trajectories = []
for i in range(npoints):
r = np.sqrt(np.random.rand()) * radius
theta = np.random.rand() * 2 * np.pi
x, y = r * np.cos(theta), r * np.sin(theta)
trajectory = []
while np.hypot(x, y) < radius:
trajectory += [[x, y]]
n = noise([x / 100, y / 100])
fx = np.sin if n > 0.5 else np.cos
fy = np.cos if n > 0.5 else np.sin
x += fx(n * 2 * np.pi) * step
y += fy(n * 2 * np.pi) * step
trajectories += [np.array(trajectory)]
return trajectories
Finally let's plot them in cartesian coordinates
trajectories = generate_noisy_planet()
fig, ax = plt.subplots(figsize=(8, 8))
ax.axis("equal")
ax.axis("off")
cmap = plt.get_cmap("Greys", len(trajectories))
for i, trajectory in enumerate(trajectories):
plt.plot(trajectory[:, 0], trajectory[:, 1], color=cmap(i))
And here are some nice examples of noisy planets
seeds = [20, 52, 666]
cmaps = ["Reds", "Greens", "Blues"]
fig, axes = plt.subplots(ncols=3, figsize=(24, 8))
for i, seed in enumerate(seeds):
axes[i].axis("equal")
axes[i].axis("off")
trajectories = generate_noisy_planet(seed=seed)
cmap = plt.get_cmap(cmaps[i], len(trajectories))
for j, trajectory in enumerate(trajectories):
axes[i].plot(*trajectory.T, color=cmap(j))
Just like the original post, we finally create a gif
file for the latest evil planet (seed=666
)
import os
tmp_fig = "/tmp/planets"
os.makedirs(tmp_fig, exist_ok=True)
plt.figure(figsize=(8, 8))
min_time = 0
max_time = np.max([len(trajectory) for trajectory in trajectories])
for t in range(min_time, max_time):
plt.axis(2 * [-radius, +radius])
plt.axis("off")
for i, trajectory in enumerate(trajectories):
tlim = min(t, len(trajectory))
plt.plot(trajectory[:tlim, 0], trajectory[:tlim, 1], color=cmap(i))
plt.savefig(os.path.join(tmp_fig, "planet_{:03d}.png".format(t)))
plt.clf();
and the process conversion using ImageMagick
import subprocess
subprocess.run(
[
"convert",
"-background",
"white",
"-alpha",
"remove",
"-layers",
"optimize",
"-delay",
"10",
"-loop",
"0",
tmp_fig + "/*.png",
"./images/animation.gif",
]
);