sponges.py

3.0 kB · python · 108 lines

1import math2import sys34import lib  # noqa: F40156from PIL import Image7from mrlypy.core.colors import alpha, black, gradient, white8from mrlypy.six import GRID, LEFT, RIGHT, UP9from mrlypy.six.designs import carpet_iso1011from lib.canvas import H3, PAPER, flatten, quantize12from lib.gif import write_gif13from lib.paths import EXTRAS, GIFS, ensure, show14from lib.terminal import menu, pick_level, pick_number1516SIZE = 108017SUPER = 318GIFH = 81019GIFW = round(GIFH * H3)20PAD = 2421GREYS = 1622NUMBERS = [1, 3, 5, 7, 9]23LEVEL = 124CUBE_CAP = 8 * 1024 ** 22526# SHADES2728SHADES = gradient([black, white], 4)  # 0, 85, 170, 255: black, two equal greys, paper29PALETTE = {UP: [SHADES[2]], LEFT: [SHADES[1]], RIGHT: [SHADES[0]], GRID: [alpha]}3031# SPONGE3233def cost(number, level):34    return number ** (3 * level)3536def sponge(number, level):37    weight = cost(number, level)38    if weight > CUBE_CAP:39        sys.exit("%d-%d is %d little cubes: mrlypy.six.iso walks every one of them"40                 % (number, level, weight))41    return carpet_iso(number, level).paint(PALETTE)4243# CANVAS4445def picture(cell, height):46    scale = max(1, math.ceil(SUPER * height / (cell.height + 2)))47    image = flatten(cell.draw(scale=scale)).convert("L")48    w, h = image.size49    image = image.resize((max(1, round(w * H3)), h), Image.LANCZOS)50    image.thumbnail((round(height * H3), height), Image.LANCZOS)51    return image5253# GIF5455def frame(cell):56    image = picture(cell, GIFH - 2 * PAD)57    canvas = Image.new("L", (GIFW, GIFH), PAPER.r)58    canvas.paste(image, ((GIFW - image.width) // 2, (GIFH - image.height) // 2))59    return quantize(canvas, GREYS)6061# RUN6263def draw_one(number, level):64    ensure()65    path = EXTRAS / ("sponge-%d-%d.png" % (number, level))66    picture(sponge(number, level), SIZE).save(path, optimize=True)67    print("%s (%d cubes across)" % (show(path), number ** level))68    return 06970def sweep(level):71    ensure()72    frames = []73    for number in NUMBERS:74        frames.append(frame(sponge(number, level)))75        print("sponge %d ready" % number)76    path = GIFS / ("sponges-%d.gif" % level)77    write_gif(path, frames)78    print("%s (%s)" % (show(path), " ".join(str(n) for n in NUMBERS)))79    return 08081# TERMINAL8283COMMANDS = {84    "sweep": (sweep, "every number, one frame each, as a gif"),85    "draw": (draw_one, "draw one NUMBER LEVEL"),86}8788def help():89    menu("sponges.py <command> <number> <level>   the sponges themselves, corner-on",90         COMMANDS,91         [str(number) for number in NUMBERS],92         f"'sweep' alone draws the lot at level {LEVEL}; add a level to go deeper.")9394def terminal():95    match sys.argv[1:]:96        case ["sweep"]:97            sys.exit(sweep(LEVEL) or 0)98        case ["sweep", level]:99            sys.exit(sweep(pick_level(level)) or 0)100        case ["draw", number]:101            sys.exit(draw_one(pick_number(number), LEVEL) or 0)102        case ["draw", number, level]:103            sys.exit(draw_one(pick_number(number), pick_level(level)) or 0)104        case _:105            help()106107if __name__ == "__main__":108    terminal()