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",
]
);
