gallery.py

At 1ae6528

1ae6528History

Preview

Code · 3.9 KiB
#!/usr/bin/env python3
"""Render a labeled silhouette library and four curated glass compositions."""
from pathlib import Path
import argparse
from PIL import Image, ImageDraw, ImageFont
from shapes import SHAPES,shape
from geometry import curve_points
from renderer import render

ROOT=Path(__file__).parent

def glass(role='middle',warmth=0.,hue=0.,opacity=.93):
    return dict(role=role,warmth=warmth,hue=hue,opacity=opacity)

def piece(name,center,radius,rotation,role):
    return dict(shape=name,center=center,radius=radius,rotation=rotation,material=glass(role))

STUDIES={
    'heart':[piece('heart',(486,600),400,0,'middle')],
    'soft-forms':[
        piece('cloud',(300,350),240,0,'outer'),
        piece('bubbles',(588,580),305,.24,'middle'),
        piece('pebble',(365,864),240,-.45,'front')],
    'cutouts':[
        piece('seaweed',(305,365),252,-.3,'outer'),
        piece('coral',(604,554),325,.3,'middle'),
        piece('bow',(350,835),294,-.7,'front')],
    'celestial':[
        piece('crescent',(322,350),260,-.22,'outer'),
        piece('star',(594,559),326,.21,'middle'),
        piece('sparkle',(377,845),282,-.19,'front')],
    'orchard':[
        piece('apple',(337,395),223,-.18,'outer'),
        piece('strawberry',(614,563),316,.20,'middle'),
        piece('lemon',(355,826),277,-.48,'front')]
}

def font(size):
    # Pillow's bundled font keeps the sheet portable across operating systems.
    return ImageFont.load_default(size=size)

def sampler(outlines=False,names=None):
    cellw,cellh=360,420
    catalog=SHAPES if names is None else {name:SHAPES[name] for name in names}
    columns=4 if names is None else 2
    height=180+cellh*((len(catalog)+columns-1)//columns)
    sheet=Image.new('RGB',(cellw*columns,height),(241,235,232));draw=ImageDraw.Draw(sheet)
    draw.text((48,36),'GLASS / REFINED SHAPES' if names else ('CONTOUR / SHAPE LIBRARY' if outlines else 'GLASS / SHAPE LIBRARY'),font=font(30),fill=(46,42,55))
    draw.text((48,83),'New heart silhouette · butterfly · rounded bow · strawberry' if names else ('16 continuous contours · no shading or texture' if outlines else '16 silhouettes · one iridescent material · generated from curves'),font=font(17),fill=(104,97,110))
    for i,(name,label) in enumerate(catalog.items()):
        if outlines:
            tile=Image.new('RGB',(round(972*.34)*4,round(1202*.34)*4),(241,235,232))
            ImageDraw.Draw(tile).polygon([tuple(p*.34*4) for p in curve_points(shape(name,(486,565),370))],fill=(42,37,50))
            tile=tile.resize((round(972*.34),round(1202*.34)),Image.Resampling.LANCZOS)
        else:
            tile=render(scale=.34,specs=[piece(name,(486,565),370,0,'middle')])
        tile=tile.crop((0,10,tile.width,355))
        x=(i%columns)*cellw; y=142+(i//columns)*cellh
        sheet.paste(tile,(x+(cellw-tile.width)//2,y))
        draw.text((x+28,y+357),label,font=font(20),fill=(46,42,55))
        draw.text((x+28,y+385),name,font=font(14),fill=(112,103,117))
    return sheet

def main():
    ap=argparse.ArgumentParser(description=__doc__)
    ap.add_argument('--output-dir',type=Path,default=ROOT/'renders')
    ap.add_argument('--only',choices=['sampler','outlines','refinements',*STUDIES],help='Render only one asset')
    args=ap.parse_args();args.output_dir.mkdir(parents=True,exist_ok=True)
    if args.only in (None,'sampler'):
        path=args.output_dir/'shape-library.png';sampler().save(path);print(path)
    if args.only in (None,'outlines'):
        path=args.output_dir/'shape-outlines.png';sampler(outlines=True).save(path);print(path)
    if args.only in (None,'refinements'):
        path=args.output_dir/'shape-refinements.png'
        sampler(names=['heart','butterfly','bow','strawberry']).save(path);print(path)
    for name,specs in STUDIES.items():
        if args.only in (None,name):
            path=args.output_dir/f'shape-study-{name}.png'
            render(specs=specs).save(path);print(path)
if __name__=='__main__':main()

Files