← back to Quadrille Showroom

scripts/diag-probe.mjs

105 lines

// Diagnostic probe (NOT proof) — measure, via REAL yaw only, two things:
//  (A) HOLE-2: after selecting a board, how wide is the hero on screen vs a closed
//      sliver neighbour? (project the hero face corners + a neighbour face corners).
//  (B) HOLE-1: yaw to the +72 / -72 clamp via real ArrowRight/ArrowLeft keydowns and
//      measure how many screen pixels of clad SIDE/BACK wall are visible (raycast a grid
//      of screen points into the scene; count hits on a roomWall mesh that carries a clad
//      material map).  NO setMode('room'), NO park(), NO camera move.
import pw from '/Users/macstudio3/.npm-global/lib/node_modules/playwright/index.js';
const { chromium } = pw;
const browser = await chromium.launch({ args: ['--use-gl=angle','--use-angle=swiftshader','--enable-webgl','--ignore-gpu-blocklist'] });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
const errors = [];
page.on('console', m => { if (m.type()==='error') errors.push(m.text()); });
page.on('pageerror', e => errors.push('PAGEERROR: '+e.message));
await page.goto('http://localhost:7690/', { waitUntil: 'networkidle' });
await page.waitForFunction(() => window._qh && window._qh.wingBoards && window._qh.wingBoards.length>0, { timeout:20000 });
await page.waitForTimeout(2500);

// Expose a raycast-based "clad wall pixels visible" sampler into the page.
await page.addScriptTag({ content: `
window.__measureCladWall = function() {
  const qh = window._qh, THREE = qh.THREE, cam = qh.camera, scene = qh.scene;
  const W = window.innerWidth, H = window.innerHeight;
  const rc = new THREE.Raycaster();
  // collect all room-wall meshes that currently carry a clad texture map
  const cladMeshes = [];
  scene.traverse(o => {
    if (o.isMesh && o.material && o.material.map && o.geometry && o.geometry.type==='PlaneGeometry') {
      // wall planes are large; wings are PlaneGeometry too but smaller — tag by size
      const g = o.geometry.parameters || {};
      if (g.width >= 1.0 && g.height >= 2.0) cladMeshes.push(o);
    }
  });
  // sample a 40x24 screen grid
  let cladHits=0, total=0, wingHits=0, otherHits=0;
  const targets = [];
  scene.traverse(o => { if (o.isMesh) targets.push(o); });
  for (let yi=0; yi<24; yi++) for (let xi=0; xi<40; xi++) {
    const nx = (xi+0.5)/40*2-1, ny = -((yi+0.5)/24*2-1);
    rc.setFromCamera({x:nx,y:ny}, cam);
    const hit = rc.intersectObjects(targets, false)[0];
    total++;
    if (!hit) continue;
    if (cladMeshes.includes(hit.object)) cladHits++;
    else if (hit.object.userData && hit.object.userData.isWingFace) wingHits++;
    else otherHits++;
  }
  return { cladHits, wingHits, otherHits, total, cladMeshCount: cladMeshes.length, yawDeg:+(qh.spinYaw*180/Math.PI).toFixed(1) };
};
// hero vs neighbour on-screen width (projected face X-extent in px)
window.__measureHeroWidth = function() {
  const qh = window._qh, THREE = qh.THREE, cam = qh.camera;
  const f = qh.focusedWing; if (!f) return null;
  const wb = qh.wingBoards;
  function faceWidthPx(pivot) {
    const ud = pivot.userData; const face = ud.face; if (!face) return 0;
    const g = face.geometry.parameters; const hw = g.width/2, hh=g.height/2;
    const corners = [[-hw,-hh],[hw,-hh],[hw,hh],[-hw,hh]];
    let minx=1e9,maxx=-1e9;
    for (const [cx,cy] of corners) {
      const v = new THREE.Vector3(cx,cy,0);
      face.localToWorld(v); v.project(cam);
      const px=(v.x*0.5+0.5)*window.innerWidth;
      minx=Math.min(minx,px); maxx=Math.max(maxx,px);
    }
    return maxx-minx;
  }
  const heroW = faceWidthPx(f);
  // nearest closed neighbour (index +/-2 from hero, whichever exists)
  const hi = f.userData.index;
  const nb = wb[hi+3] || wb[hi-3] || wb[hi+1] || wb[hi-1];
  const nbW = nb ? faceWidthPx(nb) : 0;
  return { heroW:+heroW.toFixed(1), nbW:+nbW.toFixed(1), ratio:+(heroW/(nbW||1)).toFixed(2), heroFlip:+f.userData.flip.toFixed(3), heroIdx:hi };
};
`});

// --- select a board via the REAL focus path ---
await page.evaluate(() => {
  const wb = window._qh.wingBoards;
  const idx = Math.floor(wb.length/2) + 4;
  window._qh.focusOnWing(wb[idx]);
});
// wait for carousel settle + flip + clad load
await page.waitForTimeout(4500);

const heroW = await page.evaluate(() => window.__measureHeroWidth());
const cladFront = await page.evaluate(() => window.__measureCladWall());

// --- REAL yaw to +72 (right) via held ArrowRight keydowns ---
async function holdYaw(key, ms) {
  await page.evaluate((k)=>{ window.dispatchEvent(new KeyboardEvent('keydown',{key:k,code:k,bubbles:true})); }, key);
  await page.waitForTimeout(ms);
  await page.evaluate((k)=>{ window.dispatchEvent(new KeyboardEvent('keyup',{key:k,code:k,bubbles:true})); }, key);
  await page.waitForTimeout(400);
}
await holdYaw('ArrowRight', 1400);
const cladRight = await page.evaluate(() => window.__measureCladWall());
// back to centre then full left
await holdYaw('ArrowLeft', 1400);  // to ~0
await holdYaw('ArrowLeft', 1400);  // to ~-72
const cladLeft = await page.evaluate(() => window.__measureCladWall());

console.log(JSON.stringify({ heroW, cladFront, cladRight, cladLeft, controlsLocked: await page.evaluate(()=>window._qh.controlsLocked), exploreMode: await page.evaluate(()=>window._qh.exploreMode), errors: errors.slice(0,5), errorCount: errors.length }, null, 2));
await browser.close();