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}