Spaces:
Runtime error
Runtime error
| from pathlib import Path | |
| from PIL import Image, ImageChops, ImageColor, ImageDraw | |
| FRAME_WIDTH = 32 | |
| FRAME_HEIGHT = 48 | |
| DIRECTIONS = ("down", "left", "right", "up") | |
| STEPS = (-1, 0, 1) | |
| OUTPUT_DIR = Path(__file__).parents[1] / "frontend" / "static" / "assets" / "player" | |
| OUTLINE = "#17202a" | |
| SKIN = "#e8aa78" | |
| SKIN_LIGHT = "#f4c18f" | |
| SHOE = "#252d36" | |
| WHITE = "#fff2c6" | |
| TINT_DARK = "#737373" | |
| TINT_MID = "#b8b8b8" | |
| TINT_LIGHT = "#f2f2f2" | |
| PACK_DARK = "#75551e" | |
| PACK_MID = "#bd8e2f" | |
| PACK_LIGHT = "#e8bd55" | |
| def rect(draw, box, fill, outline=None): | |
| draw.rectangle(box, fill=fill, outline=outline) | |
| def polygon(draw, points, fill, outline=None): | |
| draw.polygon(points, fill=fill) | |
| if outline: | |
| draw.line([*points, points[0]], fill=outline, width=1) | |
| def frame_origin(direction_index, step_index): | |
| return step_index * FRAME_WIDTH, direction_index * FRAME_HEIGHT | |
| def draw_body(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| if direction == "down": | |
| rect(draw, (ox + 9, y + 5, ox + 22, y + 18), SKIN, OUTLINE) | |
| rect(draw, (ox + 11, y + 6, ox + 20, y + 10), SKIN_LIGHT) | |
| rect(draw, (ox + 12, y + 19, ox + 19, y + 22), SKIN, OUTLINE) | |
| rect(draw, (ox + 11, y + 11, ox + 12, y + 12), OUTLINE) | |
| rect(draw, (ox + 19, y + 11, ox + 20, y + 12), OUTLINE) | |
| rect(draw, (ox + 14, y + 15, ox + 17, y + 15), "#9b523f") | |
| rect(draw, (ox + 4, y + 25, ox + 7, y + 31), SKIN, OUTLINE) | |
| rect(draw, (ox + 24, y + 25, ox + 27, y + 31), SKIN, OUTLINE) | |
| elif direction == "up": | |
| rect(draw, (ox + 9, y + 5, ox + 22, y + 18), SKIN, OUTLINE) | |
| rect(draw, (ox + 12, y + 19, ox + 19, y + 22), SKIN, OUTLINE) | |
| rect(draw, (ox + 4, y + 25, ox + 7, y + 31), SKIN, OUTLINE) | |
| rect(draw, (ox + 24, y + 25, ox + 27, y + 31), SKIN, OUTLINE) | |
| else: | |
| facing_right = direction == "right" | |
| rect(draw, (ox + 10, y + 5, ox + 21, y + 18), SKIN, OUTLINE) | |
| nose_x = ox + (22 if facing_right else 9) | |
| rect(draw, (nose_x, y + 11, nose_x, y + 13), SKIN_LIGHT, OUTLINE) | |
| eye_x = ox + (19 if facing_right else 11) | |
| rect(draw, (eye_x, y + 10, eye_x + 1, y + 11), OUTLINE) | |
| rect(draw, (ox + 12, y + 19, ox + 18, y + 22), SKIN, OUTLINE) | |
| hand_x = ox + (23 if facing_right else 6) | |
| rect(draw, (hand_x, y + 26, hand_x + 3, y + 31), SKIN, OUTLINE) | |
| left_lift = 1 if step < 0 else 0 | |
| right_lift = 1 if step > 0 else 0 | |
| rect(draw, (ox + 8, y + 42 - left_lift, ox + 14, y + 46 - left_lift), SHOE, OUTLINE) | |
| rect(draw, (ox + 17, y + 42 - right_lift, ox + 23, y + 46 - right_lift), SHOE, OUTLINE) | |
| def draw_gear_back(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| if direction == "up": | |
| rect(draw, (ox + 7, y + 20, ox + 24, y + 35), PACK_DARK, OUTLINE) | |
| rect(draw, (ox + 9, y + 21, ox + 22, y + 31), PACK_MID) | |
| rect(draw, (ox + 11, y + 22, ox + 20, y + 24), PACK_LIGHT) | |
| elif direction == "left": | |
| rect(draw, (ox + 18, y + 21, ox + 26, y + 35), PACK_DARK, OUTLINE) | |
| rect(draw, (ox + 20, y + 23, ox + 25, y + 31), PACK_MID) | |
| elif direction == "right": | |
| rect(draw, (ox + 5, y + 21, ox + 13, y + 35), PACK_DARK, OUTLINE) | |
| rect(draw, (ox + 6, y + 23, ox + 11, y + 31), PACK_MID) | |
| else: | |
| rect(draw, (ox + 21, y + 22, ox + 27, y + 34), PACK_DARK, OUTLINE) | |
| rect(draw, (ox + 22, y + 24, ox + 26, y + 31), PACK_MID) | |
| def draw_pants(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| left_lift = 1 if step < 0 else 0 | |
| right_lift = 1 if step > 0 else 0 | |
| rect(draw, (ox + 9, y + 34, ox + 22, y + 39), TINT_DARK, OUTLINE) | |
| rect(draw, (ox + 9, y + 35, ox + 14, y + 43 - left_lift), TINT_MID, OUTLINE) | |
| rect(draw, (ox + 17, y + 35, ox + 22, y + 43 - right_lift), TINT_MID, OUTLINE) | |
| if direction in ("left", "right"): | |
| rect(draw, (ox + 16, y + 36, ox + 22, y + 42 - right_lift), TINT_DARK) | |
| else: | |
| rect(draw, (ox + 10, y + 36, ox + 11, y + 41 - left_lift), TINT_LIGHT) | |
| rect(draw, (ox + 18, y + 36, ox + 19, y + 41 - right_lift), TINT_LIGHT) | |
| def draw_shirt(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| arm_shift = step | |
| rect(draw, (ox + 9, y + 21, ox + 22, y + 35), TINT_MID, OUTLINE) | |
| rect(draw, (ox + 10, y + 22, ox + 12, y + 33), TINT_LIGHT) | |
| if direction == "up": | |
| rect(draw, (ox + 8, y + 22, ox + 10, y + 33), TINT_DARK, OUTLINE) | |
| rect(draw, (ox + 21, y + 22, ox + 23, y + 33), TINT_DARK, OUTLINE) | |
| elif direction == "left": | |
| polygon(draw, [(ox + 10, y + 22), (ox + 7, y + 24 + arm_shift), (ox + 6, y + 31 + arm_shift), (ox + 9, y + 32), (ox + 12, y + 25)], TINT_MID, OUTLINE) | |
| polygon(draw, [(ox + 21, y + 22), (ox + 24, y + 25 - arm_shift), (ox + 23, y + 31 - arm_shift), (ox + 20, y + 30)], TINT_DARK, OUTLINE) | |
| elif direction == "right": | |
| polygon(draw, [(ox + 21, y + 22), (ox + 24, y + 24 - arm_shift), (ox + 25, y + 31 - arm_shift), (ox + 22, y + 32), (ox + 19, y + 25)], TINT_MID, OUTLINE) | |
| polygon(draw, [(ox + 10, y + 22), (ox + 7, y + 25 + arm_shift), (ox + 8, y + 31 + arm_shift), (ox + 11, y + 30)], TINT_DARK, OUTLINE) | |
| else: | |
| polygon(draw, [(ox + 9, y + 22), (ox + 5, y + 24 + arm_shift), (ox + 5, y + 30 + arm_shift), (ox + 8, y + 31), (ox + 11, y + 25)], TINT_MID, OUTLINE) | |
| polygon(draw, [(ox + 22, y + 22), (ox + 26, y + 24 - arm_shift), (ox + 26, y + 30 - arm_shift), (ox + 23, y + 31), (ox + 20, y + 25)], TINT_DARK, OUTLINE) | |
| polygon(draw, [(ox + 13, y + 21), (ox + 16, y + 25), (ox + 19, y + 21)], WHITE, OUTLINE) | |
| def draw_hair(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| if direction == "up": | |
| rect(draw, (ox + 8, y + 4, ox + 23, y + 16), TINT_DARK, OUTLINE) | |
| rect(draw, (ox + 10, y + 5, ox + 20, y + 8), TINT_LIGHT) | |
| rect(draw, (ox + 8, y + 13, ox + 10, y + 19), TINT_DARK, OUTLINE) | |
| rect(draw, (ox + 21, y + 13, ox + 23, y + 19), TINT_DARK, OUTLINE) | |
| elif direction == "left": | |
| polygon(draw, [(ox + 9, y + 5), (ox + 21, y + 4), (ox + 23, y + 8), (ox + 21, y + 12), (ox + 18, y + 9), (ox + 9, y + 10)], TINT_MID, OUTLINE) | |
| rect(draw, (ox + 18, y + 6, ox + 21, y + 8), TINT_LIGHT) | |
| rect(draw, (ox + 20, y + 13, ox + 22, y + 19), TINT_DARK, OUTLINE) | |
| elif direction == "right": | |
| polygon(draw, [(ox + 22, y + 5), (ox + 10, y + 4), (ox + 8, y + 8), (ox + 10, y + 12), (ox + 13, y + 9), (ox + 22, y + 10)], TINT_MID, OUTLINE) | |
| rect(draw, (ox + 10, y + 6, ox + 13, y + 8), TINT_LIGHT) | |
| rect(draw, (ox + 9, y + 13, ox + 11, y + 19), TINT_DARK, OUTLINE) | |
| else: | |
| polygon(draw, [(ox + 8, y + 10), (ox + 10, y + 5), (ox + 14, y + 3), (ox + 21, y + 5), (ox + 23, y + 9), (ox + 21, y + 11), (ox + 18, y + 8), (ox + 15, y + 11), (ox + 12, y + 8), (ox + 10, y + 13)], TINT_MID, OUTLINE) | |
| rect(draw, (ox + 12, y + 5, ox + 18, y + 6), TINT_LIGHT) | |
| rect(draw, (ox + 9, y + 12, ox + 11, y + 19), TINT_DARK, OUTLINE) | |
| rect(draw, (ox + 20, y + 11, ox + 22, y + 19), TINT_DARK, OUTLINE) | |
| def draw_gear_front(draw, ox, oy, direction, step): | |
| bob = 1 if step else 0 | |
| y = oy + bob | |
| if direction == "down": | |
| rect(draw, (ox + 21, y + 23, ox + 23, y + 32), PACK_DARK, OUTLINE) | |
| rect(draw, (ox + 22, y + 25, ox + 23, y + 27), PACK_LIGHT) | |
| elif direction == "up": | |
| rect(draw, (ox + 10, y + 22, ox + 11, y + 30), PACK_LIGHT) | |
| rect(draw, (ox + 20, y + 22, ox + 21, y + 30), PACK_LIGHT) | |
| elif direction == "left": | |
| rect(draw, (ox + 18, y + 22, ox + 19, y + 31), PACK_LIGHT) | |
| else: | |
| rect(draw, (ox + 12, y + 22, ox + 13, y + 31), PACK_LIGHT) | |
| DRAWERS = { | |
| "body": draw_body, | |
| "gear-back": draw_gear_back, | |
| "pants": draw_pants, | |
| "shirt": draw_shirt, | |
| "hair": draw_hair, | |
| "gear-front": draw_gear_front, | |
| } | |
| PALETTES = { | |
| "shirt": { | |
| "red": "#d94b48", "blue": "#3f71b5", "green": "#4d8d59", | |
| "yellow": "#e1b840", "purple": "#765495", "teal": "#398b87", | |
| }, | |
| "pants": { | |
| "navy": "#263b60", "charcoal": "#3d4148", "brown": "#6f4d3b", | |
| "olive": "#5d673f", "blue": "#365f86", "plum": "#61405d", | |
| }, | |
| "hair": { | |
| "black": "#1d2025", "dark_brown": "#3b271f", "brown": "#70462c", | |
| "blond": "#c99a45", "auburn": "#8a3f2c", | |
| }, | |
| } | |
| def tint_atlas(atlas, color): | |
| solid = Image.new("RGB", atlas.size, ImageColor.getrgb(color)) | |
| shaded = ImageChops.multiply(atlas.convert("RGB"), solid) | |
| return Image.merge("RGBA", (*shaded.split(), atlas.getchannel("A"))) | |
| def main(): | |
| OUTPUT_DIR.mkdir(parents=True, exist_ok=True) | |
| for name, drawer in DRAWERS.items(): | |
| atlas = Image.new("RGBA", (FRAME_WIDTH * 3, FRAME_HEIGHT * 4), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(atlas) | |
| for direction_index, direction in enumerate(DIRECTIONS): | |
| for step_index, step in enumerate(STEPS): | |
| drawer(draw, *frame_origin(direction_index, step_index), direction, step) | |
| if name in PALETTES: | |
| for color_name, color in PALETTES[name].items(): | |
| tint_atlas(atlas, color).save(OUTPUT_DIR / f"student-{name}-{color_name}.png", optimize=True) | |
| else: | |
| atlas.save(OUTPUT_DIR / f"student-{name}.png", optimize=True) | |
| if __name__ == "__main__": | |
| main() | |