"""Launcher icon studies: the Asheron's Call ring motif, made acdream's own.""" import numpy as np from PIL import Image, ImageFilter, ImageChops import render, ring, compose, smooth def portal_swirl(size=1024, arms=2.0, twist=2.9, core=(0.92, 0.95, 1.00), mid=(0.36, 0.52, 0.95), outer=(0.30, 0.14, 0.52)): """Procedural log-spiral vortex -- AC's other signature image.""" y, x = np.mgrid[0:size, 0:size].astype(np.float32) x = (x / (size - 1)) * 2 - 1 y = (y / (size - 1)) * 2 - 1 r = np.sqrt(x * x + y * y) th = np.arctan2(y, x) safe = np.clip(r, 1e-3, None) spiral = np.sin(arms * th + twist * np.log(safe) * 3.2) spiral = (spiral * 0.5 + 0.5) ** 1.15 # gentler falloff + a much wider core, so the vortex fills the ring rather # than sitting in the middle of it as a speck falloff = np.clip(1.0 - r / 1.05, 0, 1) ** 0.85 corebloom = np.exp(-(r ** 2) / 0.075) t = np.clip(r / 0.9, 0, 1)[..., None] ramp = (np.array(mid, dtype=np.float32)[None, None, :] * (1 - t) + np.array(outer, dtype=np.float32)[None, None, :] * t) rgb = ramp * (0.42 + 1.25 * spiral)[..., None] * falloff[..., None] rgb = rgb + np.array(core, dtype=np.float32)[None, None, :] * corebloom[..., None] a = np.clip(falloff * (0.55 + 0.95 * spiral) + corebloom, 0, 1) img = np.concatenate([np.clip(rgb, 0, 1), a[..., None]], axis=2) return Image.fromarray((img * 255).astype(np.uint8), "RGBA") def inner_shadow(subject_alpha, size, blur=22, opacity=0.6, dy=10): """Soft shadow cast by the ring onto whatever sits inside it.""" s = subject_alpha.filter(ImageFilter.GaussianBlur(blur)) s = ImageChops.offset(s, 0, dy) s = s.point(lambda v: int(v * opacity)) sh = Image.new("RGBA", (size, size), (0, 0, 0, 0)) sh.putalpha(s) return sh def render_ring(size=1024, matcap=None, gap_deg=26.0, el=0.0, exposure=1.0, gap_center_deg=90.0, R=1.0, r=0.135): mc = {"RING": matcap if matcap is not None else ring.chrome_matcap(768)} rp, rn, ruv, rt = ring.forged_ring(R=R, r=r, gap_deg=gap_deg, gap_center_deg=gap_center_deg) mesh = (rp, rn, ruv, rt, ["RING"] * len(rt)) return render.render(mesh, {}, size=size, ss=3, az=270, el=el, fov=30, fit=1.0, matcaps=mc, exposure=exposure) def render_hook(size=1024, matcap=None, exposure=1.0, scale=0.60): mc = {"RING": matcap if matcap is not None else ring.chrome_matcap(768)} gp, gn, guv, gt = ring.hook_glyph(scale=scale) mesh = (gp, gn, guv, gt, ["RING"] * len(gt)) return render.render(mesh, {}, size=size, ss=3, az=270, el=0, fov=30, fit=1.0, matcaps=mc, exposure=exposure) def place(canvas, sub, occupancy, offset=(0.0, 0.0)): """Scale `sub` to `occupancy` of the canvas and paste it centred + offset.""" size = canvas.size[0] bbox = sub.split()[3].getbbox() s = sub.crop(bbox) k = (size * occupancy) / max(s.width, s.height) s = s.resize((max(1, int(s.width * k)), max(1, int(s.height * k))), Image.LANCZOS) layer = Image.new("RGBA", canvas.size, (0, 0, 0, 0)) layer.paste(s, (int((size - s.width) / 2 + size * offset[0]), int((size - s.height) / 2 + size * offset[1])), s) return Image.alpha_composite(canvas, layer), layer