Home

hersto:  Dampers.wrl

Source of Dampers.wrl:

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

and below is its content to view online.


#VRML V2.0 utf8




WorldInfo
{
    title "Copyright (C) 2004 by Herbert Stocker (aka hersto)"

    info
    [
"       The nodes in this file are usefull for easily creating complex
        animations and for making other animations more realistic.
"
"       The original versions of the PROTOs PositionDamper, OrientationDamper,
        ColorDamper, Position2fDamper, CoordinateDamper, TexCoordDamper and
        ScalarDamper were written by me, Herbert Stocker.

        They can be found together with some test and demonstration scenes
        at http://www.hersto.de/VRML/Dampers.html .
"

"       You have permission to use, copy and modify the PROTOs in this file if
        you keep the credit information that is associated with each of these
        PROTOs as a WorldInfo node unchached.
"

"       Please send a message to hersto@hersto.de where you describe how you
        use or improve the nodes. Especially if you included the improved
        versions this makes me happy. Your application can be listed on the
        Dampers home page.
"
    ]
}






WorldInfo
{
    title "Usage of the Damper PROTOs"

    info
    [
        "
        The Damper PROTOs are behavioral nodes and have no visible or audible
        geometry. They can be used as simple animation creators or as smoothers
        for existing animations.
        "

        "
        They were originally designed to work in any VRML browser and were
        tested in Contact, Cortona and Pivoron. They are supposed to work,
        but it's your part to find that out. My part was writing them. :-)
        "

        "
        If you use them as animation creators, send a destination value
        (destination position, destination orientation, destination color, etc)
        to the set_input field every time you want to reach the object another
        position, orientation, etc. Then the Damper node creates a smooth
        animation from the current output value to the destination value on
        its output_changed eventOut. Sending a new destination value to the
        node while there is an animation currently in progress is intentionally
        supported.

        The tau parameter specifies the time it needs output_changed to reach
        the destination value. The nodes implement exponential approximation:
        After a period of tau seconds 63 % of the distance to the destination
        value have been reached. After another period of tau seconds 63 % of
        the remaining distance have been reached, and so on. Therefore the
        destination value is never reached perfectly, but practically the
        animations stops after a short number of tau periods.

        The order field specifies how many such filters are processing each
        others output. A large number for order, say 5 makes the movements
        more smooth, but results in more delay.
        "

        "
        If you use the Damper PROTOs as smoother for existing animations, you
        constantly send values to their set_input fields. You probably feed
        them by the value_changed of an Interpolator or Sensor node. Then the
        output of Dampers follows their input, but movements are smoothed, i.e.
        fast movements (high frequencies in the signal) are suppressed.
        "

        "
        The name for the nodes come from the fact that they work like the
        dampers in a car. When the car drives over a road with lots of
        ~Schlagloecher~ the wheels of the car have to follow every
        ~Unebenheit~ of the street, but the cabine of the car does not
        because the cabine is connected to the wheels through dampers.
        On the other hand, if the street goes over a hill - even the
        slightest one, the cabine fortunately follow that movemnet of
        the wheels. To compare this with the Damper nodes, the set_input
        eventIn of a PositionDamper receives the position of the wheels
        and the output_changed eventOut drives the position of the cabine.
        "
    ]
}




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



#
#EXTERNPROTO PositionDamper
#[
#    eventIn      SFVec3f      set_input
#    field        SFVec3f  initial_input           # 0 0 0
#
#    eventOut     SFVec3f          output_changed
#    field        SFVec3f  initial_output          # 0 0 0
#    eventIn      SFVec3f      set_output
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#
#]
#[
#    "Dampers.wrl#PositionDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#PositionDamper"
#]
#




#
#EXTERNPROTO OrientationDamper
#[
#    eventIn      SFRotation      set_input
#    field        SFRotation  initial_input           # 0 0 1 0
#
#    eventOut     SFRotation          output_changed
#    field        SFRotation  initial_output          # 0 0 1 0
#    eventIn      SFRotation      set_output
#
#    exposedField SFFloat  tau                        # 1
#
#    field        SFInt32  order                      # 1         # allowed: [1..5]
#
#    field        SFFloat  eps                        # .001      # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "Dampers.wrl#OrientationDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#OrientationDamper"
#]
#




