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()