
/*
 * Original URL: http://www.hersto.com/Followers/
 *
 * Copyright (C) 2007 by Herbert Stocker
 *
 * You are allowed use/modify/port this software in any way you like
 * if you keep this terms of use, the reference to the original
 * author Herbert Stocker and the link to the original URL intact
 * and at a prominent place (the begining of a file) in your source file.
 *
 */





function ScalarDamper(Params)
{
  if(Params == undefined)  Params= {};

  this.mTau=     Params.Tau   == undefined? 1             : Params.Tau;
  this.mOrder=   Params.Order == undefined? 1             : Params.Order;
  this.mEps=     Params.Eps   == undefined? .001          : Params.Eps;
  this.mValueO1= Params.Value == undefined? 0             : Params.Value;
  this.mValueO0= Params.Dest  == undefined? this.mValueO1 : Params.Dest;

  this.mValueO2=
  this.mValueO3=
  this.mValueO4=
  this.mValueO5= this.mValueO1;

  this.mbNeedTimer= false;

  this.mfStartTimer= function()
  {
    if(!this.mbNeedTimer)
    {
      this.mbNeedTimer= true;
      HST_Ticker.Add(this);
      this.Tick(HST_Ticker.getTime(), 0); // TBD: is it good to execute this? In the case of Tau == 0 or order == 0?
    }
  }

  this.mfStopTimer= function()
  {
    this.mbNeedTimer= false;
    HST_Ticker.Remove(this);
  }


  this.set_destination= function(Dest)
  {
    this.mValueO0= Dest;
    this.mfStartTimer();
  }

  this.set_value= function(Val)
  {
    this.mValueO1=
    this.mValueO2=
    this.mValueO3=
    this.mValueO4=
    this.mValueO5= Val;

    this.mfStartTimer();
  }

  this.set_tau= function(Tau)
  {
    this.mTau= Tau;
  }

  this.get_value= function()
  {
    return this.mValueO5;
  }


  this.get_isActive= function()
  {
    return this.mbNeedTimer;
  }


  this.Tick= function(Now, DeltaT)
  {
    if(!this.mbNeedTimer)
  return;


    var Alpha= Math.exp(-DeltaT / this.mTau);

    this.mValueO1= this.mOrder > 0 && !!this.mTau
             ? this.mValueO0 + (this.mValueO1 - this.mValueO0) * Alpha
             : this.mValueO0;

    this.mValueO2= this.mOrder > 1 && !!this.mTau
             ? this.mValueO1 + (this.mValueO2 - this.mValueO1) * Alpha
             : this.mValueO1;

    this.mValueO3= this.mOrder > 2 && !!this.mTau
             ? this.mValueO2 + (this.mValueO3 - this.mValueO2) * Alpha
             : this.mValueO2;

    this.mValueO4= this.mOrder > 3 && !!this.mTau
             ? this.mValueO3 + (this.mValueO4 - this.mValueO3) * Alpha
             : this.mValueO3;

    this.mValueO5= this.mOrder > 4 && !!this.mTau
             ? this.mValueO4 + (this.mValueO5 - this.mValueO4) * Alpha
             : this.mValueO4;


    var Dist= Math.abs(this.mValueO1 - this.mValueO0);
    if(this.mOrder > 1)
    {
        var Dist2= Math.abs(this.mValueO2 - this.mValueO1);
        if( Dist2 > Dist)  Dist= Dist2;
    }
    if(this.mOrder > 2)
    {
        var Dist3= Math.abs(this.mValueO3 - this.mValueO2);
        if( Dist3 > Dist)  Dist= Dist3;
    }
    if(this.mOrder > 3)
    {
        var Dist4= Math.abs(this.mValueO4 - this.mValueO3);
        if( Dist4 > Dist)  Dist= Dist4;
    }
    if(this.mOrder > 4)
    {
        var Dist5= Math.abs(this.mValueO5 - this.mValueO4);
        if( Dist5 > Dist)  Dist= Dist5;
    }

    if(Dist < this.mEps)
    {
        this.mValueO5=
        this.mValueO4=
        this.mValueO3=
        this.mValueO2=
        this.mValueO1=
        this.mValueO0;
        this.mfStopTimer();
        this.mfCallReceiver();
return;
    }

    this.mfCallReceiver();
  }

  this.Stop= function()
  {
    if(this.mbNeedTimer)
      this.mbStopTimer();
  }


  this.SetReceiver= function(Rcv)
  {
    this.mpfReceiver= Rcv;
  }


  this.mfCallReceiver= function()
  {
    if(this.mpfReceiver != undefined)
    {
      this.mpfReceiver(this.mValueO5);
    }
  }


  HST_Ticker.Add(this);
}








