#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
}
|