app
Base Scaffold Control Panel
Public Made by Adomby adom
The Base Scaffold System's control panel: electrical specifications and the connect/disconnect interface between a base scaffold and the workcell ecosystem.
← Commit history
scaffold viewer: hover block descriptions (MCU, USB, contact fields, jumpers, dock)
1 file changed
+68
README.html+68@@ -160,6 +160,74 @@ else { g.onclick = () => { groundOn = !groundOn; viewer.setGroundVisible(groundOn); paint(); }; l.onclick = () => { viewer.setBottomLight(!viewer.getBottomLightState()); paint(); }; paint(); ctl.style.display = 'flex';+ // ---- block hover descriptions (scaffold) ----+ try {+ const scene = viewer.getScene();+ const tip = document.createElement('div');+ tip.style.cssText = 'position:absolute;display:none;z-index:20;pointer-events:none;' ++ 'background:rgba(22,27,34,.95);border:1px solid rgba(240,246,252,.15);border-radius:8px;' ++ 'padding:8px 11px;max-width:260px;color:#e6edf3;font-size:12.5px;line-height:1.45;' ++ "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif";+ const tipT = document.createElement('div'); tipT.style.fontWeight = '600';+ const tipB = document.createElement('div'); tipB.style.color = '#9aa4b2'; tipB.style.marginTop = '2px';+ tip.appendChild(tipT); tip.appendChild(tipB);+ document.body.appendChild(tip);+ const root = scene.transformNodes.concat(scene.meshes).find(n => (n.name||'').indexOf('control-panel_scaffold') === 0);+ const NAME_RULES = [+ [/^(U2|Molecule_RP2350A_Mini|mini-D|MINI_)/, ['RP2350A Mini (user MCU)', '26 USER_IO with PWM, PIO and 4 ADC channels; USB 2.0 dual-role. Red = power LED, blue = status LED.']],+ [/^(WIRE_MC|MC\d)/, null],+ [/^JP\d/, ['Ground-bond jumpers', 'Large machine-pin jumpers bond each rail return to ground. Remove to float a supply.']],+ [/^CCJ/, ['CC jumper', 'Place when VBUS is broken out through machine pins without CC termination.']],+ [/^(D1$|D1_glow|D1_cone)/, ['Power LED (red)', 'Scaffold power indicator.']],+ [/^(ETT|ETS)/, ['ET60T blade dock', 'Power and signal blades that mate to the workcell each dock.']],+ [/^GHOST/, ['Control Panel: Workcell (ghost)', 'The workcell half, shown mated above the scaffold.']],+ [/^GNDSPACER/, ['Ground spacer', 'Return path to the base scaffold plate.']],+ ];+ const REGIONS = [+ [0.150, 0.192, -0.088, -0.046, ['RP2350A Mini (user MCU)', '26 USER_IO with PWM, PIO and 4 ADC channels; USB 2.0 dual-role.']],+ [0.125, 0.141, -0.068, -0.052, ['USB-C 2.0 (user)', 'Dual-role host/device, power delivery 3.0 A. VBUS sourced from PS2.']],+ [0.104, 0.141, -0.079, -0.056, ['USB power path', 'Protected VBUS switch and CC controller behind the USB-C port.']],+ [0.043, 0.101, -0.096, -0.079, ['Power contact field', 'PS1 / PS2 / PS3 rails and returns land on machine contacts MC1 to MC30.']],+ [0.101, 0.196, -0.096, -0.080, ['USER_IO contact field', 'MC31 to MC56: 26 RP2350 USER_IO lines for the project.']],+ [0.043, 0.103, -0.078, -0.046, ['Rail ground jumpers', 'Per-rail return-to-ground bonds; each rail is an explicit grounding choice.']],+ ];+ const inv = new window.BABYLON.Matrix();+ function describe(pick) {+ let n = pick.pickedMesh, names = [];+ while (n) { names.push(n.name || ''); n = n.parent; }+ for (const [re, txt] of NAME_RULES) {+ if (names.some(nm => re.test(nm))) { if (txt) return txt; break; }+ }+ if (root && pick.pickedPoint) {+ root.getWorldMatrix().invertToRef(inv);+ const p = window.BABYLON.Vector3.TransformCoordinates(pick.pickedPoint, inv);+ for (const [x0, x1, y0, y1, txt] of REGIONS) {+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) return txt;+ }+ if (p.y < -0.098) return ['ET60T blade dock', 'Power and signal blades that mate to the workcell each dock.'];+ return ['Control Panel: Scaffold', 'User power and I/O dock board for the base scaffold.'];+ }+ return null;+ }+ let raf = 0;+ wrap.addEventListener('pointermove', (ev) => {+ if (raf) return;+ raf = requestAnimationFrame(() => {+ raf = 0;+ const pick = scene.pick(scene.pointerX, scene.pointerY, m => m.isEnabled() && (m.name||'') !== 'ground');+ const d = pick && pick.hit ? describe(pick) : null;+ if (d) {+ tipT.textContent = d[0]; tipB.textContent = d[1];+ tip.style.display = 'block';+ const r = wrap.getBoundingClientRect();+ tip.style.left = Math.min(ev.clientX - r.left + 14, r.width - 280) + 'px';+ tip.style.top = Math.max(ev.clientY - r.top - 10, 46) + 'px';+ } else tip.style.display = 'none';+ });+ });+ wrap.addEventListener('pointerleave', () => { tip.style.display = 'none'; });+ } catch (e) {}+ addEventListener('message', (e) => { const d = e.data || {}; if (d.type !== 'cp-cam' || (d.board && d.board !== 'scaffold')) return;