function CVector2(X, Y)
{
  this.isA= CVector2;

  this.X= X == undefined? 0 : X;
  this.Y= Y == undefined? 0 : Y;

  this.len= function()
  {
    return Math.sqrt(this.X * this.X + this.Y * this.Y);
  }

  this.len2= function()
  {
    return this.X * this.X + this.Y * this.Y;
  }

  this.sum= function(that)
  {
    return new CVector2( this.X + that.X
                       , this.Y + that.Y
                       );
  }

  this.diff= function(that)
  {
    return new CVector2( this.X - that.X
                       , this.Y - that.Y
                       );
  }

//  this.Add= function(that)
//  {
//    this.X+= that.X;
//    this.Y+= that.Y;
//  }
//
//  this.Sub= function(that)
//  {
//    this.X-= that.X;
//    this.Y-= that.Y;
//  }
//
//  this.Mul= function(Factor)
//  {
//    this.X*= Factor;
//    this.Y*= Factor;
//  }

  this.prod= function(Factor)
  {
    return new CVector2( this.X * Factor
                       , this.Y * Factor
                       );
  }

//  this.Div= function(Dividend)
//  {
//    this.X/= Dividend;
//    this.Y/= Dividend;
//  }

  this.quot= function(Dividend)
  {
    return new CVector2( this.X / Dividend
                       , this.Y / Dividend
                       );
  }


  this.clone= function()
  {
    return new CVector2(this.X, this.Y);
  }


  this.dot= function(that)
  {
    return new CVector2(this.X * that.X + this.Y * that.Y);
  }

  this.norm= function()
  {
    if(this.X || this.Y)
  return this.quot(this.len());
    else
  return new CVector2(0, 0);
  }

//  this.Normalize= function()
//  {
//    if(this.X || this.Y)
//    {
//      var Fac= 1/this.len();
//
//      this.X*= Fac;
//      this.Y*= Fac;
//    }
//  }
}






function CVector2Damper(Params)
{
  if(Params == undefined)  Params= {};

  this.value_changed= undefined; // function(CVector2 Val);

  this.mTau=     Params.Tau   == undefined? 1              : Params.Tau;
  this.mOrder=   Params.Order == undefined? 1              : Params.Order;
  this.mEps=     Params.Eps   == undefined? .001           : Params.Eps;
  this.mValueO1= Params.Value == undefined? new CVector2() : Params.Value;
  this.mValueO0= Params.Dest  == undefined? this.mValueO1  : Params.Dest;

  this.mValueO2=
  this.mValueO3=
  this.mValueO4=
  this.mValueO5= this.mValueO1;

  this.mbNeedTimer= false;

  this.mfStartTimer= function()
  {
    if(!this.mbNeedTimer)
    {
      this.mbNeedTimer= true;
      HST_Ticker.Add(this);
      this.Tick(HST_Ticker.getTime(), 0); // TBD: is it good to execute this? In the case of Tau == 0 or order == 0?
    }
  }

  this.mfStopTimer= function()
  {
    this.mbNeedTimer= false;
    HST_Ticker.Remove(this);
  }


  this.set_destination= function(Dest)
  {
    this.mValueO0= Dest;
    this.mfStartTimer();
  }

  this.set_value= function(Val)
  {
    this.mValueO1=
    this.mValueO2=
    this.mValueO3=
    this.mValueO4=
    this.mValueO5= Val;

    this.mfStartTimer();
  }

  this.set_tau= function(Tau)
  {
    this.mTau= Tau;
  }

  this.get_value= function()
  {
    return this.mValueO5;
  }

  this.get_destination= function()
  {
    return this.mValueO0;
  }


  this.get_isActive= function()
  {
    return this.mbNeedTimer;
  }


  this.Tick= function(Now, DeltaT)
  {
    if(!this.mbNeedTimer)
  return;


    var Alpha= Math.exp(-DeltaT / this.mTau);

    this.mValueO1= this.mOrder > 0 && !!this.mTau
             ? this.mValueO0.sum(this.mValueO1.diff(this.mValueO0).prod(Alpha))
             : this.mValueO0;

    this.mValueO2= this.mOrder > 1 && !!this.mTau
             ? this.mValueO1.sum(this.mValueO2.diff(this.mValueO1).prod(Alpha))
             : this.mValueO1;

    this.mValueO3= this.mOrder > 2 && !!this.mTau
             ? this.mValueO2.sum(this.mValueO3.diff(this.mValueO2).prod(Alpha))
             : this.mValueO2;

    this.mValueO4= this.mOrder > 3 && !!this.mTau
             ? this.mValueO3.sum(this.mValueO4.diff(this.mValueO3).prod(Alpha))
             : this.mValueO3;

    this.mValueO5= this.mOrder > 4 && !!this.mTau
             ? this.mValueO4.sum(this.mValueO5.diff(this.mValueO4).prod(Alpha))
             : this.mValueO4;


    var Dist= this.mValueO1.diff(this.mValueO0).len();
    if(this.mOrder > 1)
    {
        var Dist2= this.mValueO2.diff(this.mValueO1).len();
        if( Dist2 > Dist)  Dist= Dist2;
    }
    if(this.mOrder > 2)
    {
        var Dist3= this.mValueO3.diff(this.mValueO2).len();
        if( Dist3 > Dist)  Dist= Dist3;
    }
    if(this.mOrder > 3)
    {
        var Dist4= this.mValueO4.diff(this.mValueO3).len();
        if( Dist4 > Dist)  Dist= Dist4;
    }
    if(this.mOrder > 4)
    {
        var Dist5= this.mValueO5.diff(this.mValueO4).len();
        if( Dist5 > Dist)  Dist= Dist5;
    }

    if(Dist < this.mEps)
    {
        this.mValueO5=
        this.mValueO4=
        this.mValueO3=
        this.mValueO2=
        this.mValueO1=
        this.mValueO0;
        this.mfStopTimer();
        this.mfCallReceiver();
return;
    }

    this.mfCallReceiver();
  }

  this.Stop= function()
  {
    if(this.mbNeedTimer)
      this.mbStopTimer();
  }



  this.mfCallReceiver= function()
  {
    if(this.value_changed != undefined)
    {
      this.value_changed(this.mValueO5);
    }
  }


  HST_Ticker.Add(this);
}

