Skip to content

Instantly share code, notes, and snippets.

@joferkington
Created February 24, 2020 16:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joferkington/d3076ad449cfdfa2d073eda5a89f914a to your computer and use it in GitHub Desktop.
Save joferkington/d3076ad449cfdfa2d073eda5a89f914a to your computer and use it in GitHub Desktop.
Some auto-generated mold-like art in matplotlib.
"""
Silly auto-generated art. Meant to look like some sort of mold spores.
Deliberately slow to render -- meant to look good at high-res.
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.collections import LineCollection
def main():
spore = MoldSpore()
fig, ax = plt.subplots(figsize=(6,12))
pos = 20 * np.random.random((10, 2))
pos[1,:] *= 0.7
for x, y in pos:
spore.plot(ax, center=(x, y))
ax.axis('off')
fig.patch.set(facecolor='0.3')
ax.set(aspect=1.0)
ax.autoscale()
fig.tight_layout()
plt.show()
class MoldSpore(object):
def __init__(self, fringe_color='white', linewidth=0.01, jitter=0.005,
center_light='#2C9E0D', center_dark='#073303',
random_seed=None):
self.fringe_color = fringe_color
self.lw = linewidth
self.jitter = jitter
self.center_light = center_light
self.center_dark = center_dark
if random_seed is None:
random_seed = np.random.RandomState()
elif isinstance(random_seed, int):
random_seed = np.random.RandomState(random_seed)
self.random = random_seed
def plot(self, ax, nlines=2000, center=(0,0), radius=None):
if radius is None:
radius = 1.5 * self.random.random_sample() + 0.5
mwidth = radius * (0.4 * self.random.random_sample() + 0.3)
fringe = LineCollection(list(self.lines(nlines, center, radius)),
color=self.fringe_color, lw=self.lw)
green, fringes = self.mound(center, mwidth, nlines)
ax.add_collection(fringe)
ax.add_collection(fringes)
ax.add_collection(green)
def filament(self, center, angle, radius, inner_radius=0):
npoints = int((radius - inner_radius) * 500.0)
x0, y0 = center
width = radius - inner_radius
r = np.linspace(inner_radius, radius, npoints) / radius
dx = width * np.ones(npoints) * np.cos(np.radians(angle)) / npoints
dy = width * np.ones(npoints) * np.sin(np.radians(angle)) / npoints
dx += self.random.normal(0, self.jitter, npoints) * 1 * r**2.5
dy += self.random.normal(0, self.jitter, npoints) * 1 * r**2.5
dx[0] += inner_radius * np.cos(np.radians(angle))
dy[0] += inner_radius * np.sin(np.radians(angle))
dx = dx.cumsum()
dy = dy.cumsum()
return zip(x0 + dx, y0 + dy)
def lines(self, num, center=(0, 0), outer=1.0, inner=0):
for angle in np.linspace(0, 360, num):
yield self.filament(center, angle, outer, inner)
def mound(self, center, radius, num):
symmetry = self.random.randint(1, 4)
start = self.random.random_sample() * 2 * np.pi
colors = [self.center_light, self.center_dark]
cmap = mcolors.LinearSegmentedColormap.from_list('', colors)
n = int(np.ceil(float(num) / (symmetry * 2)))
vals = self.random.normal(0, 3, n).cumsum()
vals = np.r_[vals, vals[::-1]]
vals = np.concatenate(symmetry * [vals])
theta = np.linspace(start, start + 2 * np.pi, vals.size)
rvals = (vals - vals.min()) / vals.ptp()
r = radius * (np.ones(vals.size) + 0.05 * rvals)
fwidth = 0.3 * radius
x0, y0 = center
rays = []
fringes = []
for rad, angle in zip(r, theta):
x = rad * np.cos(angle)
y = rad * np.sin(angle)
rays.append([(x0, y0), (x + x0, y + y0)])
fil = self.filament(center, np.degrees(angle), rad + fwidth, rad)
fringes.append(fil)
ccol = LineCollection(rays, array=vals, cmap=cmap, linewidths=5*self.lw,
zorder=4)
fcol = LineCollection(fringes, linewidths=self.lw,
color=self.fringe_color)
return ccol, fcol
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment