001package polaris.sdk.ui; 002/** Frame-rate-independent damped spring. Values are render state, never layout/hit-test state. */ 003public final class MotionValue { 004 private double value,velocity,last;private boolean initialized; 005 public MotionValue(){}public MotionValue(float value){snap(value,0);} 006 public void snap(float target,double time){value=target;velocity=0;last=time;initialized=true;} 007 public float value(){return (float)value;}public float velocity(){return (float)velocity;} 008 public float to(float target,double time,float frequency,float damping,boolean reduced){ 009 if(!initialized||reduced){snap(target,time);return target;} 010 double dt=Math.max(0,Math.min(.10,time-last));last=time;if(dt==0)return (float)value; 011 double omega=Math.max(.1,frequency),zeta=Math.max(.05,Math.min(.999,damping)); 012 double wd=omega*Math.sqrt(1-zeta*zeta),y=value-target,v=velocity; 013 double a=y,b=(v+zeta*omega*y)/wd,co=Math.cos(wd*dt),si=Math.sin(wd*dt),decay=Math.exp(-zeta*omega*dt); 014 double next=decay*(a*co+b*si); 015 velocity=decay*((-a*wd*si+b*wd*co)-zeta*omega*(a*co+b*si));value=target+next; 016 if(Math.abs(value-target)<.00002&&Math.abs(velocity)<.0002){value=target;velocity=0;} 017 return (float)value; 018 } 019 /** Critically predictable exponential tracking: no stale spring recoil on release. */ 020 public float follow(float target,double time,float response,boolean reduced){ 021 if(!initialized||reduced){snap(target,time);return target;} 022 double dt=Math.max(0,Math.min(.10,time-last));last=time;if(dt==0)return (float)value; 023 double previous=value;value+=(target-value)*(-Math.expm1(-Math.max(1,response)*dt)); 024 velocity=(value-previous)/dt; 025 if(Math.abs(value-target)<.00002){value=target;velocity=0;} 026 return (float)value; 027 } 028 public static float clamp(float x){return Math.max(0,Math.min(1,x));} 029}