← Commit history

Delete scaffold/viewer.html

John Lauer ·37828bc0f5 ·4d ago ·parent 3d286c7
1 file changed −141
scaffold/viewer.htmldeleted−141
@@ -1,141 +0,0 @@-<!doctype html>-<html lang="en">-<head>-<meta charset="utf-8">-<meta name="viewport" content="width=device-width, initial-scale=1">-<title>Buck Probe Scaffold</title>-<style>-  :root { --bg: #f4f5f7; --fg: #1d2330; --muted: #5b6475; --panel: rgba(255,255,255,0.92); --line: #d8dce3; }-  @media (prefers-color-scheme: dark) {-    :root:not([data-theme="light"]) { --bg: #14171d; --fg: #e6e9ef; --muted: #9aa3b2; --panel: rgba(28,32,40,0.92); --line: #323845; }-  }-  :root[data-theme="dark"] { --bg: #14171d; --fg: #e6e9ef; --muted: #9aa3b2; --panel: rgba(28,32,40,0.92); --line: #323845; }-  html, body { margin: 0; height: 100%; background: var(--bg); color: var(--fg); font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; overflow: hidden; }-  #c { position: fixed; inset: 0; }-  #legend { position: fixed; left: 16px; top: 16px; max-width: min(420px, calc(100vw - 32px)); background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; }-  #legend h1 { font-size: 15px; margin: 0 0 6px; }-  #legend p { margin: 0 0 8px; color: var(--muted); font-size: 12.5px; }-  #legend table { border-collapse: collapse; width: 100%; font-size: 12.5px; }-  #legend td { padding: 2px 6px 2px 0; vertical-align: top; }-  .sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px; vertical-align: -1px; }-  #status { position: fixed; right: 16px; bottom: 12px; color: var(--muted); font-size: 12px; }-  @media (max-width: 600px) { #legend { font-size: 12px; padding: 10px; } }-</style>-</head>-<body>-<canvas id="c"></canvas>-<div id="legend">-  <h1>12 V to 5 V buck on a probing scaffold</h1>-  <p>Buck molecule (28 x 20 mm, medium pins) on a LrgMed 64 x 64 scaffold, wired to the Control Panel scaffold. Positions come from <code>buck-probe-layout.json</code>; the control panel's place is approximate.</p>-  <table id="wires"></table>-</div>-<div id="status">loading models</div>-<script type="importmap">-{ "imports": { "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",-               "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/" } }-</script>-<script type="module">-import * as THREE from 'three';-import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';-import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';-import { OrbitControls } from 'three/addons/controls/OrbitControls.js';--const params = new URLSearchParams(location.search);-if (params.has('clean')) { document.getElementById('legend').style.display = 'none'; document.getElementById('status').style.display = 'none'; }-const status = document.getElementById('status');-const canvas = document.getElementById('c');-const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });-renderer.setPixelRatio(Math.min(devicePixelRatio, 2));-const dark = matchMedia('(prefers-color-scheme: dark)').matches && document.documentElement.dataset.theme !== 'light';-const scene = new THREE.Scene();-scene.background = new THREE.Color(dark ? 0x14171d : 0xf4f5f7);-scene.add(new THREE.HemisphereLight(0xffffff, 0x445066, 1.6));-const sun = new THREE.DirectionalLight(0xffffff, 1.6); sun.position.set(200, -300, 500); scene.add(sun);--// World frame = the layout frame: millimetres, X right, Y back, Z up (three.js is Y-up, so rotate once).-const world = new THREE.Group(); world.rotation.x = -Math.PI / 2; scene.add(world);-const camera = new THREE.PerspectiveCamera(35, 1, 1, 5000);-const controls = new OrbitControls(camera, canvas);--const CP = { centre: [272, 603], boardCentre: [119, 70.5] };   // control panel: board 160 x 45 mm (Edge.Cuts x 39..199, y 48..93)-const loader = new GLTFLoader();-const draco = new DRACOLoader(); draco.setDecoderPath('https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/libs/draco/gltf/');-loader.setDRACOLoader(draco);-const load = url => new Promise((ok, bad) => loader.load(url, g => ok(g.scene), undefined, bad));--function box(o) { return new THREE.Box3().setFromObject(o); }--async function main() {-  const [layout, cpContacts] = await Promise.all([fetch('buck-probe-layout.json').then(r => r.json()), fetch('cp-contacts.json').then(r => r.json())]);-  // base scaffold: 576 x 576 mm plate with the 18 x 18 large-contact grid (16 + 32 n)-  const base = new THREE.Mesh(new THREE.BoxGeometry(576, 576, 3), new THREE.MeshStandardMaterial({ color: dark ? 0x2a2f3a : 0xd9dde4 }));-  base.position.set(288, 288, -1.5); world.add(base);-  const dotG = new THREE.CylinderGeometry(2.6, 2.6, 0.4, 20); dotG.rotateX(Math.PI / 2);-  const dotM = new THREE.MeshStandardMaterial({ color: 0xc9a227, metalness: 0.6, roughness: 0.4 });-  for (let i = 0; i < 18; i++) for (let j = 0; j < 18; j++) { const d = new THREE.Mesh(dotG, dotM); d.position.set(16 + 32 * i, 16 + 32 * j, 0.2); world.add(d); }--  // LrgMed scaffold: OCCT GLB in metres, Z up, centred on its board; its FL large pin sits 32 mm from centre-  const sc = layout.molecules['Scaffold LrgMed 64x64'];-  const scaf = await load('scaffold-lrgmed-64x64.glb');-  scaf.scale.setScalar(1000);-  const scafG = new THREE.Group(); scafG.add(scaf); scafG.position.set(sc.position.x + 32, sc.position.y + 32, 12.5); world.add(scafG);--  // buck molecule: step2glb anchors MP1 (FL pin) at the origin, metres, Z up-  const bm = layout.molecules['Buck 12V-5V 1A'];-  const buck = await load('buck-12v5v-molecule.glb');-  buck.scale.setScalar(1000);-  const buckG = new THREE.Group(); buckG.add(buck); buckG.position.set(bm.position.x, bm.position.y, bm.position.z + 12.5); world.add(buckG);--  // control panel: Draco GLB; scale its long side to 160 mm and set it on the base's back edge-  const cp = await load('control-panel-scaffold.glb');-  const cpG = new THREE.Group(); cpG.add(cp);-  let b = box(cpG); const s = b.getSize(new THREE.Vector3());-  cp.scale.setScalar(160 / Math.max(s.x, s.y));-  b = box(cpG); const c = b.getCenter(new THREE.Vector3());-  // the bounding box includes the dock connector behind the board: anchor on the board's FRONT edge (min y), 22.5 mm in front of its centre-  cp.position.sub(new THREE.Vector3(c.x, b.min.y + 22.5, b.min.z));-  cpG.position.set(CP.centre[0], CP.centre[1], 0); world.add(cpG);-  // the panel's board is the flat mesh about 160 x 45 mm: its top face is where the contacts are-  world.updateMatrixWorld(true);-  const inv = new THREE.Matrix4().copy(world.matrixWorld).invert();-  let cpTop = 3, best = 0, cpBoard = null;-  cpG.traverse(o => { if (!o.isMesh) return; const bb = new THREE.Box3().setFromObject(o).applyMatrix4(inv); const sz = bb.getSize(new THREE.Vector3());-    const area = sz.x * sz.y; if (area > best && sz.z < 3) { best = area; cpTop = bb.max.z; cpBoard = bb; } });--  // wires: molecule-local contact -> control-panel contact, raised arcs-  const tbl = document.getElementById('wires');-  for (const w of layout.wires) {-    const st = w.startReference, m = layout.molecules[st.moleculeName];-    const a = new THREE.Vector3(m.position.x + st.contactPosition.x, m.position.y + st.contactPosition.y, m.position.z + 12.5 + st.contactPosition.z);-    const [bx, by] = cpContacts[w.endReference.controlPanelContactName];-    // KiCad board frame: Edge.Cuts x 39..199, y 48..93 (y down, y = 93 is the front edge facing the workspace)-    const e = new THREE.Vector3(cpBoard.min.x + (bx - 39), cpBoard.min.y + (93 - by), 3);-    e.z = cpTop + 0.6;-    const up = Math.max(a.z, e.z) + 18;-    const curve = new THREE.CatmullRomCurve3([a, new THREE.Vector3(a.x, a.y, up), new THREE.Vector3((a.x + e.x) / 2, (a.y + e.y) / 2, up + 6), new THREE.Vector3(e.x, e.y, up), e]);-    const col = new THREE.Color(w.color.r, w.color.g, w.color.b);-    world.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 80, 0.9, 10), new THREE.MeshStandardMaterial({ color: col, roughness: 0.5 })));-    const tip = new THREE.Mesh(new THREE.SphereGeometry(1.6, 16, 12), new THREE.MeshStandardMaterial({ color: col })); tip.position.copy(e); world.add(tip);-    const hex = '#' + col.getHexString();-    tbl.insertAdjacentHTML('beforeend', `<tr><td><span class="sw" style="background:${hex}"></span>${w.name}</td><td>${st.contactName} to ${w.endReference.controlPanelContactName}</td></tr>`);-  }--  const focus = new THREE.Vector3(272, 540, 10).applyMatrix4(world.matrix);-  world.updateMatrixWorld(); const f = new THREE.Vector3(272, 545, 10); world.localToWorld(f);-  const view = params.get('view') || 'iso';-  if (view === 'cp') f.set(272, 586, 0), world.localToWorld(f);-  if (view === 'orbit') f.set(272, 540, 0), world.localToWorld(f);-  const eye = view === 'orbit' ? new THREE.Vector3(272, 300, 260) : view === 'cp' ? new THREE.Vector3(272, 540, 75) : view === 'top' ? new THREE.Vector3(272, 545, 260) : view === 'close' ? new THREE.Vector3(215, 430, 95) : new THREE.Vector3(150, 360, 190);-  world.localToWorld(eye);-  camera.position.copy(eye); controls.target.copy(f); controls.update();-  status.textContent = 'drag to orbit, scroll to zoom';-  if (params.has('spin')) { controls.autoRotate = true; controls.autoRotateSpeed = parseFloat(params.get('spin')) || 0.6; }-  document.body.dataset.ready = '1';-}-function size() { const w = innerWidth, h = innerHeight; renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix(); }-addEventListener('resize', size); size();-renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera); });-main().catch(e => { status.textContent = 'error: ' + e.message; document.body.dataset.ready = 'err'; console.error(e); });-</script>-</body>-</html>