110 lines
3.5 KiB
Python
110 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Create a layout-only guide image for sprite sheet generation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
from PIL import Image, ImageDraw
|
|
|
|
|
|
def draw_dashed_line(
|
|
draw: ImageDraw.ImageDraw,
|
|
start: tuple[int, int],
|
|
end: tuple[int, int],
|
|
*,
|
|
fill: str,
|
|
width: int,
|
|
dash: int,
|
|
gap: int,
|
|
) -> None:
|
|
x1, y1 = start
|
|
x2, y2 = end
|
|
if x1 == x2:
|
|
for y in range(min(y1, y2), max(y1, y2), dash + gap):
|
|
draw.line((x1, y, x2, min(y + dash, max(y1, y2))), fill=fill, width=width)
|
|
return
|
|
if y1 == y2:
|
|
for x in range(min(x1, x2), max(x1, x2), dash + gap):
|
|
draw.line((x, y1, min(x + dash, max(x1, x2)), y2), fill=fill, width=width)
|
|
return
|
|
raise ValueError("draw_dashed_line only supports horizontal or vertical lines")
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--rows", type=int, required=True)
|
|
parser.add_argument("--cols", type=int, required=True)
|
|
parser.add_argument("--cell-width", type=int, default=384)
|
|
parser.add_argument("--cell-height", type=int, default=384)
|
|
parser.add_argument("--safe-margin-x", type=int, default=52)
|
|
parser.add_argument("--safe-margin-y", type=int, default=52)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument(
|
|
"--label-cells",
|
|
action="store_true",
|
|
help="Draw small row,column labels. Leave off for normal imagegen references.",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
if args.rows <= 0 or args.cols <= 0:
|
|
raise SystemExit("--rows and --cols must be positive")
|
|
if args.cell_width <= 0 or args.cell_height <= 0:
|
|
raise SystemExit("--cell-width and --cell-height must be positive")
|
|
|
|
width = args.cols * args.cell_width
|
|
height = args.rows * args.cell_height
|
|
image = Image.new("RGB", (width, height), "#f8f8f8")
|
|
draw = ImageDraw.Draw(image)
|
|
|
|
for row in range(args.rows):
|
|
for col in range(args.cols):
|
|
left = col * args.cell_width
|
|
top = row * args.cell_height
|
|
right = left + args.cell_width - 1
|
|
bottom = top + args.cell_height - 1
|
|
safe_left = left + args.safe_margin_x
|
|
safe_top = top + args.safe_margin_y
|
|
safe_right = right - args.safe_margin_x
|
|
safe_bottom = bottom - args.safe_margin_y
|
|
|
|
draw.rectangle((left, top, right, bottom), outline="#111111", width=4)
|
|
draw.rectangle(
|
|
(safe_left, safe_top, safe_right, safe_bottom), outline="#2f80ed", width=3
|
|
)
|
|
|
|
center_x = left + args.cell_width // 2
|
|
center_y = top + args.cell_height // 2
|
|
draw_dashed_line(
|
|
draw,
|
|
(center_x, safe_top),
|
|
(center_x, safe_bottom),
|
|
fill="#b8b8b8",
|
|
width=2,
|
|
dash=14,
|
|
gap=16,
|
|
)
|
|
draw_dashed_line(
|
|
draw,
|
|
(safe_left, center_y),
|
|
(safe_right, center_y),
|
|
fill="#b8b8b8",
|
|
width=2,
|
|
dash=14,
|
|
gap=16,
|
|
)
|
|
|
|
if args.label_cells:
|
|
draw.text((left + 12, top + 10), f"{row + 1},{col + 1}", fill="#777777")
|
|
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
image.save(args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|