SuperHQ Motion experiment: locomotion and action checkpoints, runtime, clips, evaluation
6adbbb1 verified Download src/profile-clock.js from superhq-ai/motion: direct link, hf CLI and curl.
- Browser
- Download file 3.33 kB
-
https://huggingface.co/superhq-ai/motion/resolve/main/src/profile-clock.js
- Command line
-
hf download hf://superhq-ai/motion/src/profile-clock.js
-
curl -L -o profile-clock.js https://huggingface.co/superhq-ai/motion/resolve/main/src/profile-clock.js
3.33 kB
| const DT = 1 / 60; | |
| const smooth = t => {t=Math.max(0,Math.min(1,t));return t*t*(3-2*t);}; | |
| const vector = (a,n) => (Array.isArray(a)||ArrayBuffer.isView(a))&&a.length===n&&Array.from(a).every(Number.isFinite); | |
| function profile(value,rate) { | |
| if(!Number.isFinite(rate)||rate<.75||rate>1.25||!value|| | |
| !vector(value.flags,9)||value.flags.some(v=>v!==0&&v!==1)||value.flags[0]!==1|| | |
| value.flags.slice(1,5).reduce((a,b)=>a+b,0)!==1|| | |
| !Number.isFinite(value.frequency_hz)||value.frequency_hz<=0||value.frequency_hz>2|| | |
| !Number.isFinite(value.speed_mps)||value.speed_mps<0||value.speed_mps>12)throw Error('Invalid Motion profile or playback rate.'); | |
| const velocity=value.velocity??[0,0,value.speed_mps]; | |
| if(!vector(velocity,3)||velocity[1]!==0||Math.abs(Math.hypot(...velocity)-value.speed_mps)>1e-4|| | |
| (value.flags[1]===1&&value.speed_mps!==0))throw Error('Invalid flat-ground profile velocity.'); | |
| return {flags:Array.from(value.flags),velocity:velocity.map(v=>v*rate),frequency:value.frequency_hz*rate}; | |
| } | |
| /** Causal 34-feature clock for the game repair checkpoint. Phase is in cycles. | |
| * Call step once per 1/60 second. The host owns movement and collision handling. | |
| * Supported profile pairs are listed in the checkpoint study protocol. | |
| */ | |
| export class MotionProfileClock { | |
| constructor(initial,{rate=1,transitionSeconds=.75}={}) { | |
| if(!Number.isFinite(transitionSeconds)||transitionSeconds<.5||transitionSeconds>1)throw Error('Transition duration must be .5 to 1 second.'); | |
| this.transitionSeconds=transitionSeconds;this.reset(initial,{rate}); | |
| } | |
| reset(initial,{rate=1,phase=0,position=[0,0,0]}={}) { | |
| const p=profile(initial,rate); | |
| if(!Number.isFinite(phase)||!vector(position,3)||position[1]!==0)throw Error('Invalid flat-ground reset.'); | |
| this.from=p;this.to=p;this.start=-this.transitionSeconds;this.time=0; | |
| this.phase=((phase%1)+1)%1;this.position=Array.from(position);this.frame=0; | |
| } | |
| at(time) { | |
| const u=smooth((time-this.start)/this.transitionSeconds),mix=(a,b)=>a*(1-u)+b*u; | |
| return {flags:this.from.flags.map((v,i)=>mix(v,this.to.flags[i])), | |
| velocity:this.from.velocity.map((v,i)=>mix(v,this.to.velocity[i])),frequency:mix(this.from.frequency,this.to.frequency)}; | |
| } | |
| setProfile(value,{rate=1}={}) { | |
| const target=profile(value,rate),current=this.at(this.time); | |
| this.from=current;this.to=target;this.start=this.time; | |
| } | |
| step() { | |
| const p=this.at(this.time),input=new Float32Array(34),delta=[0,0,0]; | |
| // Same fixed-step trajectory integration used in the training targets. | |
| for(let n=1;n<=36;n++) { | |
| const previous=this.at(this.time+(n-1)*DT); | |
| for(let j=0;j<3;j++)delta[j]+=previous.velocity[j]*DT; | |
| if(n%12===0){const future=this.at(this.time+n*DT);input.set([delta[0],delta[2],future.velocity[0],future.velocity[2],0,1],4+6*(n/12-1));} | |
| } | |
| input.set(input.subarray(6,10),0); | |
| input.set([Math.sin(this.phase*2*Math.PI),Math.cos(this.phase*2*Math.PI),p.frequency],22);input.set(p.flags,25); | |
| this.phase=(this.phase+p.frequency*DT)%1; | |
| for(let j=0;j<3;j++)this.position[j]+=p.velocity[j]*DT; | |
| this.time+=DT;this.frame++; | |
| return {input,phase:this.phase,simulationPosition:this.position.slice(),frequency:p.frequency, | |
| speed:Math.hypot(...p.velocity),flags:p.flags,frame:this.frame}; | |
| } | |
| } | |