#
#EXTERNPROTO ColorDamper
#[
#    eventIn      SFColor      set_input
#    field        SFColor  initial_input           # 0 0 0
#
#    eventOut     SFColor          output_changed
#    field        SFColor  initial_output          # 0 0 0
#    eventIn      SFColor      set_output
#
#    exposedField SFFloat  tau                     #  1
#    exposedField SFFloat  negTau                  # -1       # not yet testet
#
#    field        SFInt32  order                   #  1       # allowed: [1..5]
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "Dampers.wrl#ColorDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#ColorDamper"
#]
#




#
#EXTERNPROTO Position2fDamper
#[
#    eventIn      SFVec2f      set_input
#    field        SFVec2f  initial_input           # 0 0
#
#    eventOut     SFVec2f          output_changed
#    field        SFVec2f  initial_output          # 0 0
#    eventIn      SFVec2f      set_output
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "Dampers.wrl#Position2fDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#Position2fDamper"
#]
#





#
#EXTERNPROTO CoordinateDamper
#[
#    eventIn      MFVec3f      set_input
#    field        MFVec3f  initial_input           # []
#
#    eventOut     MFVec3f          output_changed
#    field        MFVec3f  initial_output          # []
#    eventIn      MFVec3f      set_output
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[
#    "Dampers.wrl#CoordinateDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#CoordinateDamper"
#]
#




#
#EXTERNPROTO TexCoordDamper
#[
#    eventIn      MFVec2f      set_input
#    field        MFVec2f  initial_input           # []
#
#    eventOut     MFVec2f          output_changed
#    field        MFVec2f  initial_output          # []
#    eventIn      MFVec2f      set_output
#
#    exposedField SFFloat  tau                     # 1
#
#    field        SFInt32  order                   # 1        # allowed: [1..5]
#
#    field        SFFloat  eps                     # .001     # not yet testet
#
#    eventOut     SFBool   isActive
#
#    eventOut     SFBool   isLoaded
#]
#[   "Dampers.wrl#TexCoordDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#TexCoordDamper"
#]
#


#
#EXTERNPROTO ScalarDamper
#[
#    eventIn      SFFloat      set_input
#    field        SFFloat  initial_input           # 0
#
#    eventOut     SFFloat          output_changed
#    field        SFFloat  initial_output          # 0
#    eventIn      SFFloat      set_output
#
#    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
#
#]
#[
#    "Dampers.wrl#ScalarDamper"
#    "http://www.hersto.de/VRML/Dampers.wrl#ScalarDamper"
#]
#







PROTO PositionDamper
[
    eventIn      SFVec3f      set_input
    field        SFVec3f  initial_input           0 0 0

    eventOut     SFVec3f          output_changed
    field        SFVec3f  initial_output          0 0 0
    eventIn      SFVec3f      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFVec3f      set_input          IS     set_input
        field        SFVec3f          input          IS initial_input

        eventOut     SFVec3f          output_changed IS         output_changed
        field        SFVec3f  initial_output         IS initial_output
        eventIn      SFVec3f      set_output         IS     set_output

        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        SFVec3f output5    0 0 0
        field        SFVec3f output4    0 0 0
        field        SFVec3f output3    0 0 0
        field        SFVec3f output2    0 0 0
        field        SFVec3f output1    0 0 0

        field        SFBool  haveOutput FALSE  # This is a workaround because Contact doesn't take care
                                               # to call initialize() before the first event has been
                                               #  received.

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

        url "vrmlscript:

        function StartTimer()
        {
            if(IsCortona)
        return;

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

        function StopTimer()
        {
            if(IsCortona)
        return;

            if(needTimer)
            {
                needTimer= false;
            }
        }



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


            tau= effs.tau;
            if(!haveOutput) set_output(initial_output);
            if(IsCortona)
                needTimer= true;
            else
                needTimer=    input.x != initial_output.x
                           || input.y != initial_output.y
                           || input.z != initial_output.z
                           ;
        }

        function set_tau(t)
        {
            tau= t;
        }



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


        function set_output(o)
        {
            haveOutput= true;
            output1= output2= output3= output4= output5= o;
            output_changed= o;
            StartTimer();
        }



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

            var delta= now - lastTick;

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



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

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

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

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

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


            var dist= output1.subtract(input).length();
            if(order > 1)
            {
                var dist2= output2.subtract(output1).length();
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= output3.subtract(output2).length();
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= output4.subtract(output3).length();
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= output5.subtract(output4).length();
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                output1= output2= output3= output4= output5= input;
                output_changed= input;
                StopTimer();
        return;
            }

            output_changed= output5;

            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 OrientationDamper
[
    eventIn      SFRotation      set_input
    field        SFRotation  initial_input           0 0 1 0

    eventOut     SFRotation          output_changed
    field        SFRotation  initial_output          0 0 1 0
    eventIn      SFRotation      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFRotation      set_input          IS     set_input
        field        SFRotation          input          IS initial_input

        eventOut     SFRotation          output_changed IS         output_changed
        field        SFRotation  initial_output         IS initial_output
        eventIn      SFRotation      set_output         IS     set_output

        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        SFRotation output5    0 0 1 0
        field        SFRotation output4    0 0 1 0
        field        SFRotation output3    0 0 1 0
        field        SFRotation output2    0 0 1 0
        field        SFRotation output1    0 0 1 0


        url "vrmlscript:

        function StartTimer()
        {
//            print('>>> Start Timer   (needTimer='+ needTimer +')');
            if(!needTimer)
            {
//                print('Starting.');
                lastTick= 0;
                needTimer= true;
//                print('lastTick: '+ lastTick +'    needTimer: '+ needTimer);
            }//else
//                print('not starting.');
//            print('<<< Start Timer.');
        }

        function StopTimer()
        {
//            print('>>> Stop Timer     (needTimer='+ needTimer +')');
            if(needTimer)
            {
//                print('Stopping.');
                needTimer= false;
//                print('lastTick: '+ lastTick +'    needTimer: '+ needTimer);
            }//else
//                print('not stopping.');
//            print('<<< Stop Timer.');
        }



        function initialize()
        {
            tau= effs.tau;
            set_output(initial_output);
            needTimer=    input.x != initial_output.x
                       || input.y != initial_output.y
                       || input.z != initial_output.z
                       || input.angle != initial_output.angle
                       ;
        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_input(i)
        {
//            print('######### set_input: '+ i);
            input= i;
            StartTimer();
//            print('timer started.');
        }


        function set_output(o)
        {
            output1= output2= output3= output4= output5= o;
            output_changed= o;
            StartTimer();
        }



        function tick(now)
        {
//            print('## Tick: '+ now +'    '+ lastTick);
            if(!lastTick)
            {
//                print('first time.');
                lastTick= now;
        return;
            }


            var delta= now - lastTick;

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

//            print('Tick: '+ delta);
//            print('order: '+ order);
//            print('input: '+ input);
			

            output1= order > 0 && tau
                       ? input  .slerp(output1, alpha)
                       : input;
////            print('output1: '+ output1);


            output2= order > 1 && tau
                       ? output1.slerp(output2, alpha)
                       : output1;
////            print('output2: '+ output1);

            output3= order > 2 && tau
                       ? output2.slerp(output3, alpha)
                       : output2;
////            print('output3: '+ output1);

            output4= order > 3 && tau
                       ? output3.slerp(output4, alpha)
                       : output3;
////            print('output4: '+ output1);

            output5= order > 4 && tau
                       ? output5.slerp(output4, alpha)
                       : output4;
////            print('output5: '+ output1);



            var dist= Math.abs(output1.inverse().multiply(input).angle);
////            print('dist 1: '+ dist);
            if(order > 1)
            {
                var dist2= Math.abs(output2.inverse().multiply(output1).angle);
                if( dist2 > dist)  dist= dist2;
            }
////            print('dist 2: '+ dist);
            if(order > 2)
            {
                var dist3= Math.abs(output3.inverse().multiply(output2).angle);
                if( dist3 > dist)  dist= dist3;
            }
////            print('dist 3: '+ dist);
            if(order > 3)
            {
                var dist4= Math.abs(output4.inverse().multiply(output3).angle);
                if( dist4 > dist)  dist= dist4;
            }
////            print('dist 4: '+ dist);
            if(order > 4)
            {
                var dist5= Math.abs(output5.inverse().multiply(output4).angle);
                if( dist5 > dist)  dist= dist5;
            }
//            print('dist 5: '+ dist +'    eps: '+ eps);

            if(dist < eps)
            {
//                print('dist < eps.');
                output1= output2= output3= output4= output5= input;
                output_changed= input;
//                print('output_changed: '+ output_changed);
//                print(' ');
                StopTimer();
//                print('timer stopped.');
        return;
            }

//            print('dist >= eps.');
            output_changed= output5;
//            print('output_changed: '+ output_changed);
//            print(' ');

            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 ColorDamper animates SFColor values in the HSV space.
# This means that a hue of 0 is considered equal to 1 and
# it is always the shortest way chosen to reach the destination
# value. E.g. if the current color has a hue of 0.1 and the
# destination color has a hue of 0.9, the animation goes from
# 0.1 to 0, jumps to 1 and continues to 0.9, as this is the
# shorter than animating to 0.2, 0.3, ... 0.9.
# Another implication is that if the saturation value or lighness
# value does not differ between the destination color and current
# color, these values keep constant during the animation.
# This is not perfectly true if the browser built-in conversion
# funcitons in getHLS() and setHLS() are not perfect.
#
# The negTau is by default -1, which means that the value tau
# is used for all transitions. If negTau has a positive value
# or is zero, the value of tau is used for the animation only
# when the animation goes from dark to bright colors (value of
# output is less than the value of input). When going from
# bright to dark colors negTau is used as the time constant of
# (speed of) the animation. This allows for simulating light
# bulbs that quickly come to full brightness if they are turned
# on, but need a long time to stop emitting light when they
# are turned off.


PROTO ColorDamper
[
    eventIn      SFColor      set_input
    field        SFColor  initial_input           0 0 0

    eventOut     SFColor          output_changed
    field        SFColor  initial_output          0 0 0
    eventIn      SFColor      set_output

    exposedField SFFloat  tau      1
    exposedField SFFloat  negTau  -1                       # not yet testet

    field        SFInt32  order   1

    eventOut     SFBool   isActive

    eventOut     SFBool   isLoaded


    field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau     1
        exposedField SFFloat negTau -1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFColor      set_input          IS     set_input
        field        SFColor  initial_input          IS initial_input

        eventOut     SFColor          output_changed IS         output_changed
        field        SFColor  initial_output         IS initial_output
        eventIn      SFColor      set_output         IS     set_output

        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 output5_HSV    0 0 0
        field        SFVec3f output4_HSV    0 0 0
        field        SFVec3f output3_HSV    0 0 0
        field        SFVec3f output2_HSV    0 0 0
        field        SFVec3f output1_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_input.getHSV();
//            input_HSV.x= HSV[0];
//            input_HSV.y= HSV[1];
//            input_HSV.z= HSV[2];
            input_HSV.x= initial_input.r;
            input_HSV.y= initial_input.g;
            input_HSV.z= initial_input.b;

            set_output(initial_output);

            needTimer=    initial_input.r != initial_output.r
                       || initial_input.g != initial_output.g
                       || initial_input.b != initial_output.b
                       ;
        }

        function set_tau(t)
        {
            tau= t;
        }

        function set_negTau(t)
        {
            negTau= t;
        }


        function set_input(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_output(o)
        {
//            var HSV= o.getHSV();
//            output1_HSV= output2_HSV= output3_HSV= output4_HSV= output5_HSV= new SFVec3f(HSV[0], HSV[1], HSV[2]);
            output1_HSV= output2_HSV= output3_HSV= output4_HSV= output5_HSV= new SFVec3f(o.r, o.g, o.b);
            output_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
                           : ( output5_HSV[2] < input_HSV[2]
                                 ? tau
                                 : negTau
                             )
                           ;


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




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

            output2_HSV= order > 1 && TauUsed
                           ? dynamics(output1_HSV, output2_HSV, alpha)
                           : output1_HSV;

            output3_HSV= order > 2 && TauUsed
                           ? dynamics(output2_HSV, output3_HSV, alpha)
                           : output2_HSV;

            output4_HSV= order > 3 && TauUsed
                           ? dynamics(output3_HSV, output4_HSV, alpha)
                           : output3_HSV;

            output5_HSV= order > 4 && TauUsed
                           ? dynamics(output4_HSV, output5_HSV, alpha)
                           : output4_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(output1_HSV, input_HSV);
            if(order > 1)
            {
                var dist2=  distanceHSV(output2_HSV, output1_HSV);
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3=  distanceHSV(output3_HSV, output2_HSV);
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4=  distanceHSV(output4_HSV, output3_HSV);
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= distanceHSV(output5_HSV, output4_HSV);
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                output1_HSV= output2_HSV= output3_HSV= output4_HSV= output5_HSV= input_HSV;
//                output_changed.setHSV(input_HSV[0], input_HSV[1], input_HSV[2]);
                output_changed= new SFColor(input_HSV[0], input_HSV[1], input_HSV[2]);
                StopTimer();
        return;
            }

//            output_changed.setHSV(output5_HSV[0], output5_HSV[1], output5_HSV[2]);
            output_changed= new SFColor(output5_HSV[0], output5_HSV[1], output5_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_input
    field        SFVec2f  initial_input           0 0

    eventOut     SFVec2f          output_changed
    field        SFVec2f  initial_output          0 0
    eventIn      SFVec2f      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFVec2f      set_input          IS     set_input
        field        SFVec2f          input          IS initial_input

        eventOut     SFVec2f          output_changed IS         output_changed
        field        SFVec2f  initial_output         IS initial_output
        eventIn      SFVec2f      set_output         IS     set_output

        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 output5    0 0
        field        SFVec2f output4    0 0
        field        SFVec2f output3    0 0
        field        SFVec2f output2    0 0
        field        SFVec2f output1    0 0


        url "vrmlscript:

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

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



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

        function set_tau(t)
        {
            tau= t;
        }



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


        function set_output(o)
        {
            output1= output2= output3= output4= output5= o;
            output_changed= o;
            StartTimer();
        }



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

            var delta= now - lastTick;

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



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

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

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

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

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


            var dist= output1.subtract(input).length();
            if(order > 1)
            {
                var dist2= output2.subtract(output1).length();
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= output3.subtract(output2).length();
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= output4.subtract(output3).length();
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= output5.subtract(output4).length();
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                output1= output2= output3= output4= output5= input;
                output_changed= input;
                StopTimer();
        return;
            }

            output_changed= output5;

            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 output has always the same number of elements
# than the input. If the nuber of elements differ
# between initial_input and initial_output, initial_output
# is modified to contain the correct number.
# If the number of elements is changed on set_input,
# this change is propagated immediately to the output.
# (Cannot smoothly animate the number of elements.)
#
# All the Damper nodes send their start value on the
# output. The CoordinateDamper does not follow this
# rule in one case:
# If neither initial_input nor initial_output contain
# some elements. Then it has to assume that simply no
# initial values are given on the PROTO instantiation.
# The output_changed sends no value on initialization
# if both, initial_input and initial_output contain
# no elements. This is to avoid emitting an empty array
# on output_changed just because the size of the array
# is not known before the first event on set_input
# arrives. If you don't want this, set initial_output
# to a proper initialization value.
#
# When the array length of the output 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_input event is received, but also when a
# set_output 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_input
    field        MFVec3f  initial_input           []

    eventOut     MFVec3f          output_changed
    field        MFVec3f  initial_output          []
    eventIn      MFVec3f      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      MFVec3f      set_input          IS     set_input
        field        MFVec3f          input          IS initial_input

        eventOut     MFVec3f          output_changed IS         output_changed
        field        MFVec3f  initial_output         IS initial_output
        eventIn      MFVec3f      set_output         IS     set_output

        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 output5   []
        field        MFVec3f output4   []
        field        MFVec3f output3   []
        field        MFVec3f output2   []
        field        MFVec3f output1   []

        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  outputHasBeenSet 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_output.length= input.length;


            if(initial_output.length || input.length)
            {
                if(!outputHasBeenSet)
                {
                    // TBD: Don't know if this works in all cases.
                    output1=
                    output2=
                    output3=
                    output4=
                    output5= initial_output;
                }
            }
            else{
                // Nothing to be done here.
            }


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

            needTimer= NeedTimer;

        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_input(i)
        {
            input= i;

            output1.length=
            output2.length=
            output3.length=
            output4.length=
            output5.length=
                  i.length;

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

            StartTimer();
        }


        function set_output(o)
        {
            output1=
            output2=
            output3=
            output4=
            output5= o;
            input.length= o.length;
            if(!initialized)
                initial_output= o;
            output_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,   output1, alpha);
                else
                    output1= input;
            }


            if(order > 1)
            {
                if(tau)
                    dynamics(output1, output2, alpha);
                else
                    output2= output1;
            }



            if(order > 2)
            {
                if(tau)
                    dynamics(output2, output3, alpha);
                else
                    output3= output2;
            }



            if(order > 3)
            {
                if(tau)
                    dynamics(output3, output4, alpha);
                else
                    output4= output3;
            }


            if(order > 4)
            {
                if(tau)
                    dynamics(output4, output5, alpha);
                else
                    output5= output4;
            }



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

//            if(IsDistLargerThan(output1, input, eps))
//                NeedContinue= true;
//            else if(order > 1 && IsDistLargerThan(output2, output1, eps))
//                NeedContinue= true;
//            else if(order > 2 && IsDistLargerThan(output3, output2, eps))
//                NeedContinue= true;
//            else if(order > 3 && IsDistLargerThan(output4, output3, eps))
//                NeedContinue= true;
//            else if(order > 4 && IsDistLargerThan(output5, output4, eps))
//                NeedContinue= true;


//            if(!NeedContinue)
//            {
//                output1= output2= output3= output4= output5= input;
//                output_changed= input;
//                needTimer= false;
//        return;
//            }

            output_changed= order <= 1? output1 : (
                            order == 2? output2 : (
                            order == 3? output3 : (
                            order == 4? output4 : (
                                        output5   ))));


            ;

            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_input
    field        MFVec2f  initial_input           []

    eventOut     MFVec2f          output_changed
    field        MFVec2f  initial_output          []
    eventIn      MFVec2f      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      MFVec2f      set_input          IS     set_input
        field        MFVec2f          input          IS initial_input

        eventOut     MFVec2f          output_changed IS         output_changed
        field        MFVec2f  initial_output         IS initial_output
        eventIn      MFVec2f      set_output         IS     set_output

        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 output5   []
        field        MFVec2f output4   []
        field        MFVec2f output3   []
        field        MFVec2f output2   []
        field        MFVec2f output1   []

        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  outputHasBeenSet 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_output.length= input.length;


            if(initial_output.length || input.length)
            {
                if(!outputHasBeenSet)
                {
                    // TBD: Don't know if this works in all cases.
                    output1=
                    output2=
                    output3=
                    output4=
                    output5= initial_output;
                }
            }
            else{
                // Nothing to be done here.
            }


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

            needTimer= NeedTimer;

        }

        function set_tau(t)
        {
            tau= t;
        }



        function set_input(i)
        {


            input= i;

            output1.length=
            output2.length=
            output3.length=
            output4.length=
            output5.length=
                  i.length;

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

            StartTimer();
        }


        function set_output(o)
        {
            output1=
            output2=
            output3=
            output4=
            output5= o;
            input.length= o.length;
            if(!initialized)
                initial_output= o;
            output_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,   output1, alpha);
                else
                    output1= input;
            }


            if(order > 1)
            {
                if(tau)
                    dynamics(output1, output2, alpha);
                else
                    output2= output1;
            }



            if(order > 2)
            {
                if(tau)
                    dynamics(output2, output3, alpha);
                else
                    output3= output2;
            }



            if(order > 3)
            {
                if(tau)
                    dynamics(output3, output4, alpha);
                else
                    output4= output3;
            }


            if(order > 4)
            {
                if(tau)
                    dynamics(output4, output5, alpha);
                else
                    output5= output4;
            }



//            var NeedContinue= false;

//            if(IsDistLargerThan(output1, input, eps))
//                NeedContinue= true;
//            else if(order > 1 && IsDistLargerThan(output2, output1, eps))
//                NeedContinue= true;
//            else if(order > 2 && IsDistLargerThan(output3, output2, eps))
//                NeedContinue= true;
//            else if(order > 3 && IsDistLargerThan(output4, output3, eps))
//                NeedContinue= true;
//            else if(order > 4 && IsDistLargerThan(output5, output4, eps))
//                NeedContinue= true;


//            if(!NeedContinue)
//            {
//                output1= output2= output3= output4= output5= input;
//                output_changed= input;
//                needTimer= false;
//        return;
//            }

            output_changed= order <= 1? output1 : (
                            order == 2? output2 : (
                            order == 3? output3 : (
                            order == 4? output4 : (
                                        output5   ))));

            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_input
    field        SFFloat  initial_input           0

    eventOut     SFFloat          output_changed
    field        SFFloat  initial_output          0
    eventIn      SFFloat      set_output

    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.com/"
    field SFString info    "This version is out-of-date. Please find an up-to-date version at http://www.hersto.com/Followers/"
]
{
    PROTO EFFS
    [
        exposedField SFFloat tau  1
    ]
       { Group {} }



    DEF EFFS EFFS
    {
        tau IS tau
    }



    DEF Worker Script
    {
        eventIn      SFFloat      set_input          IS     set_input
        field        SFFloat          input          IS initial_input

        eventOut     SFFloat          output_changed IS         output_changed
        field        SFFloat  initial_output         IS initial_output
        eventIn      SFFloat      set_output         IS     set_output

        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 output5    0
        field        SFFloat output4    0
        field        SFFloat output3    0
        field        SFFloat output2    0
        field        SFFloat output1    0

        field        SFBool  haveOutput FALSE  # This is a workaround because Contact doesn't take care to call 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_output(initial_output);
            needTimer=    input != initial_output;
        }

        function set_tau(t)
        {
            tau= t;
        }



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


        function set_output(o)
        {
            haveOutput= true;
            output1= output2= output3= output4= output5= o;
            output_changed= o;
            SetTransparency();
            StartTimer();
        }



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

            var delta= now - lastTick;

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



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

            output2= order > 1 && tau					
                       ? output1 + (output2 - output1) * alpha
                       : output1;

            output3= order > 2 && tau
                       ? output2 + (output3 - output2) * alpha
                       : output2;

            output4= order > 3 && tau
                       ? output3 + (output4 - output3) * alpha
                       : output3;

            output5= order > 4 && tau
                       ? output4 + (output5 - output4) * alpha
                       : output4;


            var dist= Math.abs(output1 - input);
            if(order > 1)
            {
                var dist2= Math.abs(output2 - output1);
                if( dist2 > dist)  dist= dist2;
            }
            if(order > 2)
            {
                var dist3= Math.abs(output3 - output2);
                if( dist3 > dist)  dist= dist3;
            }
            if(order > 3)
            {
                var dist4= Math.abs(output4 - output3);
                if( dist4 > dist)  dist= dist4;
            }
            if(order > 4)
            {
                var dist5= Math.abs(output5 - output4);
                if( dist5 > dist)  dist= dist5;
            }

            if(dist < eps)
            {
                output1= output2= output3= output4= output5= input;
                output_changed= input;
                SetTransparency();
                StopTimer();
        return;
            }

            output_changed= output5;
            SetTransparency();

            lastTick= now;

        }



        function SetTransparency()
        {
            transparency_changed= 1 - output_changed * output_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;
        }

        "
    }
}












Transform
{
    translation 0 2.2 0
    scale 1.05 1 1

    children Shape
    {
        appearance DEF App Appearance
        {
            material Material
            {
                diffuseColor 0 0 0
            }
        }

        geometry Text
        {
            string "This file contains a set of PROTOs that you"

            fontStyle DEF Font FontStyle
            {
                style "USE_TEXTURE"
            }
        }
    }
}

Transform
{
    translation 0 1.1 0
    scale 1.12 1 1

    children Shape
    {
        appearance USE App

        geometry Text
        {
            string "have permission to use, copy and modify."

            fontStyle USE Font
        }
    }
}


Transform
{
    translation 0 0 0

    children Shape
    {
        appearance USE App

        geometry Text
        {
            string "Go to"

            fontStyle USE Font
        }
    }
}


Transform
{
    translation 0 -1.1 0

    children Anchor
    {
        url "http://www.hersto.de/VRML/Dampers.html"

        description "http://www.hersto.de"

        children
        [
            Shape
            {
                appearance

                Appearance {
                    material Material {
                        diffuseColor .15 .15 1
                        emissiveColor .05 .07 .2
                        ambientIntensity 0
                        shininess 0
                    }
                }

                geometry Text
                {
                    string "    http://www.hersto.de/VRML/Dampers.html"

                    fontStyle USE Font
                }
            }

            Shape
            {
                appearance Appearance {
                    material Material {
                        transparency 1
                    }
                }

                geometry Box
                {
                    size 60 1 .01
                }
            }
        ]
    }
}

Transform
{
    translation 0 -2.2 0
    scale .975 1 1

    children Shape
    {
        appearance USE App

        geometry Text
        {
            string "in order to view a description and some demon-"

            fontStyle USE Font
        }
    }
}

Transform
{
    translation 0 -3.3 0

    children Shape
    {
        appearance USE App

        geometry Text
        {
            string "stration scenes."

            fontStyle USE Font
        }
    }
}





Viewpoint
{
    position 11 0 30
}


NavigationInfo
{
    type [ "EXAMINE" "ANY" ]
    avatarSize [0.25 1.6 0.75, 11 0 0]
}


Background
{
    skyColor .75 1 1
}


__.-.__
end of document