"""로봇 손목(툴 플랜지면) 위치·지름 측정 — 새 플랜지 원판 기본 치수 산출용."""
import asyncio, json
from playwright.async_api import async_playwright
URL='http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html'
async def main():
    async with async_playwright() as p:
        br=await p.chromium.launch(channel='msedge',headless=True)
        pg=await br.new_page(viewport={'width':1000,'height':700})
        await pg.goto(URL)
        await pg.wait_for_function("window.__dzw && window.__dzw._truthTool", timeout=15000)
        res=await pg.evaluate("""()=>{ const d=window.__dzw, T=d._T; const out={};
          const tt=d._truthTool; tt.updateMatrixWorld(true);
          const O=new T.Vector3(); tt.getWorldPosition(O);         // 툴 플랜지면 원점(월드)
          // 툴 Z축(월드): truthTool 로컬 +Z 방향
          const zAxis=new T.Vector3(0,0,1).transformDirection(tt.matrixWorld).normalize();
          // 로봇 팔 메시 후보 수집(토치/마커/축 제외), 툴원점 근처 정점의 축수직 반경 분포
          const meshes=[]; d.three.scene.traverse(o=>{ if(o.isMesh){ const n=(o.name||'');
            if(n.indexOf('torch')<0 && o!==d._weldPoint && o!==d._tcpMarker) meshes.push(o); } });
          out.meshCount=meshes.length;
          // 각 메시별로 툴원점에서 축방향 ±60mm(월드스케일 고려는 아래) 내 정점의 반경 최대 → 손목지름 후보
          // 월드스케일: truthTool의 월드스케일 계수
          const sc=new T.Vector3(); tt.getWorldScale(sc); const S=sc.x||1;   // mm→world
          const band=60*S, near=[]; let best=null;
          for(const m of meshes){ const g=m.geometry; if(!g||!g.attributes||!g.attributes.position) continue;
            const pos=g.attributes.position; const step=Math.max(1,(pos.count/4000|0));
            let cnt=0,rmax=0; const v=new T.Vector3();
            for(let i=0;i<pos.count;i+=step){ v.fromBufferAttribute(pos,i); m.localToWorld(v);
              const rel=v.clone().sub(O); const along=rel.dot(zAxis); const perp=rel.clone().sub(zAxis.clone().multiplyScalar(along)).length();
              if(Math.abs(along)<band){ cnt++; if(perp>rmax)rmax=perp; }
            }
            if(cnt>20){ const rmm=rmax/S; near.push({name:m.name||'(no name)', nearVerts:cnt, radius_mm:+rmm.toFixed(1), diam_mm:+(2*rmm).toFixed(1)});
              if(!best||cnt>best.cnt) best={cnt,name:m.name,diam:2*rmax/S}; }
          }
          out.worldScale_mm_to_unit=+S.toFixed(5);
          out.near_meshes=near.sort((a,b)=>b.nearVerts-a.nearVerts).slice(0,8);
          return out;
        }""")
        print(json.dumps(res,ensure_ascii=False,indent=1))
        await br.close()
asyncio.run(main())
