main
Colby Knox 1.1.0: every molecule gets large machine pins on the 32 mm grid; gallery rebuilt; corrected notes on kicad-to-molecule c794385 4d ago
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Board to Molecule 3D</title>
<style>
  :root { --bg: #0b0f14; }
  html, body { margin: 0; height: 100%; background: var(--bg); overflow: hidden; }
  #host { position: absolute; inset: 0; }
</style>
<script>
  // Node-style globals the viewer bundle's inspector deps expect, set before loading it.
  window.process = window.process || { env: { NODE_ENV: "production" } };
  window.global = window.global || window;
</script>
</head>
<body>
<div id="host"></div>
<script type="module">
// One Adom 3D viewer per document: the viewer keeps its engine, scene and camera in
// page-wide statics, so a compare view puts each model in its own frame and links
// the cameras through the parent page.
const VIEWER = 'https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/adom-3d-viewer-babylon9.esm.js';
const q = new URLSearchParams(location.search);
const id = q.get('id') || 'v';
const post = (msg) => parent.postMessage({ source: 'b2m-3d', id, ...msg }, '*');

try {
  await import(VIEWER);
  const viewer = window.Adom3DViewerBabylon9.init(document.getElementById('host'), {
    zUp: true, showViewCube: q.get('cube') !== '0', showGround: true,
  });
  await viewer.whenReady();
  await viewer.loadModel(q.get('model'));
  viewer.frameModel(0.8);
  const cam = viewer.getCamera();
  // Let the framing animation finish before taking or applying a shared pose.
  await new Promise(r => setTimeout(r, 1200));
  let applying = 0;
  const pose = () => ({ alpha: cam.alpha, beta: cam.beta, radius: cam.radius });
  cam.onViewMatrixChangedObservable.add(() => { if (!applying) post({ type: 'camera', pose: pose() }); });
  addEventListener('message', (e) => {
    const m = e.data;
    if (!m || m.source !== 'b2m-parent') return;
    if (m.type === 'camera' || m.type === 'view') {
      const p = m.pose;
      // A pose from the other frame must not echo back; a button press should.
      if (m.type === 'camera') applying++;
      if (p.alpha != null) cam.alpha = p.alpha;
      if (p.beta != null) cam.beta = p.beta;
      if (p.radius != null) cam.radius = p.radius;
      cam.inertialAlphaOffset = 0; cam.inertialBetaOffset = 0; cam.inertialRadiusOffset = 0;
      if (m.type === 'camera') requestAnimationFrame(() => requestAnimationFrame(() => applying--));
    } else if (m.type === 'home') {
      viewer.frameModel(0.8);
    }
  });
  post({ type: 'ready', pose: pose(), report: viewer.getReadyReport() });
} catch (e) {
  post({ type: 'error', message: String(e && e.message || e) });
}
</script>
</body>
</html>