landing/art/icons.py
Module icons: one 512 px tile per atelier module, each drawn with the module it stands for.
Render it yourself: atelier render icons.py --preview (it imports _common.py for the palette and output folder).
# Module icons: one 512 px tile per atelier module, each drawn with the module it stands for.
import numpy as np
from atelier import *
from _common import *
S = 512
NAMES = ["core", "field", "vector", "brush", "fx", "sdf", "cli", "pt", "stylize", "nature", "figure", "inspect"]
c = Canvas(S * len(NAMES), S)
def tile(i):
"""Rounded tile with a soft top-lit gradient and a hairline rim; returns (x0, shape)."""
x0 = i * S
t = rect(x0 + 24, 24, S - 48, S - 48, r=112)
c.draw(t, fill=gradient([(0, "#24211b"), (1, "#121110")]).within(t))
c.draw(t.outline(3), fill=gradient([(0, "#4a443a"), (1, "#1c1a16")]).within(t), opacity=0.9)
return x0, t
# --- core: three translucent stacked layers (isometric), blended like layers in a canvas
x0, t = tile(0)
L = c.layer(blend="screen")
for k, col in enumerate([TEAL, GOLD, VERMILION]):
cy = 330 - k * 74
q = polygon([(x0 + 256, cy - 70), (x0 + 400, cy), (x0 + 256, cy + 70), (x0 + 112, cy)])
L.draw(q, fill=gradient([(0, col), (1, "#2a2620")], angle=-30).within(q), opacity=0.9)
L.draw(q.outline(3), fill=PAPER, opacity=0.55)
c.layer()
# --- field: domain-warped noise disc, shaded like terrain
x0, t = tile(1)
f = domain_warp(fbm(1.6, 4, seed=3), 0.35, 1.5, seed=4)
d = circle(x0 + 256, 256, 150)
c.layer().fill(shade(f, color=f.ramp(["#0b1e3f", "#0f5e6e", TEAL, GOLD, "#fde9d0"]), strength=0.8,
light=(120, 40), ambient=0.35), mask=d)
c.layer(blend="screen", opacity=0.5).fill("#fff2d8", mask=(1 - np.abs(np.sin(f * np.pi * 10))) ** 20 * d)
c.layer()
c.draw(d.outline(3), fill=PAPER, opacity=0.35)
# --- vector: a cubic bézier with handles and anchors
x0, t = tile(2)
p0, p1, p2, p3 = (x0 + 120, 360), (x0 + 150, 120), (x0 + 360, 400), (x0 + 392, 150)
c.path(f"M {p0[0]} {p0[1]} C {p1[0]} {p1[1]} {p2[0]} {p2[1]} {p3[0]} {p3[1]}", fill=None, stroke=PAPER, width=14)
for a, b in [(p0, p1), (p3, p2)]:
c.line(*a, *b, stroke=GOLD, width=4, dash=(10, 8))
c.circle(*b, 14, fill=INK2, stroke=GOLD, width=5)
for p in (p0, p3):
c.rect(p[0] - 18, p[1] - 18, 36, 36, r=6, fill=VERMILION)
# --- brush: an oil stroke over a watercolour wash
x0, t = tile(3)
c.layer().stroke([(x0 + 150, 330), (x0 + 250, 300), (x0 + 360, 220)], "watercolor", size=110,
color=TEAL, opacity=0.8, seed=5)
oil = c.layer()
oil.stroke([(x0 + 120, 360), (x0 + 200, 250), (x0 + 300, 210), (x0 + 400, 140)], "oil", size=60,
color=VERMILION, taper=(0.2, 0.6), seed=7, impasto=1.0)
oil.relief = 0.9
c.layer().stroke([(x0 + 130, 395), (x0 + 260, 375), (x0 + 390, 330)], "ink", size=10, color=PAPER, taper=(0.3, 0.9), seed=2)
c.layer()
c.erase(1 - t)
# --- fx: a blooming sun with chromatic rings over a halftone gradient
x0, t = tile(4)
ht = c.layer(blend="screen")
ht.fill(Field(lambda x, y: np.clip(1.0 - y * 1.5, 0, 1) ** 1.4), mask=t)
ht.apply(halftone(16, 30, ink="#000000", paper="#7a3f8a"))
ht.erase(1 - t)
sun = c.layer()
sun.circle(x0 + 256, 236, 80, fill="#fff3d6")
sun.apply(glow(40, 1.4, GOLD), bloom(0.5, 24, 1.0, tint=VERMILION))
sun.erase(1 - t)
c.layer()
for r, col in [(118, VERMILION), (126, TEAL)]:
c.draw(circle(x0 + 256, 236, r).outline(3), fill=col, opacity=0.7)
# --- sdf: raymarched gold torus around a glossy sphere (rendered with alpha)
x0, t = tile(5)
s = Scene()
s.add(torus(0.62, 0.16).rotate(x=62, z=-18), Mat(GOLD, rough=0.22, metal=1))
s.add(sphere(0.36), Mat(VERMILION, rough=0.18, sss=0.25))
s.area(pos=(-2, 2.5, 2.5), size=(2, 2), normal=(0.6, -0.7, -0.6), color="#fff1dc", power=30)
s.sky("#2a3550", "#d9b48a", "#16120e", power=0.6)
s.camera((0, 0.35, 3.1), (0, 0, 0), fov=40)
sub = Canvas(S, S)
sub.render(s, aa=2, alpha=True)
c.place(sub.flatten(), x=x0 + 256, y=262, scale=S * 0.92 / sub.W)
# --- cli: a terminal window with a prompt
x0, t = tile(6)
win = rect(x0 + 92, 140, 328, 232, r=26)
c.draw(win, fill="#0b0a09")
c.draw(win.outline(3), fill="#4a443a")
for k, col in enumerate([VERMILION, GOLD, TEAL]):
c.circle(x0 + 124 + k * 26, 170, 8, fill=col)
c.text(">", x0 + 120, 262, 54, font="mono", fill=GOLD, weight="bold")
c.text("render", x0 + 168, 258, 40, font="mono", fill=PAPER)
c.rect(x0 + 120, 300, 26, 40, fill=TEAL)
c.rect(x0 + 160, 316, 120, 10, r=5, fill="#4a443a")
def inset(i, img):
"""Paste a sub-render into tile i, clipped to the rounded tile."""
x0, t = tile(i)
c.place(img, x=x0 + 256, y=256, scale=S / img.shape[1])
c.erase(1 - t)
c.layer()
return x0, t
# --- pt: path-traced glass sphere and brass bead with real bounce light (alpha)
x0, t = tile(7)
s = Scene()
stripes = Field(lambda x, y: (np.sin(x * 40) > 0) * 1.0).ramp([TEAL, GOLD]) # a floor the glass can refract
s.add(sphere(0.42).move(-0.12, 0, 0.1), Mat("#f2fbf8", glass=1, rough=0, ior=1.5))
s.add(sphere(0.24).move(0.48, -0.18, -0.45), Mat(VERMILION, rough=0.3, sss=0.3))
s.add(plane(-0.42), Mat(stripes, rough=0.6, proj="xz", tex_scale=0.5))
s.add(plane(-1.4, (0, 0, 1)), Mat("#3a3128", rough=0.9))
s.area(pos=(-1.6, 2.0, 2.0), size=(1.6, 1.6), normal=(0.6, -0.7, -0.6), color="#fff1dc", power=60, pattern="window", visible=True)
s.sky("#2a3040", "#c9a27a", "#141210", power=0.9)
s.camera((0, 0.75, 2.4), (0.05, -0.12, 0), fov=40)
sub = Canvas(S, S)
sub.pathtrace(s, spp=48, bounces=6, seed=SEED)
sub.apply(vignette(0.5))
inset(7, sub.flatten())
# --- stylize: the same kind of 3D model, inked as a comic panel
s = Scene()
s.add(cylinder(0.32, 1.1).move(0, 0.05, 0), Mat(PAPER, rough=0.6))
s.add(cone(0.38, 0.32).move(0, 0.76, 0), Mat(VERMILION, rough=0.6))
s.add(box(0.7, 0.12, 0.7).move(0, -0.56, 0), Mat(GOLD, rough=0.7))
s.sun((-1.2, 1.6, 1.4), power=3)
s.sky("#2fa39a", "#9ad6cf", "#2a2620")
s.camera((0.9, 0.6, 2.8), (0, 0.15, 0), fov=40)
sub = Canvas(S, S, bg=TEAL)
r = sub.render(s, alpha=True)
sub.cur.apply(toon(r, bands=3, shadow=0.45), shadow_dots(r, size=6, lo=0.3), outlines(r, width=5, inner=2))
inset(8, sub.flatten())
# --- nature: physical dusk sky over an oak and grass
sub = Canvas(S, S)
sub.sky(sun_elev=4, sun_az=-20, horizon=0.68, clouds=0.25, seed=2)
sub.tree(270, 430, 290, kind="oak", season="autumn", seed=4, light=(160, 20))
sub.grass(256, 470, width=512, height=60, n=160, seed=3)
inset(9, sub.flatten())
# --- figure: a flat character waving, with a dog
x0, t = tile(10)
c.circle(x0 + 256, 236, 150, fill="#22201b")
c.figure(x0 + 215, 430, 330, pose="wave", style="flat", seed=6, hair="bun",
colors={"top": VERMILION, "bottom": NAVY, "hair": "#2a1a12", "shoes": "#111111"})
c.figure(x0 + 355, 430, 90, kind="dog", pose="sit", style="flat", seed=2, colors={"body": GOLD})
c.erase(1 - t)
c.layer()
# --- inspect: a thumbnail with zoom windows, a magnifier and a histogram
x0, t = tile(11)
thumb = rect(x0 + 104, 128, 304, 200, r=14)
c.draw(thumb, fill=gradient(["#0f5e6e", GOLD, VERMILION], angle=20).within(thumb))
for (bx, by) in [(150, 170), (290, 230)]:
c.rect(x0 + bx, by, 64, 52, fill=None, stroke="#ffe08a", width=5)
c.circle(x0 + 322, 256, 62, fill=None, stroke=PAPER, width=12)
c.line(x0 + 366, 300, x0 + 418, 352, stroke=PAPER, width=20, cap="round")
for k, hgt in enumerate([18, 34, 52, 76, 64, 44, 30, 22, 14]):
c.rect(x0 + 112 + k * 22, 410 - hgt, 16, hgt, r=3, fill=TEAL if k < 6 else VERMILION)
c.apply(grain(0.012, seed=SEED))
save_cells(c, [f"icon-{n}.png" for n in NAMES], S)