Spaces:
Runtime error
Runtime error
| // RealWonder β Three.js 3D force direction viewer | |
| // Exposes window.rwInitForceViewer() and window.rwUpdateArrow(fx, fy, fz) | |
| (function () { | |
| "use strict"; | |
| var renderer3d = null; | |
| var scene3d = null; | |
| var camera3d = null; | |
| var forceArrow3d = null; | |
| var viewer3dReady = false; | |
| window.rwInitForceViewer = function () { | |
| if (viewer3dReady) return; | |
| if (typeof THREE === "undefined") { | |
| console.warn("Three.js not loaded β 3D viewer disabled."); | |
| return; | |
| } | |
| var viewerCanvas = document.getElementById("forceViewer3d"); | |
| if (!viewerCanvas) { | |
| console.warn("forceViewer3d canvas not found β will retry via MutationObserver."); | |
| return; | |
| } | |
| renderer3d = new THREE.WebGLRenderer({ canvas: viewerCanvas, antialias: true, alpha: true }); | |
| renderer3d.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
| renderer3d.setSize(160, 160); | |
| scene3d = new THREE.Scene(); | |
| camera3d = new THREE.PerspectiveCamera(42, 1.0, 0.1, 50); | |
| camera3d.position.set(1.6, 1.2, 1.9); | |
| camera3d.lookAt(0, 0, 0); | |
| // Origin sphere | |
| var originMesh = new THREE.Mesh( | |
| new THREE.SphereGeometry(0.07, 16, 16), | |
| new THREE.MeshBasicMaterial({ color: 0x6b8aed }) | |
| ); | |
| scene3d.add(originMesh); | |
| // Reference axis arrows | |
| // World X (left-right) β Three.js +X (red) | |
| // World Z (up-down) β Three.js +Y (green) | |
| // World Y (in-out) β Three.js -Z (blue) | |
| var axLen = 0.65, hLen = 0.14, hW = 0.07; | |
| scene3d.add(new THREE.ArrowHelper(new THREE.Vector3(1, 0, 0), new THREE.Vector3(), axLen, 0xcc3333, hLen, hW)); | |
| scene3d.add(new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0), new THREE.Vector3(), axLen, 0x33994a, hLen, hW)); | |
| scene3d.add(new THREE.ArrowHelper(new THREE.Vector3(0, 0, -1), new THREE.Vector3(), axLen, 0x3355cc, hLen, hW)); | |
| // Force arrow (vivid orange, initially hidden) | |
| forceArrow3d = new THREE.ArrowHelper( | |
| new THREE.Vector3(1, 0, 0), new THREE.Vector3(), 0.5, 0xe06010, 0.22, 0.10 | |
| ); | |
| forceArrow3d.visible = false; | |
| scene3d.add(forceArrow3d); | |
| scene3d.add(new THREE.AmbientLight(0xffffff, 1.0)); | |
| viewer3dReady = true; | |
| render3D(); | |
| console.log("RealWonder force viewer initialized."); | |
| }; | |
| // forceX = world X (left/right), forceY = world Y (out/in), forceZ = world Z (down/up) | |
| window.rwUpdateArrow = function (forceX, forceY, forceZ) { | |
| if (!viewer3dReady || !forceArrow3d) return; | |
| // Map world β Three.js: XβX, ZβY(up), Yβ-Z(into screen) | |
| var tx = forceX, ty = forceZ, tz = -forceY; | |
| var len = Math.sqrt(tx * tx + ty * ty + tz * tz); | |
| if (len < 0.01) { | |
| forceArrow3d.visible = false; | |
| } else { | |
| forceArrow3d.visible = true; | |
| forceArrow3d.setDirection(new THREE.Vector3(tx / len, ty / len, tz / len)); | |
| // Scale arrow length 0.25β0.85 with normalized force magnitude (max strength β 2) | |
| forceArrow3d.setLength(0.25 + 0.60 * Math.min(len / 2.0, 1.0), 0.20, 0.09); | |
| } | |
| render3D(); | |
| }; | |
| function render3D() { | |
| if (renderer3d && scene3d && camera3d) { | |
| renderer3d.render(scene3d, camera3d); | |
| } | |
| } | |
| // Auto-init when #forceViewer3d appears in DOM (Gradio renders dynamically) | |
| var _observer = new MutationObserver(function () { | |
| if (!viewer3dReady && typeof THREE !== "undefined" && document.getElementById("forceViewer3d")) { | |
| window.rwInitForceViewer(); | |
| } | |
| }); | |
| _observer.observe(document.documentElement, { childList: true, subtree: true }); | |
| // Also attempt immediate init if DOM is already ready | |
| if (document.readyState !== "loading") { | |
| if (typeof THREE !== "undefined" && document.getElementById("forceViewer3d")) { | |
| window.rwInitForceViewer(); | |
| } | |
| } | |
| })(); | |