Improve board sizing and page packing
This commit is contained in:
+51
-24
@@ -1,9 +1,32 @@
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from .assets import load_json, load_rgb
|
||||
from .settings import BOARD_DECK, DECKS_DIR, BuildConfig
|
||||
|
||||
ORIGINAL_BOARD_MM = (88.9, 139.7)
|
||||
TRIMMED_BOARD_ASPECT_RATIO = ORIGINAL_BOARD_MM[0] / ORIGINAL_BOARD_MM[1]
|
||||
BOARD_SIZE_OPTIONS_MM = {
|
||||
"original": ORIGINAL_BOARD_MM,
|
||||
"a6": (105.0, 148.0),
|
||||
"a5": (148.0, 210.0),
|
||||
"a4": (210.0, 297.0),
|
||||
"half-letter": (139.7, 215.9),
|
||||
"letter": (215.9, 279.4),
|
||||
}
|
||||
BOARD_MODE_CHOICES = tuple(
|
||||
[name for name in BOARD_SIZE_OPTIONS_MM]
|
||||
+ [f"{name}-fit" for name in BOARD_SIZE_OPTIONS_MM]
|
||||
+ ["custom", "custom-fit"]
|
||||
)
|
||||
VISIBLE_BOARD_MODE_CHOICES = ("original", "a6", "a6-fit", "a5", "a5-fit", "half-letter", "half-letter-fit", "custom")
|
||||
|
||||
|
||||
def fit_size_inside(box_mm: tuple[float, float], ratio: float = TRIMMED_BOARD_ASPECT_RATIO) -> tuple[float, float]:
|
||||
max_w, max_h = box_mm
|
||||
if ratio > max_w / max_h:
|
||||
return max_w, max_w / ratio
|
||||
return max_h * ratio, max_h
|
||||
|
||||
|
||||
def board_target_mm_for_mode(
|
||||
board_path: Path,
|
||||
@@ -11,29 +34,16 @@ def board_target_mm_for_mode(
|
||||
board_custom_mm: tuple[float, float] | None = None,
|
||||
) -> tuple[float, float]:
|
||||
if board_custom_mm is not None:
|
||||
if board_mode == "custom-fit":
|
||||
return fit_size_inside(board_custom_mm)
|
||||
return board_custom_mm
|
||||
ratio = board_aspect_ratio(board_path)
|
||||
if board_mode == "a4-fit":
|
||||
max_w, max_h = 210.0, 297.0
|
||||
if ratio > max_w / max_h:
|
||||
return max_w, max_w / ratio
|
||||
return max_h * ratio, max_h
|
||||
if board_mode == "a5-fit":
|
||||
max_w, max_h = 148.0, 210.0
|
||||
if ratio > max_w / max_h:
|
||||
return max_w, max_w / ratio
|
||||
return max_h * ratio, max_h
|
||||
|
||||
# Default: preserve aspect ratio while using approximately A5 area,
|
||||
# capped to fit inside A4.
|
||||
a5_area = 148.0 * 210.0
|
||||
width = math.sqrt(a5_area * ratio)
|
||||
height = math.sqrt(a5_area / ratio)
|
||||
if width > 210.0 or height > 297.0:
|
||||
scale = min(210.0 / width, 297.0 / height)
|
||||
width *= scale
|
||||
height *= scale
|
||||
return width, height
|
||||
if board_mode.endswith("-fit"):
|
||||
base_mode = board_mode.removesuffix("-fit")
|
||||
if base_mode in BOARD_SIZE_OPTIONS_MM and base_mode != "original":
|
||||
return fit_size_inside(BOARD_SIZE_OPTIONS_MM[base_mode])
|
||||
|
||||
return BOARD_SIZE_OPTIONS_MM.get(board_mode, ORIGINAL_BOARD_MM)
|
||||
|
||||
|
||||
def board_aspect_ratio(board_path: Path) -> float:
|
||||
@@ -50,16 +60,33 @@ def board_target_mm(board_path: Path, config: BuildConfig) -> tuple[float, float
|
||||
return board_target_mm_for_mode(board_path, config.board_mode, config.board_custom_mm)
|
||||
|
||||
|
||||
def board_mode_fits_paper(board_mode: str, paper_mm: tuple[float, float], margin_mm: float) -> bool:
|
||||
def board_size_fits_paper(
|
||||
board_mode: str,
|
||||
board_custom_mm: tuple[float, float] | None,
|
||||
paper_mm: tuple[float, float],
|
||||
margin_mm: float,
|
||||
) -> bool:
|
||||
usable_w = paper_mm[0] - 2 * margin_mm
|
||||
usable_h = paper_mm[1] - 2 * margin_mm
|
||||
board_paths = sorted((DECKS_DIR / BOARD_DECK).glob("*-jumbo-front.png"))
|
||||
if not board_paths:
|
||||
return True
|
||||
for board_path in board_paths:
|
||||
board_w, board_h = board_target_mm_for_mode(board_path, board_mode)
|
||||
board_w, board_h = board_target_mm_for_mode(board_path, board_mode, board_custom_mm)
|
||||
fits_upright = board_w <= usable_w and board_h <= usable_h
|
||||
fits_rotated = board_h <= usable_w and board_w <= usable_h
|
||||
if not (fits_upright or fits_rotated):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def board_mode_fits_paper(board_mode: str, paper_mm: tuple[float, float], margin_mm: float) -> bool:
|
||||
return board_size_fits_paper(board_mode, None, paper_mm, margin_mm)
|
||||
|
||||
|
||||
def crop_board_bleed(img):
|
||||
target_w = round(img.width * (3.5 / 3.75))
|
||||
target_h = round(img.height * (5.5 / 5.75))
|
||||
left = max(0, (img.width - target_w) // 2)
|
||||
top = max(0, (img.height - target_h) // 2)
|
||||
return img.crop((left, top, left + target_w, top + target_h))
|
||||
|
||||
Reference in New Issue
Block a user