Home

hersto:  Chasers.wrl

Source of Chasers.wrl:

Here is a link for right-clicking and downloading the file: Chasers.wrl (49.8 K Byte),

and below is its content to view online.


#VRML V2.0 utf8


WorldInfo
{
    title "Damper nodes"

    info
    [
        "The ExternProto nodes found in this file implement principels described in"
        "the paper 'Linear Filters - Animating Objects in a Flexible and Pleasing Way'."
        "They have been proposed and added to the X3D standard in 2006."
        "Webpage: http://www.hersto.net/Followers"
        ""
        "Please use the code in this file in any content or application you like"
        "or modify it in any way."
        ""
        "The code here works, however things like detecting when a transition has ended"
        "and when the node can stop calculating and updating the output or secondary fields"
        "like set_value or initial_destination are not yet implemented."
        "Nevertheless, set_destination and value_changed do work."
        ""
    ]
}




#
# These commented out EXTERNPROTO statements may help
# you include the nodes.
#



#
#EXTERNPROTO PositionChaser
#[
#    eventIn  SFVec3f     set_destination
#    eventOut SFVec3f         value_changed
#
#    field    SFVec3f initial_destination  # 0 0 0
#    field    SFVec3f initial_value        # 0 0 0
#    eventIn  SFVec3f     set_value
#
#    field    SFTime          duration     # 1
#
#    eventOut SFBool          isActive
#]
#[
#    "urn:inet:hersto.net:node:PositionChaser"
#    "Chasers.wrl#PositionChaser"
#    "http://www.hersto.net/Followers/Chasers.wrl#PositionChaser"
#]
#



#
#EXTERNPROTO OrientationChaser
#[
#    eventIn  SFRotation     set_destination
#    eventOut SFRotation         value_changed
#
#    field    SFRotation initial_destination  # 0 0 1 0
#    field    SFRotation initial_value        # 0 0 1 0
#    eventIn  SFRotation     set_value
#
#    field    SFTime          duration        # 1
#
#    eventOut SFBool          isActive
#]
#[
#    "urn:inet:hersto.net:node:OrientationChaser"
#    "Chasers.wrl#OrientationChaser"
#    "http://www.hersto.net/Followers/Chasers.wrl#OrientationChaser"
#]
#



#
#EXTERNPROTO Position2fChaser
#[
#    eventIn  SFVec2f     set_destination
#    eventOut SFVec2f         value_changed
#
#    field    SFVec2f initial_destination    # 0 0
#    field    SFVec2f initial_value          # 0 0
#    eventIn  SFVec2f     set_value
#
#    field    SFTime          duration       # 1
#
#    eventOut SFBool          isActive
#]
#[
#    "urn:inet:hersto.net:node:Position2fChaser"
#    "Chasers.wrl#Position2fChaser"
#    "http://www.hersto.net/Followers/Chasers.wrl#Position2fChaser"
#]
#






#EXTERNPROTO PlacementChaser
#[
#    eventIn  SFVec3f     set_destinationPos
#    eventIn  SFRotation  set_destinationOri
#    eventOut SFVec3f         valuePos_changed
#    eventOut SFRotation      valueOri_changed


#    field    SFVec3f    initial_destinationPos  # 0 0 0
#    field    SFRotation initial_destinationOri  # 0 0 1 0
#    field    SFVec3f    initial_valuePos        # 0 0 0
#    field    SFRotation initial_valueOri        # 0 0 1 0
#    eventIn  SFVec3f     set_valuePos
#    eventIn  SFRotation  set_valueOri

#    field    SFTime          duration           # 1

#    eventOut SFBool          isActive

#    eventOut SFBool   isLoaded
#]
#[
#    "urn:inet:hersto.net:node:PlacementChaser"
#    "Chasers.wrl#PlacementChaser"
#    "http://www.hersto.net/Followers/Chasers.wrl#PlacementChaser"
#]








