Spaces:
Sleeping
Sleeping
File size: 2,375 Bytes
78738de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | """Extract quỹ đạo polyline từ System sau simulate(render=True) — MỚI 23/07/2026.
Đúng logic render_png CLI đang dùng: ưu tiên history_cts (đã continuize
dt=0.01), fallback history event-based; vectorize() → rvw[:, 0, :2].
Downsample còn ≤ max_points điểm/bi để JSON nhẹ (~10 KB/cú).
25/07/2026 (Production §2e): thêm ``min_disp`` OPT-IN để lọc bi đứng yên.
``continuize`` điền history cho MỌI bi kể cả bi không hề nhúc nhích → bàn
full rack cho ra 200 điểm × 10 bi × 4 cú ≈ 250-350 KB JSON/response (đo bàn
3 bi chỉ ~10 KB/cú). Mặc định None = hành vi cũ y nguyên (đường v1 + CLI
render_png KHÔNG được đổi một pixel).
"""
from __future__ import annotations
def extract_trajectories(system, max_points: int = 200,
min_disp: float | None = None) -> dict:
"""{ball_id: [[x, y], ...]} — chỉ gồm bi có quỹ đạo ≥ 2 điểm.
min_disp: bỏ qua bi có ĐỘ DÀI ĐƯỜNG ĐI < min_disp (mét). Cố ý dùng độ
dài đường đi chứ không phải dịch chuyển ròng đầu→cuối: bi bị đánh đi rồi
dội băng về gần chỗ cũ có dịch chuyển ròng ~0 nhưng đó chính là quỹ đạo
người dùng cần thấy. None (mặc định) = không lọc.
"""
import numpy as np
out: dict = {}
if system is None:
return out
for bid, ball in system.balls.items():
xy = None
for hist in (getattr(ball, "history_cts", None),
getattr(ball, "history", None)):
if hist is None:
continue
try:
rvw, _ss, _ts = hist.vectorize()
except Exception:
continue
if rvw is not None and len(rvw) > 1:
xy = rvw[:, 0, :2]
break
if xy is None:
continue
if min_disp is not None:
# đo trên mảng ĐẦY ĐỦ, trước khi downsample
path = float(np.linalg.norm(np.diff(xy, axis=0), axis=1).sum())
if path < min_disp:
continue
if len(xy) > max_points:
idx = np.round(np.linspace(0, len(xy) - 1, max_points)).astype(int)
xy = xy[idx]
out[bid] = [[float(p[0]), float(p[1])] for p in xy]
return out
|