Coverage for src / puzzletree / cli / commands / tile / __init__.py: 100.00%
32 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-12 20:35 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-12 20:35 +0000
1from __future__ import annotations
3import logging
4from pathlib import Path
6from typer import Context, Exit, Option, Typer
8from puzzletree.cli.messages import error_panel, info_panel
9from puzzletree.reconstruct.io import save_tiles_from_image
10from puzzletree.utils.logging import get_logger_console
11from puzzletree.utils.progress_bar import StageProgressBar
13app = Typer(add_completion=True, no_args_is_help=True)
15DEFAULT_TILE_ROWS = 4
16DEFAULT_TILE_COLS = 5
19def _default_output_dir(input_image: Path) -> Path:
20 return Path.cwd() / f"puzzletree-{input_image.stem}-tiles"
23@app.callback(invoke_without_command=True, no_args_is_help=True)
24def tile(
25 ctx: Context,
26 input_image: Path = Option(..., "--input-image", "-i", help="Source image to split into tiles."),
27 output_dir: Path | None = Option(
28 None,
29 "--output-dir",
30 "-o",
31 help="Directory to write tile PNGs. Defaults to ./puzzletree-<image-stem>-tiles.",
32 ),
33 rows: int = Option(DEFAULT_TILE_ROWS, "--rows", help="Number of tile rows."),
34 cols: int = Option(DEFAULT_TILE_COLS, "--cols", help="Number of tile columns."),
35 prefix: str = Option("tile", "--prefix", help="Filename prefix for generated tiles."),
36 overwrite: bool = Option(
37 False,
38 "--overwrite",
39 help="Replace existing generated PNG tiles with the same prefix in the output directory.",
40 ),
41) -> None:
42 """Split an image into a regular grid of tiles."""
43 verbose = bool(ctx.obj.get("verbose", False)) if isinstance(ctx.obj, dict) else False
44 log_level = logging.DEBUG if verbose else logging.INFO
45 logger, console = get_logger_console("puzzletree.tile", log_level=log_level)
46 resolved_output_dir = output_dir if output_dir is not None else _default_output_dir(input_image)
48 logger.debug(
49 "Tile command options: input_image=%s output_dir=%s rows=%s cols=%s prefix=%s overwrite=%s",
50 input_image,
51 resolved_output_dir,
52 rows,
53 cols,
54 prefix,
55 overwrite,
56 )
58 with StageProgressBar(console=console, use_progress_bar=bool(getattr(console, "is_terminal", True))) as progress:
59 progress.start(total_steps=2, description="Validating tiling request")
60 try:
61 progress.advance("Writing tile images")
62 result = save_tiles_from_image(
63 input_image=input_image,
64 output_dir=resolved_output_dir,
65 rows=rows,
66 cols=cols,
67 prefix=prefix,
68 overwrite=overwrite,
69 )
70 progress.advance("Finalizing tile summary")
71 except Exception as exc:
72 logger.exception("Tile generation failed")
73 console.print(error_panel(str(exc), console=console))
74 raise Exit(1) from exc
76 summary = "\n".join(
77 [
78 f"Tiles written: {len(result.paths)}",
79 f"Grid: {result.rows}x{result.cols}",
80 f"Tile size: {result.tile_width}x{result.tile_height}",
81 f"Saved: {result.output_dir}",
82 ],
83 )
84 console.print(info_panel(summary, console=console))