Spaces:
Running
Running
| """Perspective scaling: map real-world cm dimensions to a pixel footprint. | |
| Uses the row-based ``Perspective`` model so an item is the correct apparent size | |
| for where it sits in the room (spec section 5, stage 4). | |
| """ | |
| from __future__ import annotations | |
| from .floor_plane import Perspective | |
| def footprint_quad( | |
| persp: Perspective, anchor_xy: tuple[float, float], width_cm: float, depth_cm: float | |
| ) -> list[list[float]]: | |
| """Floor corners (px) of a ``width_cm`` x ``depth_cm`` rectangle whose FRONT | |
| edge is centered at ``anchor_xy``. | |
| Returns 4 points ordered front-left, front-right, back-right, back-left. The | |
| back edge is both higher up the image and narrower, giving a perspective- | |
| correct trapezoid rather than a flat sprite. | |
| """ | |
| x_a, y_a = anchor_xy | |
| ppc_front = persp.ppc(y_a) | |
| w_front = width_cm * ppc_front | |
| dy = depth_cm * ppc_front * persp.foreshorten | |
| y_back = max(persp.horizon_y + 1.0, y_a - dy) | |
| w_back = width_cm * persp.ppc(y_back) | |
| return [ | |
| [x_a - w_front / 2, y_a], | |
| [x_a + w_front / 2, y_a], | |
| [x_a + w_back / 2, y_back], | |
| [x_a - w_back / 2, y_back], | |
| ] | |
| def object_height_px(persp: Perspective, anchor_y: float, height_cm: float) -> float: | |
| """Apparent vertical extent (px) of an item of ``height_cm`` at row ``anchor_y``.""" | |
| return height_cm * persp.ppc(anchor_y) | |
| def describe_scale( | |
| persp: Perspective, | |
| anchor_xy: tuple[float, float], | |
| width_cm: float, | |
| depth_cm: float, | |
| height_cm: float, | |
| ) -> str: | |
| x_a, y_a = anchor_xy | |
| ppc = persp.ppc(y_a) | |
| return ( | |
| f"floor row y={int(y_a)} -> {ppc:.2f}px/cm; " | |
| f"{width_cm}x{depth_cm}x{height_cm}cm item renders ~" | |
| f"{int(width_cm * ppc)}x{int(height_cm * ppc)}px (W*H)" | |
| ) | |