"""플랜지 앵커 부착 검증: 로드 에러, 플랜지 밀착 잔차, 손목-플랜지 클로즈업 캡처."""
import asyncio, json
from playwright.async_api import async_playwright
URL='http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html'
OUT=r'E:\도진팩토리\3D스캔및티칭시스템\shots\ceo_feedback'
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':1400,'height':1000})
        errs=[]; logs=[]
        pg.on('pageerror',lambda e: errs.append(str(e)))
        pg.on('console',lambda m: logs.append(m.text) if 'torch' in m.text else None)
        await pg.goto(URL)
        await pg.wait_for_function("window.__dzw && window.__dzw._torchMesh", timeout=15000)
        res=await pg.evaluate("""()=>{ const d=window.__dzw, T=d._T; const out={};
          const me=d._torchMesh; me.updateMatrixWorld(true);
          // 플랜지 중심(로컬원점=피벗후 me 원점)의 truthTool-로컬 좌표 = 밀착 잔차(원점=0이어야)
          const tt=d._truthTool; tt.updateMatrixWorld(true);
          const flangeW=new T.Vector3(); me.getWorldPosition(flangeW);   // me 원점=플랜지중심
          const flangeLocal=tt.worldToLocal(flangeW.clone());
          out.flangeInTool=[+flangeLocal.x.toFixed(1),+flangeLocal.y.toFixed(1),+flangeLocal.z.toFixed(1)];
          out.flangeResidual_mm=+flangeLocal.length().toFixed(1);
          // 플랜지 법선(월드) vs 마운팅축(툴 +Z 월드) 각도 = square 검증
          // me 로컬 +Z? 플랜지 법선은 정렬로 툴+Z가 됨. me의 회전 후 배럴이 어디로 가는지 대신, 툴프레임에서 배럴축 확인
          const camTgt=flangeW.clone();
          const sph=1;
          if(d.orbit){ d.orbit.tgt.copy(camTgt); d.orbit.r=0.35; d.orbit.theta=-0.9; d.orbit.phi=1.35; d.updateCam&&d.updateCam(); }
          return out;
        }""")
        await pg.wait_for_timeout(400)
        await pg.screenshot(path=OUT+r'\fix_param_torch_1.png')
        print(json.dumps(res,ensure_ascii=False)); print("errs:",errs if errs else "없음"); print("logs:",logs[:2])
        await br.close()
asyncio.run(main())
