Home

hersto:  Dampers.wrl

Source of Dampers.wrl:

Here is a link for right-clicking and downloading the file: Dampers.wrl (58.4 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 you find here in any content or application you like"
        "or modify it in any way."
        ""
        "The code here seams to be bug-free as it has been used in many cases."
    ]
}




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



#
#EXTERNPROTO PositionDamper
#[
#    eventIn      SFVec3f      set_destination
#    field        SFVec3f  initial_destination     # 0 0 0
#
#    eventOut     SFVec3f          value_changed
#    field        SFVec3f  initial_value          # 0 0 0
#    eventIn      SFVec3f      set_value
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    field        SFFloat  reachThreshold          # .01
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   reached
#
#    eventOut     SFBool   isLoaded
#
#    field        SFBool   takeFirstInput          # TRUE
#
#]
#[
#    "urn:inet:hersto.net:node:PositionDamper"
#    "Dampers.wrl#PositionDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#PositionDamper"
#]
#




#
#EXTERNPROTO OrientationDamper
#[
#    eventIn      SFRotation      set_destination
#    field        SFRotation  initial_destination     # 0 0 1 0
#
#    eventOut     SFRotation          value_changed
#    field        SFRotation  initial_value          # 0 0 1 0
#    eventIn      SFRotation      set_value
#
#    exposedField SFFloat  tau                        # 1
#
#    field        SFInt32  order                      # 1         # allowed: [1..5]
#
#    field        SFFloat  eps                        # .001      # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#
#    field        SFBool   takeFirstInput          # TRUE
#
#]
#[
#    "urn:inet:hersto.net:node:OrientationDamper"
#    "Dampers.wrl#OrientationDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#OrientationDamper"
#]
#




#
#EXTERNPROTO ColorDamper
#[
#    eventIn      SFColor      set_destination
#    field        SFColor  initial_destination     # 0 0 0
#
#    eventOut     SFColor          value_changed
#    field        SFColor  initial_value          # 0 0 0
#    eventIn      SFColor      set_value
#
#    exposedField SFFloat  tau                     #  1
#    exposedField SFFloat  negTau                  # -1       # not yet testet
#
#    field        SFInt32  order                   #  1       # allowed: [1..5]
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#    "urn:inet:hersto.net:node:ColorDamper"
#    "Dampers.wrl#ColorDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#ColorDamper"
#]
#




#
#EXTERNPROTO Position2fDamper
#[
#    eventIn      SFVec2f      set_destination
#    field        SFVec2f  initial_destination     # 0 0
#
#    eventOut     SFVec2f          value_changed
#    field        SFVec2f  initial_value          # 0 0
#    eventIn      SFVec2f      set_value
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "urn:inet:hersto.net:node:Position2fDamper"
#    "Dampers.wrl#Position2fDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#Position2fDamper"
#]
#





#
#EXTERNPROTO CoordinateDamper
#[
#    eventIn      MFVec3f      set_destination
#    field        MFVec3f  initial_destination     # []
#
#    eventOut     MFVec3f          value_changed
#    field        MFVec3f  initial_value          # []
#    eventIn      MFVec3f      set_value
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "urn:inet:hersto.net:node:CoordinateDamper"
#    "Dampers.wrl#CoordinateDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#CoordinateDamper"
#]
#




#
#EXTERNPROTO TexCoordDamper
#[
#    eventIn      MFVec2f      set_destination
#    field        MFVec2f  initial_destination     # []
#
#    eventOut     MFVec2f          value_changed
#    field        MFVec2f  initial_value          # []
#    eventIn      MFVec2f      set_value
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "urn:inet:hersto.net:node:TexCoordDamper"
#    "Dampers.wrl#TexCoordDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#TexCoordDamper"
#]
#


#
#EXTERNPROTO ScalarDamper
#[
#    eventIn      SFFloat      set_destination
#    field        SFFloat  initial_destination     # 0
#
#    eventOut     SFFloat          value_changed
#    field        SFFloat  initial_value          # 0
#    eventIn      SFFloat      set_value
#
#    eventOut     SFFloat  transparency_changed
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#
#]
#[
#    "urn:inet:hersto.net:node:ScalarDamper"
#    "Dampers.wrl#ScalarDamper"
#    "http://www.hersto.net/Followers/Dampers.wrl#ScalarDamper"
#]
#







