/* ─── 히트존 매핑 상세 화면 ────────────────────────────────────────────── */ /* 5×5 zone, 안쪽 3×3이 스트라이크 존. 각 셀에 더미 (avg, slg, n). */ const HZ_PLAYERS = { "lee-jh": { ko:"이정후", team:"SF", lg:"MLB", color:"#FD5A1E", role:"좌타자" }, "kim-hys": { ko:"김혜성", team:"LAD", lg:"MLB", color:"#005A9C", role:"좌타자" }, "kim-hs": { ko:"김하성", team:"TB", lg:"MLB", color:"#092C5C", role:"우타자" }, "kim-dy": { ko:"김도영", team:"KIA", lg:"KBO", color:"#EA0029", role:"좌타자" }, "austin": { ko:"오스틴", team:"LG", lg:"KBO", color:"#C30452", role:"우타자" }, }; /* 5×5 matrix of {avg, slg, n}. Inner 3×3 are strike zone. */ function makeZone(seed=1) { // simple deterministic-ish dummy zone for each player+hand+season combo const rng = (a,b,k)=> { const x = Math.sin((a+1)*(b+2)*seed + k)*10000; return x - Math.floor(x); }; const grid = []; for (let r=0; r<5; r++){ const row = []; for (let c=0; c<5; c++){ const strike = r>=1 && r<=3 && c>=1 && c<=3; const base = strike ? 0.25 : 0.18; const center = (r===2 && c===2) ? 0.12 : (r===2||c===2) && strike ? 0.07 : 0; const noise = rng(r,c,0)*0.10; const avg = Math.max(0.12, Math.min(0.52, base + center + noise)); const slg = Math.max(0.18, Math.min(0.92, avg + 0.18 + rng(r,c,1)*0.22)); const n = strike ? 28 + Math.floor(rng(r,c,2)*42) : 8 + Math.floor(rng(r,c,2)*16); row.push({avg,slg,n,strike}); } grid.push(row); } return grid; } const ZONE_NAMES = [ ["하이 아웃","하이 인","하이 센터","하이 가까이","하이 몸쪽"], ["바깥 높음","아웃 하이","상단","인 하이","몸쪽 높음"], ["바깥","아웃","센터","인","몸쪽"], ["바깥 낮음","아웃 로우","하단","인 로우","몸쪽 낮음"], ["로우 아웃","로우 인","로우 센터","로우 가까이","로우 몸쪽"], ]; function ToolHeatzone({ onClose }) { const [pid, setPid] = React.useState("lee-jh"); const [hand, setHand] = React.useState("R"); // 상대 투수 손 const [season, setSeason] = React.useState("2026"); const [hover, setHover] = React.useState(null); const player = HZ_PLAYERS[pid]; const seedKey = pid.length + (hand==="R"?2:5) + season.charCodeAt(3); const zone = React.useMemo(() => makeZone(seedKey), [pid,hand,season]); const flat = []; zone.forEach((row,r)=>row.forEach((c,i)=>flat.push({r,c:i,...c,name:ZONE_NAMES[r][i]}))); const strikes = flat.filter(c=>c.strike).sort((a,b)=>b.avg-a.avg); const top3 = strikes.slice(0,3); const bot3 = [...strikes].sort((a,b)=>a.avg-b.avg).slice(0,3); const maxAvg = Math.max(...flat.map(c=>c.avg)); return (