PROTO PositionChaser
[
    eventIn  SFVec3f     set_destination
    eventOut SFVec3f         value_changed

    field    SFVec3f initial_destination  0 0 0
    field    SFVec3f initial_value        0 0 0
    eventIn  SFVec3f     set_value

    field    SFTime          duration     1

    eventOut SFBool          isActive

    field    MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    DEF ScrPosDamp Script
    {
        #interface
        eventIn  SFVec3f     set_destination     IS set_destination
        eventOut SFVec3f         value_changed   IS value_changed

        field    SFVec3f initial_destination     IS initial_destination
        field    SFVec3f initial_value           IS initial_value
        eventIn  SFVec3f     set_value           IS set_value

        field    SFTime          duration        IS duration

        eventOut SFBool          isActive        IS isActive

        eventIn  SFTime          Tick

        #config
        field    SFInt32        cNumSupports  10  # number of support points in the Buffer array.


        #internal



        field    SFVec3f        destination     0 0 0   # Destination can somehow be seen as Buffer[-1].

        field    MFVec3f        Buffer [] # Buffer[0] is the newest value, and Buffer[cNumSupports-1] the oldest one.

        field    SFTime         BufferEndTime   0  # which point in time Buffer[0] is associated with.
        field    SFTime        cStepTime        0  # the delta time between two adjacent values in Buffer. It equals to duration / cNumSupports and does not change.

        field    SFVec3f        previousValue   0 0 0 # The value that has just been shifted out of the buffer. It's step response has
                                                      # already reached its destination. We use it as a starting point for adding the
                                                      # step responses still being in progress.
                                                      # It can be seen as Buffer[Buffer.length].


        field SFBool bInitialized FALSE

        url "vrmlscript:


        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;  // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
                Init();
            }
        }

        function Init()
        {
            destination= initial_destination;

            Buffer.length= cNumSupports;

            Buffer[0]= initial_destination;
            for(var C= 1; C<Buffer.length; C++ )
                Buffer[C]= initial_value;

            previousValue= initial_value;

            cStepTime= duration / cNumSupports;
        }



        function set_destination(Dest, Now)
        {
            CheckInit();

            destination= Dest;
            // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer.
            // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the
            // output because Buffer[0] is associated with a value in the past.

            UpdateBuffer(Now);
        }



        function Tick(Now)
        {
            CheckInit();

            if(!BufferEndTime)
            {
                BufferEndTime= Now; // first event we received, so we are in the initialization phase.

                value_changed= initial_value;
        return;
            }

            var Frac= UpdateBuffer(Now);
            // Frac is a value in   0 <= Frac < 1.

            // Now we can calculate the output.
            // This means we calculate the delta between each entry in Buffer and its previous
            // entries, calculate the step response of each such step and add it to form the output.

            // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has
            // no previous value. More exactly, we haven't stored a previous value anymore.
            // However, the step response of that missing previous value has already reached its
            // destination, so we can - would we have that previous value - use this as a start point
            // for adding the step responses.
            // Actually UpdateBuffer(.) maintains this value in

            var Output= previousValue;

            var DeltaIn= Buffer[Buffer.length - 1].subtract(previousValue);

            var DeltaOut= DeltaIn.multiply(StepResponse((Buffer.length - 1 + Frac) * cStepTime));

            Output= Output.add(DeltaOut);

            for(var C= Buffer.length - 2; C>=0; C-- )
            {
                var DeltaIn= Buffer[C].subtract(Buffer[C + 1]);

                var DeltaOut= DeltaIn.multiply(StepResponse((C + Frac) * cStepTime));

                Output= Output.add(DeltaOut);
            }


            if(Output != value_changed)
                value_changed= Output;
        }




        function UpdateBuffer(Now)
        {
            var Frac= (Now - BufferEndTime) / cStepTime;
            // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
            // of the oldest entry has already reached its destination, and it's time for a newer entry.
            // has already reached it
            // In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.

            if(Frac >= 1)
            {
                var NumToShift= Math.floor(Frac);
                Frac-= NumToShift;

                if(NumToShift < Buffer.length)
                {   // normal case.

                    previousValue= Buffer[Buffer.length - NumToShift];

                    for(var C= Buffer.length - 1; C>=NumToShift; C-- )
                        Buffer[C]= Buffer[C - NumToShift];

                    for(var C= 0; C<NumToShift; C++ )
                    {
                        // Hmm, we have a destination value, but don't know how it has
                        // reached the current state.
                        // Therefore we do a linear interpolation from the latest value in the buffer to destination.

                        var Alpha= C / NumToShift;

                        Buffer[C]= Buffer[NumToShift].multiply(Alpha).add(destination.multiply((1 - Alpha)));
                    }
                }else
                {
                    // degenerated case:
                    //
                    // We have a _VERY_ low frame rate...
                    // we can only guess how we should fill the array.
                    // Maybe we could write part of a linear interpolation
                    // from Buffer[0] to destination, that goes from BufferEndTime to Now
                    // (possibly only the end of the interpolation is to be written),
                    // but if we rech here we are in a very degenerate case...
                    // Thus we just write destination to the buffer.

                    previousValue= NumToShift == Buffer.length? Buffer[0] : destination;

                    for(var C= 0; C<Buffer.length; C++ )
                        Buffer[C]= destination;
                }

                BufferEndTime+= NumToShift * cStepTime;
            }

        return Frac;
        }



        function StepResponse(t)
        {
            if(t < 0)
        return 0;

            if(t > duration)
        return 1;

            // When optimizing for speed, the above two if(.) cases can be omitted,
            // as this funciton will not be called for values outside of 0..duration.

        return StepResponseCore(t / duration);
        }


        // This function defines the shape of how the output responds to the input.
        // It must accept values for T in the range 0 <= T <= 1.
        // In order to create a smooth animation, it should return 0 for T == 0,
        // 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.

        // It should be optimized for speed, in order for high performance. It's
        // executed Buffer.length + 1 times each simulation tick.
        function StepResponseCore(T)
        {
        return .5 - .5 * Math.cos(T * Math.PI);
        }


        // The following functions are not used. They provide other, responses (for fun)
        function StepResponseCoreF(T)
        {
            var cTau= .3;
            var cFrequency= 2.5;
      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
        }


        function StepResponseCoreE(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreD(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreC(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreB(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreA(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

            var Alpha= .2 * T;
        return A * (1 - Alpha) + B * Alpha;
        }

        "
    }


    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Timer.time TO ScrPosDamp.Tick
}








PROTO OrientationChaser
[
    eventIn  SFRotation     set_destination
    eventOut SFRotation         value_changed

    field    SFRotation initial_destination  0 0 1 0
    field    SFRotation initial_value        0 0 1 0
    eventIn  SFRotation     set_value

    field    SFTime          duration     1

    eventOut SFBool          isActive

    field    MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    DEF ScrPosDamp Script
    {
        #interface
        eventIn  SFRotation     set_destination     IS set_destination
        eventOut SFRotation         value_changed   IS value_changed

        field    SFRotation initial_destination     IS initial_destination
        field    SFRotation initial_value           IS initial_value
        eventIn  SFRotation     set_value           IS set_value

        field    SFTime          duration        IS duration

        eventOut SFBool          isActive        IS isActive

        eventIn  SFTime          Tick

        #config
        field    SFInt32        cNumSupports  10  # number of support points in the Buffer array.


        #internal



        field    SFRotation        destination     0 0 1 0   # Destination can somehow be seen as Buffer[-1].

        field    MFRotation        Buffer [] # Buffer[0] is the newest value, and Buffer[cNumSupports-1] the oldest one.

        field    SFTime         BufferEndTime   0  # which point in time Buffer[0] is associated with.
        field    SFTime        cStepTime        0  # the delta time between two adjacent values in Buffer. It equals to duration / cNumSupports and does not change.

        field    SFRotation        previousValue  0 0 1 0 # The value that has just been shifted out of the buffer. It's step response has
                                                      # already reached its destination. We use it as a starting point for adding the
                                                      # step responses still being in progress.
                                                      # It can be seen as Buffer[Buffer.length].


        field SFBool bInitialized FALSE

        url "vrmlscript:


        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;  // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
                Init();
            }
        }

        function Init()
        {
            destination= initial_destination;

            Buffer.length= cNumSupports;

            Buffer[0]= initial_destination;
            for(var C= 1; C<Buffer.length; C++ )
                Buffer[C]= initial_value;

            previousValue= initial_value;

            cStepTime= duration / cNumSupports;
        }



        function set_destination(Dest, Now)
        {
            CheckInit();

            destination= Dest;
            // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer.
            // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the
            // output because Buffer[0] is associated with a value in the past.

            UpdateBuffer(Now);
        }



        function Tick(Now)
        {
            CheckInit();

            if(!BufferEndTime)
            {
                BufferEndTime= Now; // first event we received, so we are in the initialization phase.

                value_changed= initial_value;
        return;
            }

            var Frac= UpdateBuffer(Now);
            // Frac is a value in   0 <= Frac < 1.

            // Now we can calculate the output.
            // This means we calculate the delta between each entry in Buffer and its previous
            // entries, calculate the step response of each such step and add it to form the output.

            // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has
            // no previous value. More exactly, we haven't stored a previous value anymore.
            // However, the step response of that missing previous value has already reached its
            // destination, so we can - would we have that previous value - use this as a start point
            // for adding the step responses.
            // Actually UpdateBuffer(.) maintains this value in

            var Output= previousValue;

            var DeltaIn= previousValue.inverse().multiply(Buffer[Buffer.length - 1]);

            Output= Output.slerp(Output.multiply(DeltaIn), StepResponse((Buffer.length - 1 + Frac) * cStepTime));

            for(var C= Buffer.length - 2; C>=0; C-- )
            {
                var DeltaIn= Buffer[C + 1].inverse().multiply(Buffer[C]);

                Output= Output.slerp(Output.multiply(DeltaIn), StepResponse((C + Frac) * cStepTime));
            }


            if(Output != value_changed)
                value_changed= Output;
        }




        function UpdateBuffer(Now)
        {
            var Frac= (Now - BufferEndTime) / cStepTime;
            // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
            // of the oldest entry has already reached its destination, and it's time for a newer entry.
            // has already reached it
            // In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.

            if(Frac >= 1)
            {
                var NumToShift= Math.floor(Frac);
                Frac-= NumToShift;

                if(NumToShift < Buffer.length)
                {   // normal case.

                    previousValue= Buffer[Buffer.length - NumToShift];

                    for(var C= Buffer.length - 1; C>=NumToShift; C-- )
                        Buffer[C]= Buffer[C - NumToShift];

                    for(var C= 0; C<NumToShift; C++ )
                    {
                        // Hmm, we have a destination value, but don't know how it has
                        // reached the current state.
                        // Therefore we do a linear interpolation from the latest value in the buffer to destination.

                        Buffer[C]= destination.slerp(Buffer[NumToShift], C / NumToShift);
                    }
                }else
                {
                    // degenerated case:
                    //
                    // We have a _VERY_ low frame rate...
                    // we can only guess how we should fill the array.
                    // Maybe we could write part of a linear interpolation
                    // from Buffer[0] to destination, that goes from BufferEndTime to Now
                    // (possibly only the end of the interpolation is to be written),
                    // but if we rech here we are in a very degenerate case...
                    // Thus we just write destination to the buffer.

                    previousValue= NumToShift == Buffer.length? Buffer[0] : destination;

                    for(var C= 0; C<Buffer.length; C++ )
                        Buffer[C]= destination;
                }

                BufferEndTime+= NumToShift * cStepTime;
            }

        return Frac;
        }



        function StepResponse(t)
        {
            if(t < 0)
        return 0;

            if(t > duration)
        return 1;

            // When optimizing for speed, the above two if(.) cases can be omitted,
            // as this funciton will not be called for values outside of 0..duration.

        return StepResponseCore(t / duration);
        }


        // This function defines the shape of how the output responds to the input.
        // It must accept values for T in the range 0 <= T <= 1.
        // In order to create a smooth animation, it should return 0 for T == 0,
        // 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.

        // It should be optimized for speed, in order for high performance. It's
        // executed Buffer.length + 1 times each simulation tick.
        function StepResponseCore(T)
        {
        return .5 - .5 * Math.cos(T * Math.PI);
        }

        // The following functions are not used. They provide other, responses (for fun)

        function StepResponseCoreG(T)
        {
            var cTau= .3;
            var cFrequency= 5;
        return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
        }



        function StepResponseCoreF(T)
        {
            var cTau= .3;
            var cFrequency= 2.5;
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
        }


        function StepResponseCoreE(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreD(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreC(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreB(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreA(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

            var Alpha= .2 * T;
        return A * (1 - Alpha) + B * Alpha;
        }

        "
    }


    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Timer.time TO ScrPosDamp.Tick
}




PROTO Position2fChaser
[
    eventIn  SFVec2f     set_destination
    eventOut SFVec2f         value_changed

    field    SFVec2f initial_destination  0 0
    field    SFVec2f initial_value        0 0
    eventIn  SFVec2f     set_value

    field    SFTime          duration     1

    eventOut SFBool          isActive

    field    MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    DEF ScrPosDamp Script
    {
        #interface
        eventIn  SFVec2f     set_destination     IS set_destination
        eventOut SFVec2f         value_changed   IS value_changed

        field    SFVec2f initial_destination     IS initial_destination
        field    SFVec2f initial_value           IS initial_value
        eventIn  SFVec2f     set_value           IS set_value

        field    SFTime          duration        IS duration

        eventOut SFBool          isActive        IS isActive

        eventIn  SFTime          Tick

        #config
        field    SFInt32        cNumSupports  10  # number of support points in the Buffer array.


        #internal



        field    SFVec2f        destination     0 0   # Destination can somehow be seen as Buffer[-1].

        field    MFVec2f        Buffer [] # Buffer[0] is the newest value, and Buffer[cNumSupports-1] the oldest one.

        field    SFTime         BufferEndTime   0  # which point in time Buffer[0] is associated with.
        field    SFTime        cStepTime        0  # the delta time between two adjacent values in Buffer. It equals to duration / cNumSupports and does not change.

        field    SFVec2f        previousValue   0 0 # The value that has just been shifted out of the buffer. It's step response has
                                                    # already reached its destination. We use it as a starting point for adding the
                                                    # step responses still being in progress.
                                                    # It can be seen as Buffer[Buffer.length].


        field SFBool bInitialized FALSE

        url "vrmlscript:


        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;  // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
                Init();
            }
        }

        function Init()
        {
            destination= initial_destination;

            Buffer.length= cNumSupports;

            Buffer[0]= initial_destination;
            for(var C= 1; C<Buffer.length; C++ )
                Buffer[C]= initial_value;

            previousValue= initial_value;

            cStepTime= duration / cNumSupports;
        }



        function set_destination(Dest, Now)
        {
            CheckInit();

            destination= Dest;
            // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer.
            // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the
            // output because Buffer[0] is associated with a value in the past.

            UpdateBuffer(Now);
        }



        function Tick(Now)
        {
            CheckInit();

            if(!BufferEndTime)
            {
                BufferEndTime= Now; // first event we received, so we are in the initialization phase.

                value_changed= initial_value;
        return;
            }

            var Frac= UpdateBuffer(Now);
            // Frac is a value in   0 <= Frac < 1.

            // Now we can calculate the output.
            // This means we calculate the delta between each entry in Buffer and its previous
            // entries, calculate the step response of each such step and add it to form the output.

            // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has
            // no previous value. More exactly, we haven't stored a previous value anymore.
            // However, the step response of that missing previous value has already reached its
            // destination, so we can - would we have that previous value - use this as a start point
            // for adding the step responses.
            // Actually UpdateBuffer(.) maintains this value in

            var Output= previousValue;

            var DeltaIn= Buffer[Buffer.length - 1].subtract(previousValue);

            var DeltaOut= DeltaIn.multiply(StepResponse((Buffer.length - 1 + Frac) * cStepTime));

            Output= Output.add(DeltaOut);

            for(var C= Buffer.length - 2; C>=0; C-- )
            {
                var DeltaIn= Buffer[C].subtract(Buffer[C + 1]);

                var DeltaOut= DeltaIn.multiply(StepResponse((C + Frac) * cStepTime));

                Output= Output.add(DeltaOut);
            }


            if(Output != value_changed)
                value_changed= Output;
        }




        function UpdateBuffer(Now)
        {
            var Frac= (Now - BufferEndTime) / cStepTime;
            // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
            // of the oldest entry has already reached its destination, and it's time for a newer entry.
            // has already reached it
            // In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.

            if(Frac >= 1)
            {
                var NumToShift= Math.floor(Frac);
                Frac-= NumToShift;

                if(NumToShift < Buffer.length)
                {   // normal case.

                    previousValue= Buffer[Buffer.length - NumToShift];

                    for(var C= Buffer.length - 1; C>=NumToShift; C-- )
                        Buffer[C]= Buffer[C - NumToShift];

                    for(var C= 0; C<NumToShift; C++ )
                    {
                        // Hmm, we have a destination value, but don't know how it has
                        // reached the current state.
                        // Therefore we do a linear interpolation from the latest value in the buffer to destination.

                        var Alpha= C / NumToShift;

                        Buffer[C]= Buffer[NumToShift].multiply(Alpha).add(destination.multiply((1 - Alpha)));
                    }
                }else
                {
                    // degenerated case:
                    //
                    // We have a _VERY_ low frame rate...
                    // we can only guess how we should fill the array.
                    // Maybe we could write part of a linear interpolation
                    // from Buffer[0] to destination, that goes from BufferEndTime to Now
                    // (possibly only the end of the interpolation is to be written),
                    // but if we rech here we are in a very degenerate case...
                    // Thus we just write destination to the buffer.

                    previousValue= NumToShift == Buffer.length? Buffer[0] : destination;

                    for(var C= 0; C<Buffer.length; C++ )
                        Buffer[C]= destination;
                }

                BufferEndTime+= NumToShift * cStepTime;
            }

        return Frac;
        }



        function StepResponse(t)
        {
            if(t < 0)
        return 0;

            if(t > duration)
        return 1;

            // When optimizing for speed, the above two if(.) cases can be omitted,
            // as this funciton will not be called for values outside of 0..duration.

        return StepResponseCore(t / duration);
        }


        // This function defines the shape of how the output responds to the input.
        // It must accept values for T in the range 0 <= T <= 1.
        // In order to create a smooth animation, it should return 0 for T == 0,
        // 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.

        // It should be optimized for speed, in order for high performance. It's
        // executed Buffer.length + 1 times each simulation tick.
        function StepResponseCore(T)
        {
        return .5 - .5 * Math.cos(T * Math.PI);
        }


        // The following functions are not used. They provide other, responses (for fun)
        function StepResponseCoreF(T)
        {
            var cTau= .3;
            var cFrequency= 2.5;
      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
        }


        function StepResponseCoreE(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreD(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreC(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreB(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreA(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

            var Alpha= .2 * T;
        return A * (1 - Alpha) + B * Alpha;
        }

        "
    }


    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Timer.time TO ScrPosDamp.Tick
}





PROTO PlacementChaser
[
    eventIn  SFVec3f     set_destinationPos
    eventIn  SFRotation  set_destinationOri
    eventOut SFVec3f         valuePos_changed
    eventOut SFRotation      valueOri_changed


    field    SFVec3f    initial_destinationPos  0 0 0
    field    SFRotation initial_destinationOri  0 0 1 0
    field    SFVec3f    initial_valuePos        0 0 0
    field    SFRotation initial_valueOri        0 0 1 0
    eventIn  SFVec3f     set_valuePos
    eventIn  SFRotation  set_valueOri

    field    SFTime          duration     1

    eventOut SFBool          isActive

    eventOut SFBool   isLoaded

    field    MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    DEF ScrPosDamp Script
    {
        #interface
        eventIn  SFVec3f     set_destinationPos     IS set_destinationPos
        eventIn  SFRotation  set_destinationOri     IS set_destinationOri
        eventOut SFVec3f         valuePos_changed   IS valuePos_changed
        eventOut SFRotation      valueOri_changed   IS valueOri_changed

        field    SFVec3f    initial_destinationPos     IS initial_destinationPos
        field    SFRotation initial_destinationOri     IS initial_destinationOri
        field    SFVec3f    initial_valuePos           IS initial_valuePos
        field    SFRotation initial_valueOri           IS initial_valueOri
        eventIn  SFVec3f        set_valuePos           IS set_valuePos
        eventIn  SFRotation     set_valueOri           IS set_valueOri

        field    SFTime          duration        IS duration

        eventOut SFBool          isActive        IS isActive

        eventIn  SFTime          Tick

        #config
        field    SFInt32        cNumSupports  10  # number of support points in the Buffer array.


        #internal



        field    SFVec3f        destinationPos     0 0 0     # Destination can somehow be seen as Buffer[-1].
        field    SFRotation     destinationOri     0 0 1 0   # Destination can somehow be seen as Buffer[-1].

        field    MFVec3f        BufferPos [] # Buffer[0] is the newest value, and Buffer[cNumSupports-1] the oldest one.
        field    MFRotation     BufferOri [] # Buffer[0] is the newest value, and Buffer[cNumSupports-1] the oldest one.

        field    SFTime         BufferEndTime   0  # which point in time Buffer[0] is associated with.
        field    SFTime        cStepTime        0  # the delta time between two adjacent values in Buffer. It equals to duration / cNumSupports and does not change.

        field    SFVec3f        previousValuePos   0 0 0   # The value that has just been shifted out of the buffer. It's step response has
        field    SFRotation     previousValueOri   0 0 1 0 # The value that has just been shifted out of the buffer. It's step response has
                                                         # already reached its destination. We use it as a starting point for adding the
                                                         # step responses still being in progress.
                                                         # It can be seen as Buffer[Buffer.length].


        field SFBool bInitialized FALSE

        url "vrmlscript:


        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;  // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
                Init();
            }
        }

        function Init()
        {
            destinationPos= initial_destinationPos;
            destinationOri= initial_destinationOri;

            BufferPos.length=
            BufferOri.length= cNumSupports;

            BufferPos[0]= initial_destinationPos;
            BufferOri[0]= initial_destinationOri;
            for(var C= 1; C<BufferPos.length; C++ )
            {
                BufferPos[C]= initial_valuePos;
                BufferOri[C]= initial_valueOri;
            }

            previousValuePos= initial_valuePos;
            previousValueOri= initial_valueOri;

            cStepTime= duration / cNumSupports;
        }



        function set_destinationPos(Dest, Now)
        {
            CheckInit();

            destinationPos= Dest;
            // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer.
            // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the
            // output because Buffer[0] is associated with a value in the past.

            //UpdateBuffer(Now);
        }

        function set_destinationOri(Dest, Now)
        {
            CheckInit();

            destinationOri= Dest;
            // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer.
            // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the
            // output because Buffer[0] is associated with a value in the past.

            //UpdateBuffer(Now);
        }


        function Tick(Now)
        {
            CheckInit();

            if(!BufferEndTime)
            {
                BufferEndTime= Now; // first event we received, so we are in the initialization phase.

                valuePos_changed= initial_valuePos;
                valueOri_changed= initial_valueOri;
        return;
            }

            var Frac= UpdateBuffer(Now);
            // Frac is a value in   0 <= Frac < 1.

            // Now we can calculate the output.
            // This means we calculate the delta between each entry in Buffer and its previous
            // entries, calculate the step response of each such step and add it to form the output.

            // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has
            // no previous value. More exactly, we haven't stored a previous value anymore.
            // However, the step response of that missing previous value has already reached its
            // destination, so we can - would we have that previous value - use this as a start point
            // for adding the step responses.
            // Actually UpdateBuffer(.) maintains this value in

            var OutputPos= previousValuePos;
            var OutputOri= previousValueOri;

            var DeltaInPos= BufferPos[BufferPos.length - 1].subtract(previousValuePos);
            var DeltaInOri= previousValueOri.inverse().multiply(BufferOri[BufferOri.length - 1]);

            var DeltaOutPos= DeltaInPos.multiply(StepResponse((BufferPos.length - 1 + Frac) * cStepTime));

            OutputPos= OutputPos.add(DeltaOutPos);
            OutputOri= OutputOri.slerp(OutputOri.multiply(DeltaInOri), StepResponse((BufferOri.length - 1 + Frac) * cStepTime));

            for(var C= BufferPos.length - 2; C>=0; C-- )
            {
                var DeltaInPos= BufferPos[C].subtract(BufferPos[C + 1]);
                var DeltaInOri= BufferOri[C + 1].inverse().multiply(BufferOri[C]);

                var DeltaOutPos= DeltaInPos.multiply(StepResponse((C + Frac) * cStepTime));

                OutputPos= OutputPos.add(DeltaOutPos);
                OutputOri= OutputOri.slerp(OutputOri.multiply(DeltaInOri), StepResponse((C + Frac) * cStepTime));
            }


            if(OutputPos != valuePos_changed)
                valuePos_changed= OutputPos;

            if(OutputOri != valueOri_changed)
                valueOri_changed= OutputOri;
        }




        function UpdateBuffer(Now)
        {
            var Frac= (Now - BufferEndTime) / cStepTime;
            // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
            // of the oldest entry has already reached its destination, and it's time for a newer entry.
            // has already reached it
            // In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.

            if(Frac >= 1)
            {
                var NumToShift= Math.floor(Frac);
                Frac-= NumToShift;

                if(NumToShift < BufferPos.length)
                {   // normal case.

                    previousValuePos= BufferPos[BufferPos.length - NumToShift];
                    previousValueOri= BufferOri[BufferOri.length - NumToShift];

                    for(var C= BufferPos.length - 1; C>=NumToShift; C-- )
                    {
                        BufferPos[C]= BufferPos[C - NumToShift];
                        BufferOri[C]= BufferOri[C - NumToShift];
                    }

                    for(var C= 0; C<NumToShift; C++ )
                    {
                        // Hmm, we have a destination value, but don't know how it has
                        // reached the current state.
                        // Therefore we do a linear interpolation from the latest value in the buffer to destination.

                        var Alpha= C / NumToShift;

                        BufferPos[C]= BufferPos[NumToShift].multiply(Alpha).add(destinationPos.multiply((1 - Alpha)));
                        BufferOri[C]= destinationOri.slerp(BufferOri[NumToShift], Alpha);
                    }
                }else
                {
                    // degenerated case:
                    //
                    // We have a _VERY_ low frame rate...
                    // we can only guess how we should fill the array.
                    // Maybe we could write part of a linear interpolation
                    // from Buffer[0] to destination, that goes from BufferEndTime to Now
                    // (possibly only the end of the interpolation is to be written),
                    // but if we rech here we are in a very degenerate case...
                    // Thus we just write destination to the buffer.

                    previousValuePos= NumToShift == BufferPos.length? BufferPos[0] : destinationPos;
                    previousValueOri= NumToShift == BufferOri.length? BufferOri[0] : destinationOri;

                    for(var C= 0; C<BufferPos.length; C++ )
                    {
                        BufferPos[C]= destinationPos;
                        BufferOri[C]= destinationOri;
                    }
                }

                BufferEndTime+= NumToShift * cStepTime;
            }

        return Frac;
        }



        function StepResponse(t)
        {
            if(t < 0)
        return 0;

            if(t > duration)
        return 1;

            // When optimizing for speed, the above two if(.) cases can be omitted,
            // as this funciton will not be called for values outside of 0..duration.

        return StepResponseCore(t / duration);
        }


        // This function defines the shape of how the output responds to the input.
        // It must accept values for T in the range 0 <= T <= 1.
        // In order to create a smooth animation, it should return 0 for T == 0,
        // 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.

        // It should be optimized for speed, in order for high performance. It's
        // executed Buffer.length + 1 times each simulation tick.
        function StepResponseCore(T)
        {
        return .5 - .5 * Math.cos(T * Math.PI);
        }


        // The following functions are not used. They provide other, responses (for fun)
        function StepResponseCoreF(T)
        {
            var cTau= .3;
            var cFrequency= 2.5;
      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
//      return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
        }


        function StepResponseCoreE(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreD(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cFrequency= 2.5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);

        return A * .8 + B * .2;
        }


        function StepResponseCoreC(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreB(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

        return A * .8 + B * .2;
        }


        function StepResponseCoreA(T)
        {
            var A= .5 - .5 * Math.cos(T * Math.PI);

          var cTau= .3;
          var cFrequency= 5;
            var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);

            var Alpha= .2 * T;
        return A * (1 - Alpha) + B * Alpha;
        }

        "
    }


    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Timer.time TO ScrPosDamp.Tick


    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}



__.-.__
end of document