PROTO PositionDamper
[
    eventIn      SFVec3f      set_destination
    field        SFVec3f  initial_destination     0 0 0

    eventOut     SFVec3f          value_changed
    field        SFVec3f  initial_value          0 0 0
    eventIn      SFVec3f      set_value

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    field        SFFloat  reachThreshold .01

    eventOut     SFBool   isActive

    eventOut     SFBool   reached

    eventOut     SFBool   isLoaded

    field        SFBool   takeFirstInput          TRUE

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFVec3f      set_destination    IS     set_destination
        field        SFVec3f          input          IS initial_destination

        eventOut     SFVec3f          value_changed IS         value_changed
        field        SFVec3f  initial_value         IS initial_value
        eventIn      SFVec3f      set_value         IS     set_value

        field        SFNode           effs           USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1
        field        SFFloat          reachThreshold IS reachThreshold

        field        SFInt32          order          IS         order

        field        SFFloat          eps            IS         eps

        field        SFBool           takeFirstInput IS         takeFirstInput

        eventOut     SFBool           reached        IS         reached

        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        SFVec3f value5    0 0 0
        field        SFVec3f value4    0 0 0
        field        SFVec3f value3    0 0 0
        field        SFVec3f value2    0 0 0
        field        SFVec3f value1    0 0 0

        field        SFBool  bInitialized FALSE # This is a workaround because Contact does not allways call initialize() before the first event has been received.

        field        SFBool IsCortona FALSE # does it appear only to me, or does it have problems with receiving values on TimeSensor.enable?

        field        SFBool bNeedToTakeFirstInput TRUE

        url "vrmlscript:

        function StartTimer()
        {
            if(IsCortona)
        return;

            if(!needTimer)
            {
                lastTick= 0;
                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(IsCortona)
        return;

            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;
                Init();
            }

        }


        function Init()
        {
            IsCortona= false && Browser.getName().indexOf('Cortona') != -1;

            bNeedToTakeFirstInput= takeFirstInput;

            tau= effs.tau;
            set_value(initial_value);
            if(IsCortona)
                needTimer= true;
            else
                needTimer=    input.x != initial_value.x
                           || input.y != initial_value.y
                           || input.z != initial_value.z
                           ;
        }


        function set_tau(t)
        {
            CheckInit();

            tau= t;
        }



        function set_destination(i)
        {
            CheckInit();

            if(bNeedToTakeFirstInput)
            {
                bNeedToTakeFirstInput= false;
                set_value(i);
            }


            if(i != input)
            {
                input= i;
                StartTimer();
            }
        }


        function set_value(o)
        {
            CheckInit();

            bNeedToTakeFirstInput= false;

            value1= value2= value3= value4= value5= o;
            value_changed= o;
            UpdateReached();
            StartTimer();
        }



        function tick(now)
        {
            CheckInit();

            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;
            lastTick= now;

            var alpha= Math.exp(-delta / tau);


            if(bNeedToTakeFirstInput)  // then don't do any processing.
        return;



            value1= order > 0 && tau
                       ? input  .add(value1.subtract(input  ).multiply(alpha))
                       : input;

            value2= order > 1 && tau
                       ? value1.add(value2.subtract(value1).multiply(alpha))
                       : value1;

            value3= order > 2 && tau
                       ? value2.add(value3.subtract(value2).multiply(alpha))
                       : value2;

            value4= order > 3 && tau
                       ? value3.add(value4.subtract(value3).multiply(alpha))
                       : value3;

            value5= order > 4 && tau
                       ? value4.add(value5.subtract(value4).multiply(alpha))
                       : value4;


            var dist= GetDist();


            if(dist < eps)
            {
                value1= value2= value3= value4= value5= input;

                value_changed= input;
                UpdateReached2(dist);

                StopTimer();
        return;
            }

            value_changed= value5;
            UpdateReached2(dist);

        }


        function GetDist()
        {
            var dist= value1.subtract(input).length();
            if(order > 1)
            {
                var dist2= value2.subtract(value1).length();
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= value3.subtract(value2).length();
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= value4.subtract(value3).length();
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= value5.subtract(value4).length();
                if( dist5 > dist)  dist= dist5;
            }

        return dist;
        }



        function UpdateReached()
        {
            return UpdateReached2(GetDist());
        }


        function UpdateReached2(Dist)
        {
            if(reached)
            {
                if(Dist > reachThreshold)
                    reached= false;
            }else
            {
                if(Dist <= reachThreshold)
                    reached= true;
            }
        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}




PROTO OrientationDamper
[
    eventIn      SFRotation      set_destination
    field        SFRotation  initial_destination           0 0 1 0

    eventOut     SFRotation          value_changed
    field        SFRotation  initial_value          0 0 1 0
    eventIn      SFRotation      set_value

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded

    field        SFBool   takeFirstInput          FALSE

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFRotation      set_destination    IS     set_destination
        field        SFRotation          input          IS initial_destination

        eventOut     SFRotation          value_changed IS         value_changed
        field        SFRotation  initial_value         IS initial_value
        eventIn      SFRotation      set_value         IS     set_value

        field        SFNode           effs              USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau               1

        field        SFInt32          order             IS         order

        field        SFFloat          eps               IS         eps

        field        SFBool           takeFirstInput    IS         takeFirstInput


        # internal fields
        eventOut     SFBool needTimer                   IS      isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        SFRotation value5    0 0 1 0
        field        SFRotation value4    0 0 1 0
        field        SFRotation value3    0 0 1 0
        field        SFRotation value2    0 0 1 0
        field        SFRotation value1    0 0 1 0


        field        SFBool  bInitialized FALSE # This is a workaround because Contact does not allways call initialize() before the first event has been received.

        field        SFBool bNeedToTakeFirstInput FALSE


        url "vrmlscript:

        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;
                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            CheckInit();
        }

        function CheckInit()
        {
            if(!bInitialized)
            {
                bInitialized= true;
                Init();
            }
        }

        function Init()
        {
            bNeedToTakeFirstInput= takeFirstInput;

            tau= effs.tau;
            set_value(initial_value);
            needTimer=    input.x != initial_value.x
                       || input.y != initial_value.y
                       || input.z != initial_value.z
                       || input.angle != initial_value.angle
                       ;
        }

        function set_tau(t)
        {
            CheckInit();

            tau= t;
        }



        function set_destination(i)
        {
            CheckInit();

            if(bNeedToTakeFirstInput)
            {
                bNeedToTakeFirstInput= false;
                set_value(i);
            }

            input= i;
            StartTimer();
        }


        function set_value(o)
        {
            CheckInit();

            bNeedToTakeFirstInput= false;

            value1= value2= value3= value4= value5= o;
            value_changed= o;
            StartTimer();
        }



        function tick(now)
        {
            CheckInit();

            if(!lastTick)
            {
                lastTick= now;
        return;
            }


            var delta= now - lastTick;
            lastTick= now;

            var alpha= Math.exp(-delta / tau);

            if(bNeedToTakeFirstInput)  // then don't do any processing.
        return;
			

            value1= order > 0 && tau
                       ? input  .slerp(value1, alpha)
                       : input;


            value2= order > 1 && tau
                       ? value1.slerp(value2, alpha)
                       : value1;

            value3= order > 2 && tau
                       ? value2.slerp(value3, alpha)
                       : value2;

            value4= order > 3 && tau
                       ? value3.slerp(value4, alpha)
                       : value3;

            value5= order > 4 && tau
                       ? value5.slerp(value4, alpha)
                       : value4;



            var dist= Math.abs(value1.inverse().multiply(input).angle);
            if(order > 1)
            {
                var dist2= Math.abs(value2.inverse().multiply(value1).angle);
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= Math.abs(value3.inverse().multiply(value2).angle);
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= Math.abs(value4.inverse().multiply(value3).angle);
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= Math.abs(value5.inverse().multiply(value4).angle);
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                value1= value2= value3= value4= value5= input;
                value_changed= input;
                StopTimer();
        return;
            }

            value_changed= value5;

        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}


#
# NOTE:
# The ColorDamper animates SFColor values in the HSV space.
# Initially i though it'd be good to work in perception space
# rather than a technology oriented space (RGB).
# However, this leads to the case that when a color should
# go - say from red to blue, it does so by going through
# other colors, i.e. yellow and green, whereas an animation
# computed in RGB space appears to create a much directer
# transition.
#
# Animating in HSV space basically means to consider hue
# values of 0 and 1 as equal and finding the shorter
# of the two paths possible on a circle. Maybe there's an
# analogy with the slerp interpolation on a 4d sphere.
#
# The below implementation contains code for animating in
# HSV space, but it's commented out and replaced by code
# for calculating in RGB space.
#


PROTO ColorDamper
[
    eventIn      SFColor      set_destination
    field        SFColor  initial_destination     0 0 0

    eventOut     SFColor          value_changed
    field        SFColor  initial_value          0 0 0
    eventIn      SFColor      set_value

    exposedField SFFloat  tau      1
    exposedField SFFloat  negTau  -1                # not yet testet
                                                    # The idea behind negTau is to use a different
    field        SFInt32  order   1                 # speed for when the brighness is to be lowered
                                                    # than when it is to be raised. This allows to
    eventOut     SFBool   isActive                  # implement a lamp which comes to full brightness quickly
                                                    # when turned on, but has a much longer phase of after-
    eventOut     SFBool   isLoaded                  # burn when turned off.

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau     1
        exposedField SFFloat negTau -1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFColor      set_destination    IS     set_destination
        field        SFColor  initial_destination    IS initial_destination

        eventOut     SFColor          value_changed IS         value_changed
        field        SFColor  initial_value         IS initial_value
        eventIn      SFColor      set_value         IS     set_value

        field        SFNode           effs           USE        EFFS

        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1

        eventIn      SFFloat      set_negTau
        field        SFFloat          negTau        -1

        field        SFInt32          order          IS         order

        field        SFFloat          eps            .001



        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        SFVec3f input_HSV      0 0 0
        field        SFVec3f value5_HSV    0 0 0
        field        SFVec3f value4_HSV    0 0 0
        field        SFVec3f value3_HSV    0 0 0
        field        SFVec3f value2_HSV    0 0 0
        field        SFVec3f value1_HSV    0 0 0



        url "vrmlscript:



        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;
                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            tau= effs.tau;
            negTau= effs.negTau;

//            var HSV= initial_destination.getHSV();
//            input_HSV.x= HSV[0];
//            input_HSV.y= HSV[1];
//            input_HSV.z= HSV[2];
            input_HSV.x= initial_destination.r;
            input_HSV.y= initial_destination.g;
            input_HSV.z= initial_destination.b;

            set_value(initial_value);

            needTimer=    initial_destination.r != initial_value.r
                       || initial_destination.g != initial_value.g
                       || initial_destination.b != initial_value.b
                       ;
        }

        function set_tau(t)
        {
            tau= t;
        }

        function set_negTau(t)
        {
            negTau= t;
        }


        function set_destination(i)
        {
//            var HSV= i.getHSV();
//            input_HSV.x= HSV[0];
//            input_HSV.y= HSV[1];
//            input_HSV.z= HSV[2];
            input_HSV.x= i.r;
            input_HSV.y= i.g;
            input_HSV.z= i.b;

            StartTimer();
        }


        function set_value(o)
        {
//            var HSV= o.getHSV();
//            value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= new SFVec3f(HSV[0], HSV[1], HSV[2]);
            value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= new SFVec3f(o.r, o.g, o.b);
            value_changed= o;
            StartTimer();
        }


        function dynamics(dest, val, alpha)
        {
            var diff= val.subtract(dest);
//            if(diff[0] >  .5) diff[0]-= 1;
//            if(diff[0] < -.5) diff[0]+= 1;

            var ret= dest.add(diff.multiply(alpha));
//            if(ret[0] < 0) ret[0]+= 1;
//            if(ret[0] > 1) ret[0]-= 1;

        return ret;
        }




        function distanceHSV(a, b)
        {
            diffHue= a[0] - b[0];
//            if(diffHue >  .5) diffHue-= 1;
//            if(diffHue < -.5) diffHue+= 1;
            diffSat= a[1] - b[1];
            diffVal= a[2] - b[2];

        return Math.sqrt(  diffHue * diffHue
                         + diffSat * diffSat
                         + diffVal * diffVal
                        );
        }


        function tick(now)
        {
            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;

            var TauUsed= negTau < 0
                           ? tau
                           : ( value5_HSV[2] < input_HSV[2]
                                 ? tau
                                 : negTau
                             )
                           ;


            var alpha= Math.exp(-delta / TauUsed);




            value1_HSV= order > 0 && TauUsed
                           ? dynamics(input_HSV,   value1_HSV, alpha)
                           : input_HSV;

            value2_HSV= order > 1 && TauUsed
                           ? dynamics(value1_HSV, value2_HSV, alpha)
                           : value1_HSV;

            value3_HSV= order > 2 && TauUsed
                           ? dynamics(value2_HSV, value3_HSV, alpha)
                           : value2_HSV;

            value4_HSV= order > 3 && TauUsed
                           ? dynamics(value3_HSV, value4_HSV, alpha)
                           : value3_HSV;

            value5_HSV= order > 4 && TauUsed
                           ? dynamics(value4_HSV, value5_HSV, alpha)
                           : value4_HSV;


            // Hmm, don't know if it is perception theoretically correct to calculate the distance in HSV space or in RGB space.
            var dist= distanceHSV(value1_HSV, input_HSV);
            if(order > 1)
            {
                var dist2=  distanceHSV(value2_HSV, value1_HSV);
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3=  distanceHSV(value3_HSV, value2_HSV);
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4=  distanceHSV(value4_HSV, value3_HSV);
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= distanceHSV(value5_HSV, value4_HSV);
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= input_HSV;
//                value_changed.setHSV(input_HSV[0], input_HSV[1], input_HSV[2]);
                value_changed= new SFColor(input_HSV[0], input_HSV[1], input_HSV[2]);
                StopTimer();
        return;
            }

//            value_changed.setHSV(value5_HSV[0], value5_HSV[1], value5_HSV[2]);
            value_changed= new SFColor(value5_HSV[0], value5_HSV[1], value5_HSV[2]);

            lastTick= now;

        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}




PROTO Position2fDamper
[
    eventIn      SFVec2f      set_destination
    field        SFVec2f  initial_destination     0 0

    eventOut     SFVec2f          value_changed
    field        SFVec2f  initial_value          0 0
    eventIn      SFVec2f      set_value

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFVec2f      set_destination    IS     set_destination
        field        SFVec2f          input          IS initial_destination

        eventOut     SFVec2f          value_changed IS         value_changed
        field        SFVec2f  initial_value         IS initial_value
        eventIn      SFVec2f      set_value         IS     set_value

        field        SFNode           effs           USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1

        field        SFInt32          order          IS         order

        field        SFFloat          eps            IS         eps



        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        SFVec2f value5    0 0
        field        SFVec2f value4    0 0
        field        SFVec2f value3    0 0
        field        SFVec2f value2    0 0
        field        SFVec2f value1    0 0


        url "vrmlscript:

        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;
                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            tau= effs.tau;
            set_value(initial_value);
            needTimer=    input.x != initial_value.x
                       || input.y != initial_value.y
                       ;
        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_destination(i)
        {
            input= i;
            StartTimer();
        }


        function set_value(o)
        {
            value1= value2= value3= value4= value5= o;
            value_changed= o;
            StartTimer();
        }



        function tick(now)
        {
            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;

            var alpha= Math.exp(-delta / tau);



            value1= order > 0 && tau
                       ? input  .add(value1.subtract(input  ).multiply(alpha))
                       : input;

            value2= order > 1 && tau
                       ? value1.add(value2.subtract(value1).multiply(alpha))
                       : value1;

            value3= order > 2 && tau
                       ? value2.add(value3.subtract(value2).multiply(alpha))
                       : value2;

            value4= order > 3 && tau
                       ? value3.add(value4.subtract(value3).multiply(alpha))
                       : value3;

            value5= order > 4 && tau
                       ? value4.add(value5.subtract(value4).multiply(alpha))
                       : value4;


            var dist= value1.subtract(input).length();
            if(order > 1)
            {
                var dist2= value2.subtract(value1).length();
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= value3.subtract(value2).length();
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= value4.subtract(value3).length();
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= value5.subtract(value4).length();
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                value1= value2= value3= value4= value5= input;
                value_changed= input;
                StopTimer();
        return;
            }

            value_changed= value5;

            lastTick= now;

        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}






# The value has always the same number of elements
# than the input. If the nuber of elements differ
# between initial_destination and initial_value, initial_value
# is modified to contain the correct number.
# If the number of elements is changed on set_destination,
# this change is propagated immediately to the value.
# (Cannot smoothly animate the number of elements.)
#
# All the Damper nodes send their start value on the
# value. The CoordinateDamper does not follow this
# rule in one case:
# If neither initial_destination nor initial_value contain
# some elements. Then it has to assume that simply no
# initial values are given on the PROTO instantiation.
# The value_changed sends no value on initialization
# if both, initial_destination and initial_value contain
# no elements. This is to avoid emitting an empty array
# on value_changed just because the size of the array
# is not known before the first event on set_destination
# arrives. If you don't want this, set initial_value
# to a proper initialization value.
#
# When the array length of the value has to be adjusted,
# the last elements of the array are deleted, or elements
# with the default value (0, 0, 0) of SFVec3f is appended.
#
# The array length is adjusted to the value received not
# only when a set_destination event is received, but also when a
# set_value event is received.
#


# TBD: Should an MFFloat version of tau be added?
#      Resulting in a separate tau value for each
#      element in the MFVec3f values?

PROTO CoordinateDamper
[
    eventIn      MFVec3f      set_destination
    field        MFVec3f  initial_destination     []

    eventOut     MFVec3f          value_changed
    field        MFVec3f  initial_value          []
    eventIn      MFVec3f      set_value

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      MFVec3f      set_destination    IS     set_destination
        field        MFVec3f          input          IS initial_destination

        eventOut     MFVec3f          value_changed IS         value_changed
        field        MFVec3f  initial_value         IS initial_value
        eventIn      MFVec3f      set_value         IS     set_value

        field        SFNode           effs           USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1

        field        SFInt32          order          IS         order

        field        SFFloat          eps            IS         eps



        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        MFVec3f value5   []
        field        MFVec3f value4   []
        field        MFVec3f value3   []
        field        MFVec3f value2   []
        field        MFVec3f value1   []

        field        SFBool  initialized FALSE # Contact seams to be able to send events before it calls the initialize() function.
                                               # I think these events come from other Scripts initialize() functions and
                                               # from Contacts immediate event propagation.
        field        SFBool  valueHasBeenSet FALSE # for the same problem.


        url "vrmlscript:

        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;


                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            initialized= true;

            tau= effs.tau;

            initial_value.length= input.length;


            if(initial_value.length || input.length)
            {
                if(!valueHasBeenSet)
                {
                    // TBD: Don't know if this works in all cases.
                    value1=
                    value2=
                    value3=
                    value4=
                    value5= initial_value;
                }
            }
            else{
                // Nothing to be done here.
            }


            var NeedTimer= false;
            for(var C= 0; C<input.length; C++)
            {
                if(   input[C].x != initial_value[C].x
                   || input[C].y != initial_value[C].y
                   || input[C].z != initial_value[C].z
                  )
                {
                    NeedTimer= true;
            break;
                }
            }

            needTimer= NeedTimer;

        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_destination(i)
        {
            input= i;

            value1.length=
            value2.length=
            value3.length=
            value4.length=
            value5.length=
                  i.length;

            if(!initialized)
                initial_value.length= i.length;

            StartTimer();
        }


        function set_value(o)
        {
            value1=
            value2=
            value3=
            value4=
            value5= o;
            input.length= o.length;
            if(!initialized)
                initial_value= o;
            value_changed= o;
            StartTimer();
        }




        function dynamics(dest, val, alpha)
        {
            var Val;
            var Dest;
            for(var C= 0; C<dest.length; C++ )
            {
                Val= val[C];
                Dest= dest[C];

//                Val= Dest.add(Val.subtract(Dest).multiply(alpha));
                Val.x= Dest.x + (Val.x - Dest.x) * alpha;
                Val.y= Dest.y + (Val.y - Dest.y) * alpha;
                Val.z= Dest.z + (Val.z - Dest.z) * alpha;

                val[C]= Val;



//                val[C]= dest[C].add(val[C].subtract(dest[C]).multiply(alpha));
            }
        }


        function IsDistLargerThan(a, b, Eps)
        {
            for(var C= 0; C<a.length; C++ )
            {
                var A= a[C];
                var B= b[C];

                var dist=  Math.abs(A.x - B.x)
                         + Math.abs(A.y - B.y)
                         + Math.abs(A.z - B.z)
                         ;
                if(dist > Eps)
        return true;
            }

        return false;
        }




        function tick(now)
        {
            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;

            var alpha= tau? Math.exp(-delta / tau) : 1;


            if(order >0)
            {
                if(tau)
                    dynamics(input,   value1, alpha);
                else
                    value1= input;
            }


            if(order > 1)
            {
                if(tau)
                    dynamics(value1, value2, alpha);
                else
                    value2= value1;
            }



            if(order > 2)
            {
                if(tau)
                    dynamics(value2, value3, alpha);
                else
                    value3= value2;
            }



            if(order > 3)
            {
                if(tau)
                    dynamics(value3, value4, alpha);
                else
                    value4= value3;
            }


            if(order > 4)
            {
                if(tau)
                    dynamics(value4, value5, alpha);
                else
                    value5= value4;
            }



    // Don't Know if this has a performance penalty.
//            var NeedContinue= false;

//            if(IsDistLargerThan(value1, input, eps))
//                NeedContinue= true;
//            else if(order > 1 && IsDistLargerThan(value2, value1, eps))
//                NeedContinue= true;
//            else if(order > 2 && IsDistLargerThan(value3, value2, eps))
//                NeedContinue= true;
//            else if(order > 3 && IsDistLargerThan(value4, value3, eps))
//                NeedContinue= true;
//            else if(order > 4 && IsDistLargerThan(value5, value4, eps))
//                NeedContinue= true;


//            if(!NeedContinue)
//            {
//                value1= value2= value3= value4= value5= input;
//                value_changed= input;
//                needTimer= false;
//        return;
//            }

            value_changed= order <= 1? value1 : (
                            order == 2? value2 : (
                            order == 3? value3 : (
                            order == 4? value4 : (
                                        value5   ))));


            ;

            lastTick= now;

        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}



# The same as CoordinateDamper, but for MFVec2f.
PROTO TexCoordDamper
[
    eventIn      MFVec2f      set_destination
    field        MFVec2f  initial_destination     []

    eventOut     MFVec2f          value_changed
    field        MFVec2f  initial_value          []
    eventIn      MFVec2f      set_value

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      MFVec2f      set_destination    IS     set_destination
        field        MFVec2f          input          IS initial_destination

        eventOut     MFVec2f          value_changed IS         value_changed
        field        MFVec2f  initial_value         IS initial_value
        eventIn      MFVec2f      set_value         IS     set_value

        field        SFNode           effs           USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1

        field        SFInt32          order          IS         order

        field        SFFloat          eps            IS         eps



        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        MFVec2f value5   []
        field        MFVec2f value4   []
        field        MFVec2f value3   []
        field        MFVec2f value2   []
        field        MFVec2f value1   []

        field        SFBool  initialized FALSE # Contact seams to be able to send events before it calls the initialize() function.
                                               # I think these events come from other Scripts initialize() functions and
                                               # from Contacts immediate event propagation.
        field        SFBool  valueHasBeenSet FALSE # for the same problem.


        url "vrmlscript:

        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;


                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            initialized= true;

            tau= effs.tau;

            initial_value.length= input.length;


            if(initial_value.length || input.length)
            {
                if(!valueHasBeenSet)
                {
                    // TBD: Don't know if this works in all cases.
                    value1=
                    value2=
                    value3=
                    value4=
                    value5= initial_value;
                }
            }
            else{
                // Nothing to be done here.
            }


            var NeedTimer= false;
            for(var C= 0; C<input.length; C++)
            {
                if(   input[C].x != initial_value[C].x
                   || input[C].y != initial_value[C].y
                  )
                {
                    NeedTimer= true;
            break;
                }
            }

            needTimer= NeedTimer;

        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_destination(i)
        {


            input= i;

            value1.length=
            value2.length=
            value3.length=
            value4.length=
            value5.length=
                  i.length;

            if(!initialized)
                initial_value.length= i.length;

            StartTimer();
        }


        function set_value(o)
        {
            value1=
            value2=
            value3=
            value4=
            value5= o;
            input.length= o.length;
            if(!initialized)
                initial_value= o;
            value_changed= o;
            StartTimer();
        }





        function dynamics(dest, val, alpha)
        {
            for(var C= 0; C<dest.length; C++ )
                val[C]= dest[C].add(val[C].subtract(dest[C]).multiply(alpha));
        }


        function IsDistLargerThan(a, b, Eps)
        {
            for(var C= 0; C<a.length; C++ )
            {
                var A= a[C];
                var B= b[C];

                var dist=  Math.abs(A.x - B.x)
                         + Math.abs(A.y - B.y)
                         ;
                if(dist > Eps)
        return true;
            }

        return false;
        }




        function tick(now)
        {
            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;

            var alpha= tau? Math.exp(-delta / tau) : 1;


            if(order >0)
            {
                if(tau)
                    dynamics(input,   value1, alpha);
                else
                    value1= input;
            }


            if(order > 1)
            {
                if(tau)
                    dynamics(value1, value2, alpha);
                else
                    value2= value1;
            }



            if(order > 2)
            {
                if(tau)
                    dynamics(value2, value3, alpha);
                else
                    value3= value2;
            }



            if(order > 3)
            {
                if(tau)
                    dynamics(value3, value4, alpha);
                else
                    value4= value3;
            }


            if(order > 4)
            {
                if(tau)
                    dynamics(value4, value5, alpha);
                else
                    value5= value4;
            }



//            var NeedContinue= false;

//            if(IsDistLargerThan(value1, input, eps))
//                NeedContinue= true;
//            else if(order > 1 && IsDistLargerThan(value2, value1, eps))
//                NeedContinue= true;
//            else if(order > 2 && IsDistLargerThan(value3, value2, eps))
//                NeedContinue= true;
//            else if(order > 3 && IsDistLargerThan(value4, value3, eps))
//                NeedContinue= true;
//            else if(order > 4 && IsDistLargerThan(value5, value4, eps))
//                NeedContinue= true;


//            if(!NeedContinue)
//            {
//                value1= value2= value3= value4= value5= input;
//                value_changed= input;
//                needTimer= false;
//        return;
//            }

            value_changed= order <= 1? value1 : (
                            order == 2? value2 : (
                            order == 3? value3 : (
                            order == 4? value4 : (
                                        value5   ))));

            lastTick= now;
        }





        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}








PROTO ScalarDamper
[
    eventIn      SFFloat      set_destination
    field        SFFloat  initial_destination     0

    eventOut     SFFloat          value_changed
    field        SFFloat  initial_value          0
    eventIn      SFFloat      set_value

    eventOut     SFFloat transparency_changed

    exposedField SFFloat  tau     1

    field        SFInt32  order   1

    field        SFFloat  eps     .001

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded

    field        MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
    PROTO EFFS   # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFFloat      set_destination    IS     set_destination
        field        SFFloat          input          IS initial_destination

        eventOut     SFFloat          value_changed IS         value_changed
        field        SFFloat  initial_value         IS initial_value
        eventIn      SFFloat      set_value         IS     set_value

        eventOut     SFFloat  transparency_changed   IS transparency_changed

        field        SFNode           effs           USE        EFFS
        eventIn      SFFloat      set_tau
        field        SFFloat          tau            1

        field        SFInt32          order          IS         order

        field        SFFloat          eps            IS         eps



        # internal fields
        eventOut     SFBool needTimer                IS         isActive
        eventIn      SFTime tick
        field        SFTime lastTick    0


        field        SFFloat value5    0
        field        SFFloat value4    0
        field        SFFloat value3    0
        field        SFFloat value2    0
        field        SFFloat value1    0

        field        SFBool  haveOutput FALSE  # This is a workaround because Contact does nt allwayscall initialize() before the first event has been received.

        url "vrmlscript:

        function StartTimer()
        {
            if(!needTimer)
            {
                lastTick= 0;
                needTimer= true;
            }
        }

        function StopTimer()
        {
            if(needTimer)
            {
                needTimer= false;
            }
        }



        function initialize()
        {
            tau= effs.tau;
            if(!haveOutput) set_value(initial_value);
            needTimer=    input != initial_value;
        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_destination(i)
        {
            input= i;
            StartTimer();
        }


        function set_value(o)
        {
            haveOutput= true;
            value1= value2= value3= value4= value5= o;
            value_changed= o;
            SetTransparency();
            StartTimer();
        }



        function tick(now)
        {
            if(!lastTick)
            {
                lastTick= now;
        return;
            }

            var delta= now - lastTick;

            var alpha= Math.exp(-delta / tau);



            value1= order > 0 && tau
                       ? input   + (value1 - input) * alpha
                       : input;

            value2= order > 1 && tau					
                       ? value1 + (value2 - value1) * alpha
                       : value1;

            value3= order > 2 && tau
                       ? value2 + (value3 - value2) * alpha
                       : value2;

            value4= order > 3 && tau
                       ? value3 + (value4 - value3) * alpha
                       : value3;

            value5= order > 4 && tau
                       ? value4 + (value5 - value4) * alpha
                       : value4;


            var dist= Math.abs(value1 - input);
            if(order > 1)
            {
                var dist2= Math.abs(value2 - value1);
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= Math.abs(value3 - value2);
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= Math.abs(value4 - value3);
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= Math.abs(value5 - value4);
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                value1= value2= value3= value4= value5= input;
                value_changed= input;
                SetTransparency();
                StopTimer();
        return;
            }

            value_changed= value5;
            SetTransparency();

            lastTick= now;

        }



        function SetTransparency()
        {
            transparency_changed= 1 - value_changed * value_changed;
        }


        "
    }

    DEF Timer TimeSensor
    {
        loop TRUE
    }

    ROUTE Worker.needTimer TO Timer.enabled
    ROUTE Timer.time       TO Worker.tick


    ROUTE EFFS.tau TO Worker.set_tau

	
    DEF LastNode Script
    {
        eventOut SFBool isLoaded IS isLoaded

        url "vrmlscript:

        function initialize()
        {
            isLoaded= true;
        }

        "
    }
}




__.-.__
end of document