RRS First version

This commit is contained in:
2026-09-12 03:08:23 -04:00
commit c050d25a84
41 changed files with 5575 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
"""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)