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
docs/3d-viewer.htmldeleted−163@@ -1,163 +0,0 @@-<!doctype html>-<html lang="en">-<head>-<meta charset="utf-8">-<meta name="viewport" content="width=device-width, initial-scale=1">-<title>Base Scaffold Control Panel — 3D</title>-<style>-* { margin: 0; padding: 0; box-sizing: border-box; }-html, body { height: 100%; }-body {- background: #0d1117; color: #e6edf3; overflow: hidden;- font-family: 'Segoe UI', system-ui, sans-serif; font-size: 14px;-}-#bar {- position: fixed; top: 0; left: 0; right: 0; height: 44px; z-index: 10;- display: flex; align-items: center; gap: 10px; padding: 0 14px;- background: rgba(22,27,34,.92); border-bottom: 1px solid #30363d;-}-#bar .t { font-weight: 700; font-size: 14px; }-#bar .t span { color: #00b8b0; }-.seg { display: flex; border: 1px solid #30363d; border-radius: 8px; overflow: hidden; }-.seg button {- padding: 6px 14px; background: none; border: none; color: #8b949e;- font: inherit; font-size: 12.5px; cursor: pointer;-}-.seg button + button { border-left: 1px solid #30363d; }-.seg button.on { background: rgba(0,184,176,.2); color: #00e6dc; font-weight: 700; }-.pill {- margin-left: auto; padding: 5px 12px; border-radius: 999px; border: 1px solid #30363d;- background: #1c2128; color: #8b949e; font-size: 12px; cursor: pointer;-}-.pill.on { color: #00e6dc; border-color: rgba(0,184,176,.4); }-#hint { position: fixed; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 10;- font-size: 11.5px; color: #8b949e; background: rgba(22,27,34,.8); padding: 5px 12px; border-radius: 999px; }-#load { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;- color: #8b949e; z-index: 5; pointer-events: none; }-canvas { display: block; }-</style>-</head>-<body>-<div id="bar">- <span class="t">Control Panel <span>3D</span></span>- <div class="seg" id="boards">- <button data-b="workcell" class="on">Workcell</button>- <button data-b="scaffold">Scaffold</button>- </div>- <button class="pill" id="anim">Animation on</button>- <button class="pill" id="bottom">Bottom light</button>-</div>-<div id="load">loading model…</div>-<div id="hint">drag to orbit · wheel to zoom · right-drag to pan</div>-<script type="importmap">{"imports":{"three":"https://unpkg.com/[email protected]/build/three.module.js","three/addons/":"https://unpkg.com/[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 MODELS = {- workcell: 'models/Control_Panel_Workcell_v2.0.1.glb',- scaffold: 'models/Control_Panel_Scaffold_v2.0.1.glb',-};--const renderer = new THREE.WebGLRenderer({ antialias: true });-renderer.outputColorSpace = THREE.SRGBColorSpace;-renderer.setPixelRatio(Math.min(devicePixelRatio, 2));-document.body.appendChild(renderer.domElement);--const scene = new THREE.Scene();-// component-viewer style vertical gradient background-const bgCanvas = document.createElement('canvas');-bgCanvas.width = 2; bgCanvas.height = 256;-const bctx = bgCanvas.getContext('2d');-const grad = bctx.createLinearGradient(0, 0, 0, 256);-grad.addColorStop(0, '#1e2a3d'); grad.addColorStop(0.2, '#1e2a3d'); grad.addColorStop(1, '#0a121f');-bctx.fillStyle = grad; bctx.fillRect(0, 0, 2, 256);-scene.background = new THREE.CanvasTexture(bgCanvas);--scene.add(new THREE.HemisphereLight(0xddeeff, 0x222428, 1.1));-scene.add(new THREE.AmbientLight(0xffffff, 0.65));-const key = new THREE.DirectionalLight(0xfff0d8, 0.95);-key.position.set(0.5, 1, 0.8); scene.add(key);-const bottomLight = new THREE.DirectionalLight(0xbfd7ff, 0.0);-bottomLight.position.set(0, -1, 0.2); scene.add(bottomLight);--const camera = new THREE.PerspectiveCamera(35, 1, 0.001, 100);-const controls = new OrbitControls(camera, renderer.domElement);-controls.enableDamping = true;-controls.zoomToCursor = true;--const loader = new GLTFLoader();-const draco = new DRACOLoader();-draco.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');-loader.setDRACOLoader(draco);--const cache = {};-let current = null, mixer = null, animOn = true;-const clock = new THREE.Clock();--async function show(name) {- document.getElementById('load').style.display = 'flex';- if (current) { scene.remove(current.root); current = null; mixer = null; }- if (!cache[name]) {- // relative resolves under /render/; fall back to the blob route if that 404s- const candidates = ['../' + MODELS[name], '/blob/app/base-scaffold-control-panel/' + MODELS[name]];- let lastErr = null;- for (const url of candidates) {- try { cache[name] = await loader.loadAsync(url); lastErr = null; break; }- catch (e) { lastErr = e; }- }- if (lastErr) { document.getElementById('load').textContent = 'load failed: ' + lastErr.message; return; }- }- const g = cache[name];- const root = g.scene;- scene.add(root);- current = { root, gltf: g };- if (g.animations && g.animations.length) {- mixer = new THREE.AnimationMixer(root);- mixer.clipAction(g.animations[0]).play();- }- const box = new THREE.Box3().setFromObject(root);- const c = box.getCenter(new THREE.Vector3());- const r = box.getSize(new THREE.Vector3()).length() / 2;- controls.target.copy(c);- camera.position.set(c.x + r * 0.9, c.y - r * 1.1, c.z + r * 1.4);- camera.near = r / 100; camera.far = r * 100;- camera.up.set(0, 0, 1);- camera.updateProjectionMatrix();- controls.update();- document.getElementById('load').style.display = 'none';-}--document.querySelectorAll('#boards button').forEach(b => b.onclick = () => {- document.querySelectorAll('#boards button').forEach(o => o.classList.toggle('on', o === b));- show(b.dataset.b);-});-const animBtn = document.getElementById('anim');-animBtn.classList.add('on');-animBtn.onclick = () => { animOn = !animOn; animBtn.classList.toggle('on', animOn); animBtn.textContent = animOn ? 'Animation on' : 'Animation off'; };-const bottomBtn = document.getElementById('bottom');-bottomBtn.onclick = () => {- const on = bottomLight.intensity === 0;- bottomLight.intensity = on ? 0.9 : 0;- bottomBtn.classList.toggle('on', on);-};--function frame() {- requestAnimationFrame(frame);- const w = innerWidth, h = innerHeight;- if (renderer.domElement.width !== w * renderer.getPixelRatio()) {- renderer.setSize(w, h); camera.aspect = w / h; camera.updateProjectionMatrix();- }- const dt = clock.getDelta();- if (mixer && animOn) mixer.update(dt);- controls.update();- renderer.render(scene, camera);-}-frame();-show('workcell');-addEventListener('resize', () => renderer.setSize(innerWidth, innerHeight));-</script>-</body>-</html>