showcase.py

3.6 kB · python · 121 lines

1import sys23import lib  # noqa: F40145from PIL import Image, ImageDraw, ImageFont6from mrlypy.six import designs7from mrlypy.six.geometry import radial8from mrlypy.six.renderer import draw9from mrlypy.two import designs as designs_2d10from mrlypy.two.renderer import to_image1112from lib.canvas import H3, INK, PAPER, quantize13from lib.gif import write_gif14from lib.paths import GIFS, ensure, show15from lib.terminal import menu1617NUMBERS = [3, 5, 7]18LEVEL = 219RADIUS = 220SCALE = 1021CANVAS = 81022COLORS = 6423STRIP = 11024FAMILIES = ["carpet", "net", "tree", "void"]25VIEWS = ["flat", "iso", "pro", "cut"]2627# FRAME2829def label(canvas, family):30    try:31        font = ImageFont.load_default(48)32    except TypeError:33        font = ImageFont.load_default()34    ImageDraw.Draw(canvas).text((30, CANVAS - 78), family, fill=INK.to_rgb(), font=font)3536def flat_frame(family, number):37    cell = getattr(designs_2d, f"{family}_2d")(number, LEVEL).paint()38    image = to_image(cell, max(1, (CANVAS - STRIP) // cell.height))39    canvas = Image.new("RGB", (CANVAS, CANVAS), PAPER.to_rgb())40    canvas.paste(image, ((CANVAS - image.width) // 2, (CANVAS - STRIP - image.height) // 2), image)41    label(canvas, family)42    return quantize(canvas, COLORS)4344def frame(family, projection, number):45    if projection == "flat":46        return flat_frame(family, number)47    cell = getattr(designs, f"{family}_{projection}")(number, LEVEL).paint()48    sheet = radial(cell, RADIUS)49    image = draw(sheet, scale=SCALE, start=1 - cell.start)50    w, h = image.size51    if cell.orientation == "horizontal":52        image = image.resize((w, round(h * H3)), Image.LANCZOS)53    else:54        image = image.resize((round(w * H3), h), Image.LANCZOS)55    image.thumbnail((CANVAS, CANVAS), Image.LANCZOS)56    canvas = Image.new("RGB", (CANVAS, CANVAS), PAPER.to_rgb())57    canvas.paste(image, ((CANVAS - image.width) // 2, (CANVAS - image.height) // 2), image)58    label(canvas, family)59    return quantize(canvas, COLORS)6061# SHEETS6263def sheets(projection, numbers):64    for number in numbers:65        frames = [frame(family, projection, number) for family in FAMILIES]66        path = GIFS / f"showcase-{projection}-{number}.gif"67        write_gif(path, frames)68        print(f"{show(path)} ({' '.join(FAMILIES)})")69    return 07071def flat(numbers):72    return sheets("flat", numbers)7374def iso(numbers):75    return sheets("iso", numbers)7677def pro(numbers):78    return sheets("pro", numbers)7980def cut(numbers):81    return sheets("cut", numbers)8283def every(numbers):84    for projection in VIEWS:85        sheets(projection, numbers)86    return 08788def pick(token):89    if not token.isdigit() or int(token) not in NUMBERS:90        sys.exit(f"'{token}' is not in the showcase: " + ", ".join(str(n) for n in NUMBERS))91    return [int(token)]9293# TERMINAL9495COMMANDS = {96    "all": (every, "every view at every number"),97    "flat": (flat, "the family rule in 2D, before the third axis"),98    "iso": (iso, "the whole sponge, seen isometrically"),99    "pro": (pro, "the three faces the sponge shows you"),100    "cut": (cut, "the diagonal slice through the middle"),101}102103def help():104    menu(f"showcase.py <command> <number>   {' '.join(FAMILIES)} at level {LEVEL}",105         COMMANDS,106         [str(number) for number in NUMBERS],107         "a command alone sweeps every number; add a number to draw just that one.")108109def terminal():110    match sys.argv[1:]:111        case [cmd] if cmd in COMMANDS:112            ensure()113            sys.exit(COMMANDS[cmd][0](NUMBERS) or 0)114        case [cmd, token] if cmd in COMMANDS:115            ensure()116            sys.exit(COMMANDS[cmd][0](pick(token)) or 0)117        case _:118            help()119120if __name__ == "__main__":121    terminal()