Spaces:
Running
Running
Download marshaller.js from Superboss1976/arff-instructor: direct link, hf CLI and curl.
- Browser
- Download file 4.54 kB
-
https://huggingface.co/spaces/Superboss1976/arff-instructor/resolve/main/marshaller.js
- Command line
-
hf download hf://spaces/Superboss1976/arff-instructor/marshaller.js
-
curl -L -o marshaller.js https://huggingface.co/spaces/Superboss1976/arff-instructor/resolve/main/marshaller.js
4.54 kB
| import * as T from 'three'; | |
| import { OrbitControls } from './vendor/OrbitControls.js'; | |
| // Procedural teaching rig. Instructor validation is required for formal signal instruction. | |
| export function createMarshaller(container){ | |
| const scene=new T.Scene();scene.background=new T.Color('#203641');scene.fog=new T.Fog('#203641',12,35); | |
| const r=new T.WebGLRenderer({antialias:true,powerPreference:'low-power'});r.setPixelRatio(Math.min(devicePixelRatio,1.5));r.outputColorSpace=T.SRGBColorSpace;container.prepend(r.domElement); | |
| const camera=new T.PerspectiveCamera(36,1,.1,60);camera.position.set(0,2.6,8.4); | |
| const controls=new OrbitControls(camera,r.domElement);controls.target.set(0,1.55,0);controls.enableDamping=true;controls.enablePan=false;controls.minDistance=4;controls.maxDistance=13;controls.maxPolarAngle=1.48; | |
| scene.add(new T.HemisphereLight('#e6f6ff','#435753',3));const sun=new T.DirectionalLight('#ffead5',3);sun.position.set(4,7,5);scene.add(sun); | |
| const material=c=>new T.MeshStandardMaterial({color:c,roughness:.68});const dark=material('#172a37'),vest=material('#d8ed4d'),skin=material('#bf9675'),silver=material('#e5edf1'),wand=material('#ff663f');wand.emissive=new T.Color('#a7240e'); | |
| function box(p,m,w,h,d,x,y,z){const o=new T.Mesh(new T.BoxGeometry(w,h,d),m);o.position.set(x,y,z);p.add(o);return o;} | |
| function sphere(p,m,r,x,y,z){const o=new T.Mesh(new T.SphereGeometry(r,20,14),m);o.position.set(x,y,z);p.add(o);return o;} | |
| const ground=new T.Mesh(new T.PlaneGeometry(80,80),material('#263e48'));ground.rotation.x=-Math.PI/2;scene.add(ground);const grid=new T.GridHelper(40,20,'#5e787e','#354f5a');grid.position.y=.002;scene.add(grid); | |
| box(scene,material('#d7b45c'),.08,.015,20,0,.01,-10); | |
| const person=new T.Group();scene.add(person);box(person,dark,.55,.85,.35,0,1.28,0);box(person,vest,.59,.65,.38,0,1.43,0);box(person,silver,.6,.07,.4,0,1.28,0);box(person,silver,.6,.07,.4,0,1.57,0); | |
| for(const side of [-1,1]){box(person,dark,.21,.9,.24,side*.17,.55,0);box(person,dark,.24,.17,.4,side*.17,.09,.08);}sphere(person,skin,.23,0,2.02,0);box(person,dark,.4,.08,.37,0,2.23,.02);sphere(person,dark,.025,-.075,2.04,.214);sphere(person,dark,.025,.075,2.04,.214); | |
| const arms=[]; | |
| for(const side of [-1,1]){const shoulder=new T.Group();shoulder.position.set(side*.35,1.72,0);person.add(shoulder);box(shoulder,vest,.17,.38,.19,0,-.19,0);const elbow=new T.Group();elbow.position.y=-.38;shoulder.add(elbow);box(elbow,dark,.14,.36,.16,0,-.18,0);sphere(elbow,skin,.08,0,-.4,0);box(elbow,wand,.055,.42,.055,0,-.62,0);arms.push({side,shoulder,elbow});} | |
| let signal='identify',paused=matchMedia('(prefers-reduced-motion: reduce)').matches,time=0,last=performance.now(); | |
| const resize=()=>{r.setSize(container.clientWidth,container.clientHeight,false);camera.aspect=container.clientWidth/container.clientHeight;camera.updateProjectionMatrix();};const observer=new ResizeObserver(resize);observer.observe(container);resize(); | |
| let alive=true,frameId; | |
| function frame(now){if(!alive)return;frameId=requestAnimationFrame(frame);const dt=Math.min(.05,(now-last)/1000);last=now;if(document.hidden||r.domElement.hidden)return;if(!paused)time+=dt;for(const a of arms){a.shoulder.rotation.set(0,0,0);a.elbow.rotation.set(0,0,0);if(signal==='stop'){const p=Math.min(1,(time%5)/3.8);a.shoulder.rotation.z=a.side*(Math.PI/2+p*1.83);}else if(signal==='emergency-stop'){a.shoulder.rotation.z=a.side*(2.82+Math.sin(time*7)*.23);}else if(signal==='ahead'){a.shoulder.rotation.x=-Math.PI/2;a.elbow.rotation.x=-.45-(Math.sin(time*2.2)+1)*.45;}else if(signal==='turn-left'){if(a.side===1)a.shoulder.rotation.z=-Math.PI/2;else{a.shoulder.rotation.x=-Math.PI/2;a.elbow.rotation.x=-.45-(Math.sin(time*2.2)+1)*.45;}}else if(signal==='turn-right'){if(a.side===-1)a.shoulder.rotation.z=Math.PI/2;else{a.shoulder.rotation.x=-Math.PI/2;a.elbow.rotation.x=-.45-(Math.sin(time*2.2)+1)*.45;}}else if(signal==='set-brakes'){if(a.side===1){a.shoulder.rotation.z=-2.7;a.elbow.rotation.x=-.25;}else a.shoulder.rotation.z=.18;}else{a.shoulder.rotation.z=a.side*3.0;}}controls.update();r.render(scene,camera);}frameId=requestAnimationFrame(frame); | |
| return{signal(value){signal=value;time=0;},setPaused(v){paused=v;},view(v){camera.position.set(...(v==='top'?[0,7,.1]:v==='side'?[6,2,1]:[0,2.6,8.4]));controls.target.set(0,1.55,0);controls.update();},dispose(){alive=false;cancelAnimationFrame(frameId);observer.disconnect();controls.dispose();scene.traverse(o=>{o.geometry?.dispose();if(o.material)o.material.dispose();});r.dispose();r.domElement.remove();}}; | |
| } | |