#!/usr/bin/env python3 """ Fractal Garden: Generative art exploring mathematical beauty. Creates evolving fractal patterns that feel organic and alive. Each run produces a unique piece based on the timestamp. """ import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap from datetime import datetime import os from pathlib import Path def create_custom_colormap(seed): """Create a unique colormap based on seed.""" np.random.seed(seed) # Generate colors with a coherent aesthetic base_hue = np.random.random() colors = [] for i in range(5): h = (base_hue + i * 0.15) % 1.0 s = 0.4 + np.random.random() * 0.4 v = 0.6 + np.random.random() * 0.3 # HSV to RGB conversion c = v * s x = c * (1 - abs((h * 6) % 2 - 1)) m = v - c if h < 1/6: r, g, b = c, x, 0 elif h < 2/6: r, g, b = x, c, 0 elif h < 3/6: r, g, b = 0, c, x elif h < 4/6: r, g, b = 0, x, c elif h < 5/6: r, g, b = x, 0, c else: r, g, b = c, 0, x colors.append((r + m, g + m, b + m)) return LinearSegmentedColormap.from_list('fractal', colors, N=256) def mandelbrot(h, w, x_center, y_center, zoom, max_iter=256): """Generate Mandelbrot set with custom center and zoom.""" x = np.linspace(x_center - 2/zoom, x_center + 2/zoom, w) y = np.linspace(y_center - 2/zoom, y_center + 2/zoom, h) X, Y = np.meshgrid(x, y) C = X + 1j * Y Z = np.zeros_like(C) M = np.zeros(C.shape) for i in range(max_iter): mask = np.abs(Z) <= 2 Z[mask] = Z[mask] ** 2 + C[mask] M[mask] = i return M def julia(h, w, c_real, c_imag, zoom=1.0, max_iter=256): """Generate Julia set for a given complex constant.""" x = np.linspace(-2/zoom, 2/zoom, w) y = np.linspace(-2/zoom, 2/zoom, h) X, Y = np.meshgrid(x, y) Z = X + 1j * Y c = complex(c_real, c_imag) M = np.zeros(Z.shape) for i in range(max_iter): mask = np.abs(Z) <= 2 Z[mask] = Z[mask] ** 2 + c M[mask] = i return M def burning_ship(h, w, x_center, y_center, zoom, max_iter=256): """Generate Burning Ship fractal.""" x = np.linspace(x_center - 2/zoom, x_center + 2/zoom, w) y = np.linspace(y_center - 2/zoom, y_center + 2/zoom, h) X, Y = np.meshgrid(x, y) C = X + 1j * Y Z = np.zeros_like(C) M = np.zeros(C.shape) for i in range(max_iter): mask = np.abs(Z) <= 2 Z[mask] = (np.abs(Z[mask].real) + 1j * np.abs(Z[mask].imag)) ** 2 + C[mask] M[mask] = i return M def create_garden(seed=None, output_dir=None): """Create a fractal garden image.""" if seed is None: seed = int(datetime.now().timestamp()) np.random.seed(seed) # Determine output directory if output_dir is None: output_dir = Path(__file__).parent.parent / "art" output_dir = Path(output_dir) output_dir.mkdir(exist_ok=True) # Image parameters h, w = 1000, 1400 dpi = 100 # Choose fractal type fractal_type = np.random.choice(['mandelbrot', 'julia', 'burning_ship']) # Generate fractal based on type if fractal_type == 'mandelbrot': # Interesting regions of Mandelbrot regions = [ (-0.5, 0, 1), # Classic view (-0.75, 0.1, 4), # Sea horse valley (-1.25, 0.02, 10), # Elephant valley (-0.16, 1.0405, 50), # Deep zoom ] x_c, y_c, zoom = regions[np.random.randint(len(regions))] M = mandelbrot(h, w, x_c, y_c, zoom) title = f"Mandelbrot at ({x_c:.3f}, {y_c:.3f})" elif fractal_type == 'julia': # Interesting Julia set constants constants = [ (-0.8, 0.156), # Classic (-0.4, 0.6), # Dendrite (0.285, 0.01), # Island (-0.70176, -0.3842), # Dragon ] c_r, c_i = constants[np.random.randint(len(constants))] zoom = 1 + np.random.random() * 2 M = julia(h, w, c_r, c_i, zoom) title = f"Julia c=({c_r:.4f}, {c_i:.4f})" else: # burning_ship regions = [ (-0.5, -0.5, 1), (-1.755, -0.04, 20), ] x_c, y_c, zoom = regions[np.random.randint(len(regions))] M = burning_ship(h, w, x_c, y_c, zoom) title = f"Burning Ship at ({x_c:.3f}, {y_c:.3f})" # Create figure fig, ax = plt.subplots(figsize=(w/dpi, h/dpi), dpi=dpi) # Apply custom colormap cmap = create_custom_colormap(seed % 1000) # Normalize and apply log transform for better contrast M_normalized = np.log1p(M) # Plot ax.imshow(M_normalized, cmap=cmap, extent=[-2, 2, -2, 2]) ax.set_axis_off() # Add subtle title fig.text(0.02, 0.02, title, fontsize=8, color='white', alpha=0.5) fig.text(0.98, 0.02, f'seed: {seed}', fontsize=8, color='white', alpha=0.5, ha='right') plt.tight_layout(pad=0) # Save filename = f"fractal_{seed}.png" filepath = output_dir / filename plt.savefig(filepath, bbox_inches='tight', pad_inches=0, facecolor='black') plt.close() print(f"Created: {filepath}") return filepath def create_gallery(count=4, output_dir=None): """Create a gallery of fractal images.""" paths = [] base_seed = int(datetime.now().timestamp()) for i in range(count): path = create_garden(seed=base_seed + i, output_dir=output_dir) paths.append(path) print(f"\nGallery created with {count} images") return paths def main(): import sys if len(sys.argv) > 1 and sys.argv[1] == 'gallery': count = int(sys.argv[2]) if len(sys.argv) > 2 else 4 create_gallery(count) else: create_garden() if __name__ == "__main__": main()