"""Table geometry read straight from pooltool -- a facts layer, no rules. Why it exists: the front-end sizes its canvas from the real table and maps broadcast coordinates (2.54 x 1.27 m) onto the app table with a single similarity factor, so ``/api/table`` has to return w/l/ball_r/pockets. The previous repository took those numbers from ``PositionPlayEnv`` (the RL environment), which is not ported here -- and hard-coding them would let them drift away from the table specs. This module reads the same source the environment read: ``pt.Table.default()``. pooltool is imported LAZILY inside the function: the app imports this module at startup while importing pooltool costs ~20s (Numba), so the call happens on the background thread of ``app/main.py``, and the contract tests (which stub pooltool) can import this module without pulling anything in. """ from __future__ import annotations BALL_R = 0.028575 # ball radius (m) -- the pooltool default, the same # number as ``poolcoach_cv.broadcast.BALL_R_M`` def pocket_name(w: float, l: float, xy) -> str: """Pocket name derived from its centre, same rule as the previous repository (``poolcoach_rl.recommend.geometry.pocket_name``). Nothing in the Analyzer front-end renders it -- it is kept because the field is part of the /api/table contract and it makes the payload readable.""" lr = "left" if xy[0] < w / 2 else "right" if xy[1] < l / 3: return f"bottom-{lr} corner" if xy[1] > 2 * l / 3: return f"top-{lr} corner" return f"{lr} side" def table_facts() -> dict: """``{w, l, ball_r, pockets: [{x, y, r, name}]}`` of the default table. Call it on a background thread (importing pooltool is heavy). Pocket order follows pooltool's ``table.pockets`` -- the same order the previous repository returned.""" import pooltool as pt table = pt.Table.default() w, l = float(table.w), float(table.l) pockets = [] for p in table.pockets.values(): xy = (float(p.center[0]), float(p.center[1])) pockets.append({"x": xy[0], "y": xy[1], "r": float(getattr(p, "radius", 2 * BALL_R)), "name": pocket_name(w, l, xy)}) return {"w": w, "l": l, "ball_r": BALL_R, "pockets": pockets}