"""Create original illustrative knit textures, not photographs of RRS products. Requires Pillow and NumPy. Generated assets are committed; runtime needs neither. """ from pathlib import Path import math import random import numpy as np from PIL import Image, ImageDraw, ImageFilter OUTPUT = Path(__file__).resolve().parents[1] / 'public' / 'images' OUTPUT.mkdir(parents=True, exist_ok=True) def texture(name, spacing, base, seed): rng = random.Random(seed) width, height = 1600, 1200 image = Image.new('RGB', (width, height), tuple(round(c * .7) for c in base)) draw = ImageDraw.Draw(image) for row in range(-2, round(height / (spacing * .72)) + 3): for column in range(-2, width // spacing + 3): x = column * spacing + math.sin(row * .16) * spacing * .16 y = row * spacing * .72 tone = rng.uniform(.91, 1.05) for side in (-1, 1): points = [] for step in range(35): t = step / 34 px = x + side * spacing * (.04 + .43 * (1 - t) + .07 * math.sin(t * math.pi)) py = y + spacing * (t * .95 - .15) points.append((px, py)) draw.line([(px + 3, py + 4) for px, py in points], fill=tuple(round(c * .55) for c in base), width=round(spacing * .36)) draw.line(points, fill=tuple(min(255, round(c * tone * .84)) for c in base), width=round(spacing * .32)) for strand in range(11): offset = (strand - 5) * spacing * .027 fibre = [(px + offset + math.sin(i * .9 + strand) * .7, py) for i, (px, py) in enumerate(points)] light = tone * (.78 + .24 * math.sin(strand * .28)) draw.line(fibre, fill=tuple(min(255, round(c * light)) for c in base), width=max(1, spacing // 32)) pixels = np.asarray(image).astype(float) yy, xx = np.mgrid[0:height, 0:width] folds = .91 + .14 * np.sin(xx / 210 + yy / 380) + .05 * np.sin(xx / 82 - yy / 230) noise = np.random.default_rng(seed).normal(0, 2.1, (height, width)) pixels = np.clip(pixels * folds[:, :, None] + noise[:, :, None], 0, 255).astype('uint8') Image.fromarray(pixels).filter(ImageFilter.GaussianBlur(.3)).save(OUTPUT / name, quality=90, optimize=True) texture('knit-natural.jpg', 70, (218, 205, 181), 21) texture('knit-fine.jpg', 27, (221, 214, 197), 42) texture('knit-structured.jpg', 108, (180, 170, 147), 63)