#VRML V2.0 utf8
WorldInfo
{
title "Damper nodes"
info
[
"The ExternProto nodes found in this file implement principels described in"
"the paper 'Linear Filters - Animating Objects in a Flexible and Pleasing Way'."
"They have been proposed and added to the X3D standard in 2006."
"Webpage: http://www.hersto.net/Followers"
""
"Please use the code you find here in any content or application you like"
"or modify it in any way."
""
"The code here seams to be bug-free as it has been used in many cases."
]
}
#
# These commented out EXTERNPROTO statements may help
# you include the nodes.
#
#
#EXTERNPROTO PositionDamper
#[
# eventIn SFVec3f set_destination
# field SFVec3f initial_destination # 0 0 0
#
# eventOut SFVec3f value_changed
# field SFVec3f initial_value # 0 0 0
# eventIn SFVec3f set_value
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# field SFFloat reachThreshold # .01
#
# eventOut SFBool isActive
#
# eventOut SFBool reached
#
# eventOut SFBool isLoaded
#
# field SFBool takeFirstInput # TRUE
#
#]
#[
# "urn:inet:hersto.net:node:PositionDamper"
# "Dampers.wrl#PositionDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#PositionDamper"
#]
#
#
#EXTERNPROTO OrientationDamper
#[
# eventIn SFRotation set_destination
# field SFRotation initial_destination # 0 0 1 0
#
# eventOut SFRotation value_changed
# field SFRotation initial_value # 0 0 1 0
# eventIn SFRotation set_value
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#
# field SFBool takeFirstInput # TRUE
#
#]
#[
# "urn:inet:hersto.net:node:OrientationDamper"
# "Dampers.wrl#OrientationDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#OrientationDamper"
#]
#
#
#EXTERNPROTO ColorDamper
#[
# eventIn SFColor set_destination
# field SFColor initial_destination # 0 0 0
#
# eventOut SFColor value_changed
# field SFColor initial_value # 0 0 0
# eventIn SFColor set_value
#
# exposedField SFFloat tau # 1
# exposedField SFFloat negTau # -1 # not yet testet
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#]
# "urn:inet:hersto.net:node:ColorDamper"
# "Dampers.wrl#ColorDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#ColorDamper"
#]
#
#
#EXTERNPROTO Position2fDamper
#[
# eventIn SFVec2f set_destination
# field SFVec2f initial_destination # 0 0
#
# eventOut SFVec2f value_changed
# field SFVec2f initial_value # 0 0
# eventIn SFVec2f set_value
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#]
#[
# "urn:inet:hersto.net:node:Position2fDamper"
# "Dampers.wrl#Position2fDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#Position2fDamper"
#]
#
#
#EXTERNPROTO CoordinateDamper
#[
# eventIn MFVec3f set_destination
# field MFVec3f initial_destination # []
#
# eventOut MFVec3f value_changed
# field MFVec3f initial_value # []
# eventIn MFVec3f set_value
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#]
#[
# "urn:inet:hersto.net:node:CoordinateDamper"
# "Dampers.wrl#CoordinateDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#CoordinateDamper"
#]
#
#
#EXTERNPROTO TexCoordDamper
#[
# eventIn MFVec2f set_destination
# field MFVec2f initial_destination # []
#
# eventOut MFVec2f value_changed
# field MFVec2f initial_value # []
# eventIn MFVec2f set_value
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#]
#[
# "urn:inet:hersto.net:node:TexCoordDamper"
# "Dampers.wrl#TexCoordDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#TexCoordDamper"
#]
#
#
#EXTERNPROTO ScalarDamper
#[
# eventIn SFFloat set_destination
# field SFFloat initial_destination # 0
#
# eventOut SFFloat value_changed
# field SFFloat initial_value # 0
# eventIn SFFloat set_value
#
# eventOut SFFloat transparency_changed
#
# exposedField SFFloat tau # 1
#
# field SFInt32 order # 1 # allowed: [1..5]
#
# field SFFloat eps # .001 # not yet testet
#
# eventOut SFBool isActive
#
# eventOut SFBool isLoaded
#
#]
#[
# "urn:inet:hersto.net:node:ScalarDamper"
# "Dampers.wrl#ScalarDamper"
# "http://www.hersto.net/Followers/Dampers.wrl#ScalarDamper"
#]
#
PROTO PositionDamper
[
eventIn SFVec3f set_destination
field SFVec3f initial_destination 0 0 0
eventOut SFVec3f value_changed
field SFVec3f initial_value 0 0 0
eventIn SFVec3f set_value
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
field SFFloat reachThreshold .01
eventOut SFBool isActive
eventOut SFBool reached
eventOut SFBool isLoaded
field SFBool takeFirstInput TRUE
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
{
tau IS tau
}
DEF Worker Script
{
eventIn SFVec3f set_destination IS set_destination
field SFVec3f input IS initial_destination
eventOut SFVec3f value_changed IS value_changed
field SFVec3f initial_value IS initial_value
eventIn SFVec3f set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFFloat reachThreshold IS reachThreshold
field SFInt32 order IS order
field SFFloat eps IS eps
field SFBool takeFirstInput IS takeFirstInput
eventOut SFBool reached IS reached
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field SFVec3f value5 0 0 0
field SFVec3f value4 0 0 0
field SFVec3f value3 0 0 0
field SFVec3f value2 0 0 0
field SFVec3f value1 0 0 0
field SFBool bInitialized FALSE # This is a workaround because Contact does not allways call initialize() before the first event has been received.
field SFBool IsCortona FALSE # does it appear only to me, or does it have problems with receiving values on TimeSensor.enable?
field SFBool bNeedToTakeFirstInput TRUE
url "vrmlscript:
function StartTimer()
{
if(IsCortona)
return;
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(IsCortona)
return;
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true;
Init();
}
}
function Init()
{
IsCortona= false && Browser.getName().indexOf('Cortona') != -1;
bNeedToTakeFirstInput= takeFirstInput;
tau= effs.tau;
set_value(initial_value);
if(IsCortona)
needTimer= true;
else
needTimer= input.x != initial_value.x
|| input.y != initial_value.y
|| input.z != initial_value.z
;
}
function set_tau(t)
{
CheckInit();
tau= t;
}
function set_destination(i)
{
CheckInit();
if(bNeedToTakeFirstInput)
{
bNeedToTakeFirstInput= false;
set_value(i);
}
if(i != input)
{
input= i;
StartTimer();
}
}
function set_value(o)
{
CheckInit();
bNeedToTakeFirstInput= false;
value1= value2= value3= value4= value5= o;
value_changed= o;
UpdateReached();
StartTimer();
}
function tick(now)
{
CheckInit();
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
lastTick= now;
var alpha= Math.exp(-delta / tau);
if(bNeedToTakeFirstInput) // then don't do any processing.
return;
value1= order > 0 && tau
? input .add(value1.subtract(input ).multiply(alpha))
: input;
value2= order > 1 && tau
? value1.add(value2.subtract(value1).multiply(alpha))
: value1;
value3= order > 2 && tau
? value2.add(value3.subtract(value2).multiply(alpha))
: value2;
value4= order > 3 && tau
? value3.add(value4.subtract(value3).multiply(alpha))
: value3;
value5= order > 4 && tau
? value4.add(value5.subtract(value4).multiply(alpha))
: value4;
var dist= GetDist();
if(dist < eps)
{
value1= value2= value3= value4= value5= input;
value_changed= input;
UpdateReached2(dist);
StopTimer();
return;
}
value_changed= value5;
UpdateReached2(dist);
}
function GetDist()
{
var dist= value1.subtract(input).length();
if(order > 1)
{
var dist2= value2.subtract(value1).length();
if( dist2 > dist) dist= dist2;
}
if(order > 2)
{
var dist3= value3.subtract(value2).length();
if( dist3 > dist) dist= dist3;
}
if(order > 3)
{
var dist4= value4.subtract(value3).length();
if( dist4 > dist) dist= dist4;
}
if(order > 4)
{
var dist5= value5.subtract(value4).length();
if( dist5 > dist) dist= dist5;
}
return dist;
}
function UpdateReached()
{
return UpdateReached2(GetDist());
}
function UpdateReached2(Dist)
{
if(reached)
{
if(Dist > reachThreshold)
reached= false;
}else
{
if(Dist <= reachThreshold)
reached= true;
}
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
PROTO OrientationDamper
[
eventIn SFRotation set_destination
field SFRotation initial_destination 0 0 1 0
eventOut SFRotation value_changed
field SFRotation initial_value 0 0 1 0
eventIn SFRotation set_value
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
eventOut SFBool isActive
eventOut SFBool isLoaded
field SFBool takeFirstInput FALSE
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn SFRotation set_destination IS set_destination
field SFRotation input IS initial_destination
eventOut SFRotation value_changed IS value_changed
field SFRotation initial_value IS initial_value
eventIn SFRotation set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFInt32 order IS order
field SFFloat eps IS eps
field SFBool takeFirstInput IS takeFirstInput
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field SFRotation value5 0 0 1 0
field SFRotation value4 0 0 1 0
field SFRotation value3 0 0 1 0
field SFRotation value2 0 0 1 0
field SFRotation value1 0 0 1 0
field SFBool bInitialized FALSE # This is a workaround because Contact does not allways call initialize() before the first event has been received.
field SFBool bNeedToTakeFirstInput FALSE
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true;
Init();
}
}
function Init()
{
bNeedToTakeFirstInput= takeFirstInput;
tau= effs.tau;
set_value(initial_value);
needTimer= input.x != initial_value.x
|| input.y != initial_value.y
|| input.z != initial_value.z
|| input.angle != initial_value.angle
;
}
function set_tau(t)
{
CheckInit();
tau= t;
}
function set_destination(i)
{
CheckInit();
if(bNeedToTakeFirstInput)
{
bNeedToTakeFirstInput= false;
set_value(i);
}
input= i;
StartTimer();
}
function set_value(o)
{
CheckInit();
bNeedToTakeFirstInput= false;
value1= value2= value3= value4= value5= o;
value_changed= o;
StartTimer();
}
function tick(now)
{
CheckInit();
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
lastTick= now;
var alpha= Math.exp(-delta / tau);
if(bNeedToTakeFirstInput) // then don't do any processing.
return;
value1= order > 0 && tau
? input .slerp(value1, alpha)
: input;
value2= order > 1 && tau
? value1.slerp(value2, alpha)
: value1;
value3= order > 2 && tau
? value2.slerp(value3, alpha)
: value2;
value4= order > 3 && tau
? value3.slerp(value4, alpha)
: value3;
value5= order > 4 && tau
? value5.slerp(value4, alpha)
: value4;
var dist= Math.abs(value1.inverse().multiply(input).angle);
if(order > 1)
{
var dist2= Math.abs(value2.inverse().multiply(value1).angle);
if( dist2 > dist) dist= dist2;
}
if(order > 2)
{
var dist3= Math.abs(value3.inverse().multiply(value2).angle);
if( dist3 > dist) dist= dist3;
}
if(order > 3)
{
var dist4= Math.abs(value4.inverse().multiply(value3).angle);
if( dist4 > dist) dist= dist4;
}
if(order > 4)
{
var dist5= Math.abs(value5.inverse().multiply(value4).angle);
if( dist5 > dist) dist= dist5;
}
if(dist < eps)
{
value1= value2= value3= value4= value5= input;
value_changed= input;
StopTimer();
return;
}
value_changed= value5;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
#
# NOTE:
# The ColorDamper animates SFColor values in the HSV space.
# Initially i though it'd be good to work in perception space
# rather than a technology oriented space (RGB).
# However, this leads to the case that when a color should
# go - say from red to blue, it does so by going through
# other colors, i.e. yellow and green, whereas an animation
# computed in RGB space appears to create a much directer
# transition.
#
# Animating in HSV space basically means to consider hue
# values of 0 and 1 as equal and finding the shorter
# of the two paths possible on a circle. Maybe there's an
# analogy with the slerp interpolation on a 4d sphere.
#
# The below implementation contains code for animating in
# HSV space, but it's commented out and replaced by code
# for calculating in RGB space.
#
PROTO ColorDamper
[
eventIn SFColor set_destination
field SFColor initial_destination 0 0 0
eventOut SFColor value_changed
field SFColor initial_value 0 0 0
eventIn SFColor set_value
exposedField SFFloat tau 1
exposedField SFFloat negTau -1 # not yet testet
# The idea behind negTau is to use a different
field SFInt32 order 1 # speed for when the brighness is to be lowered
# than when it is to be raised. This allows to
eventOut SFBool isActive # implement a lamp which comes to full brightness quickly
# when turned on, but has a much longer phase of after-
eventOut SFBool isLoaded # burn when turned off.
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
exposedField SFFloat negTau -1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn SFColor set_destination IS set_destination
field SFColor initial_destination IS initial_destination
eventOut SFColor value_changed IS value_changed
field SFColor initial_value IS initial_value
eventIn SFColor set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
eventIn SFFloat set_negTau
field SFFloat negTau -1
field SFInt32 order IS order
field SFFloat eps .001
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field SFVec3f input_HSV 0 0 0
field SFVec3f value5_HSV 0 0 0
field SFVec3f value4_HSV 0 0 0
field SFVec3f value3_HSV 0 0 0
field SFVec3f value2_HSV 0 0 0
field SFVec3f value1_HSV 0 0 0
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
tau= effs.tau;
negTau= effs.negTau;
// var HSV= initial_destination.getHSV();
// input_HSV.x= HSV[0];
// input_HSV.y= HSV[1];
// input_HSV.z= HSV[2];
input_HSV.x= initial_destination.r;
input_HSV.y= initial_destination.g;
input_HSV.z= initial_destination.b;
set_value(initial_value);
needTimer= initial_destination.r != initial_value.r
|| initial_destination.g != initial_value.g
|| initial_destination.b != initial_value.b
;
}
function set_tau(t)
{
tau= t;
}
function set_negTau(t)
{
negTau= t;
}
function set_destination(i)
{
// var HSV= i.getHSV();
// input_HSV.x= HSV[0];
// input_HSV.y= HSV[1];
// input_HSV.z= HSV[2];
input_HSV.x= i.r;
input_HSV.y= i.g;
input_HSV.z= i.b;
StartTimer();
}
function set_value(o)
{
// var HSV= o.getHSV();
// value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= new SFVec3f(HSV[0], HSV[1], HSV[2]);
value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= new SFVec3f(o.r, o.g, o.b);
value_changed= o;
StartTimer();
}
function dynamics(dest, val, alpha)
{
var diff= val.subtract(dest);
// if(diff[0] > .5) diff[0]-= 1;
// if(diff[0] < -.5) diff[0]+= 1;
var ret= dest.add(diff.multiply(alpha));
// if(ret[0] < 0) ret[0]+= 1;
// if(ret[0] > 1) ret[0]-= 1;
return ret;
}
function distanceHSV(a, b)
{
diffHue= a[0] - b[0];
// if(diffHue > .5) diffHue-= 1;
// if(diffHue < -.5) diffHue+= 1;
diffSat= a[1] - b[1];
diffVal= a[2] - b[2];
return Math.sqrt( diffHue * diffHue
+ diffSat * diffSat
+ diffVal * diffVal
);
}
function tick(now)
{
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
var TauUsed= negTau < 0
? tau
: ( value5_HSV[2] < input_HSV[2]
? tau
: negTau
)
;
var alpha= Math.exp(-delta / TauUsed);
value1_HSV= order > 0 && TauUsed
? dynamics(input_HSV, value1_HSV, alpha)
: input_HSV;
value2_HSV= order > 1 && TauUsed
? dynamics(value1_HSV, value2_HSV, alpha)
: value1_HSV;
value3_HSV= order > 2 && TauUsed
? dynamics(value2_HSV, value3_HSV, alpha)
: value2_HSV;
value4_HSV= order > 3 && TauUsed
? dynamics(value3_HSV, value4_HSV, alpha)
: value3_HSV;
value5_HSV= order > 4 && TauUsed
? dynamics(value4_HSV, value5_HSV, alpha)
: value4_HSV;
// Hmm, don't know if it is perception theoretically correct to calculate the distance in HSV space or in RGB space.
var dist= distanceHSV(value1_HSV, input_HSV);
if(order > 1)
{
var dist2= distanceHSV(value2_HSV, value1_HSV);
if( dist2 > dist) dist= dist2;
}
if(order > 2)
{
var dist3= distanceHSV(value3_HSV, value2_HSV);
if( dist3 > dist) dist= dist3;
}
if(order > 3)
{
var dist4= distanceHSV(value4_HSV, value3_HSV);
if( dist4 > dist) dist= dist4;
}
if(order > 4)
{
var dist5= distanceHSV(value5_HSV, value4_HSV);
if( dist5 > dist) dist= dist5;
}
if(dist < eps)
{
value1_HSV= value2_HSV= value3_HSV= value4_HSV= value5_HSV= input_HSV;
// value_changed.setHSV(input_HSV[0], input_HSV[1], input_HSV[2]);
value_changed= new SFColor(input_HSV[0], input_HSV[1], input_HSV[2]);
StopTimer();
return;
}
// value_changed.setHSV(value5_HSV[0], value5_HSV[1], value5_HSV[2]);
value_changed= new SFColor(value5_HSV[0], value5_HSV[1], value5_HSV[2]);
lastTick= now;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
PROTO Position2fDamper
[
eventIn SFVec2f set_destination
field SFVec2f initial_destination 0 0
eventOut SFVec2f value_changed
field SFVec2f initial_value 0 0
eventIn SFVec2f set_value
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
eventOut SFBool isActive
eventOut SFBool isLoaded
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn SFVec2f set_destination IS set_destination
field SFVec2f input IS initial_destination
eventOut SFVec2f value_changed IS value_changed
field SFVec2f initial_value IS initial_value
eventIn SFVec2f set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFInt32 order IS order
field SFFloat eps IS eps
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field SFVec2f value5 0 0
field SFVec2f value4 0 0
field SFVec2f value3 0 0
field SFVec2f value2 0 0
field SFVec2f value1 0 0
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
tau= effs.tau;
set_value(initial_value);
needTimer= input.x != initial_value.x
|| input.y != initial_value.y
;
}
function set_tau(t)
{
tau= t;
}
function set_destination(i)
{
input= i;
StartTimer();
}
function set_value(o)
{
value1= value2= value3= value4= value5= o;
value_changed= o;
StartTimer();
}
function tick(now)
{
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
var alpha= Math.exp(-delta / tau);
value1= order > 0 && tau
? input .add(value1.subtract(input ).multiply(alpha))
: input;
value2= order > 1 && tau
? value1.add(value2.subtract(value1).multiply(alpha))
: value1;
value3= order > 2 && tau
? value2.add(value3.subtract(value2).multiply(alpha))
: value2;
value4= order > 3 && tau
? value3.add(value4.subtract(value3).multiply(alpha))
: value3;
value5= order > 4 && tau
? value4.add(value5.subtract(value4).multiply(alpha))
: value4;
var dist= value1.subtract(input).length();
if(order > 1)
{
var dist2= value2.subtract(value1).length();
if( dist2 > dist) dist= dist2;
}
if(order > 2)
{
var dist3= value3.subtract(value2).length();
if( dist3 > dist) dist= dist3;
}
if(order > 3)
{
var dist4= value4.subtract(value3).length();
if( dist4 > dist) dist= dist4;
}
if(order > 4)
{
var dist5= value5.subtract(value4).length();
if( dist5 > dist) dist= dist5;
}
if(dist < eps)
{
value1= value2= value3= value4= value5= input;
value_changed= input;
StopTimer();
return;
}
value_changed= value5;
lastTick= now;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
# The value has always the same number of elements
# than the input. If the nuber of elements differ
# between initial_destination and initial_value, initial_value
# is modified to contain the correct number.
# If the number of elements is changed on set_destination,
# this change is propagated immediately to the value.
# (Cannot smoothly animate the number of elements.)
#
# All the Damper nodes send their start value on the
# value. The CoordinateDamper does not follow this
# rule in one case:
# If neither initial_destination nor initial_value contain
# some elements. Then it has to assume that simply no
# initial values are given on the PROTO instantiation.
# The value_changed sends no value on initialization
# if both, initial_destination and initial_value contain
# no elements. This is to avoid emitting an empty array
# on value_changed just because the size of the array
# is not known before the first event on set_destination
# arrives. If you don't want this, set initial_value
# to a proper initialization value.
#
# When the array length of the value has to be adjusted,
# the last elements of the array are deleted, or elements
# with the default value (0, 0, 0) of SFVec3f is appended.
#
# The array length is adjusted to the value received not
# only when a set_destination event is received, but also when a
# set_value event is received.
#
# TBD: Should an MFFloat version of tau be added?
# Resulting in a separate tau value for each
# element in the MFVec3f values?
PROTO CoordinateDamper
[
eventIn MFVec3f set_destination
field MFVec3f initial_destination []
eventOut MFVec3f value_changed
field MFVec3f initial_value []
eventIn MFVec3f set_value
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
eventOut SFBool isActive
eventOut SFBool isLoaded
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn MFVec3f set_destination IS set_destination
field MFVec3f input IS initial_destination
eventOut MFVec3f value_changed IS value_changed
field MFVec3f initial_value IS initial_value
eventIn MFVec3f set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFInt32 order IS order
field SFFloat eps IS eps
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field MFVec3f value5 []
field MFVec3f value4 []
field MFVec3f value3 []
field MFVec3f value2 []
field MFVec3f value1 []
field SFBool initialized FALSE # Contact seams to be able to send events before it calls the initialize() function.
# I think these events come from other Scripts initialize() functions and
# from Contacts immediate event propagation.
field SFBool valueHasBeenSet FALSE # for the same problem.
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
initialized= true;
tau= effs.tau;
initial_value.length= input.length;
if(initial_value.length || input.length)
{
if(!valueHasBeenSet)
{
// TBD: Don't know if this works in all cases.
value1=
value2=
value3=
value4=
value5= initial_value;
}
}
else{
// Nothing to be done here.
}
var NeedTimer= false;
for(var C= 0; C<input.length; C++)
{
if( input[C].x != initial_value[C].x
|| input[C].y != initial_value[C].y
|| input[C].z != initial_value[C].z
)
{
NeedTimer= true;
break;
}
}
needTimer= NeedTimer;
}
function set_tau(t)
{
tau= t;
}
function set_destination(i)
{
input= i;
value1.length=
value2.length=
value3.length=
value4.length=
value5.length=
i.length;
if(!initialized)
initial_value.length= i.length;
StartTimer();
}
function set_value(o)
{
value1=
value2=
value3=
value4=
value5= o;
input.length= o.length;
if(!initialized)
initial_value= o;
value_changed= o;
StartTimer();
}
function dynamics(dest, val, alpha)
{
var Val;
var Dest;
for(var C= 0; C<dest.length; C++ )
{
Val= val[C];
Dest= dest[C];
// Val= Dest.add(Val.subtract(Dest).multiply(alpha));
Val.x= Dest.x + (Val.x - Dest.x) * alpha;
Val.y= Dest.y + (Val.y - Dest.y) * alpha;
Val.z= Dest.z + (Val.z - Dest.z) * alpha;
val[C]= Val;
// val[C]= dest[C].add(val[C].subtract(dest[C]).multiply(alpha));
}
}
function IsDistLargerThan(a, b, Eps)
{
for(var C= 0; C<a.length; C++ )
{
var A= a[C];
var B= b[C];
var dist= Math.abs(A.x - B.x)
+ Math.abs(A.y - B.y)
+ Math.abs(A.z - B.z)
;
if(dist > Eps)
return true;
}
return false;
}
function tick(now)
{
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
var alpha= tau? Math.exp(-delta / tau) : 1;
if(order >0)
{
if(tau)
dynamics(input, value1, alpha);
else
value1= input;
}
if(order > 1)
{
if(tau)
dynamics(value1, value2, alpha);
else
value2= value1;
}
if(order > 2)
{
if(tau)
dynamics(value2, value3, alpha);
else
value3= value2;
}
if(order > 3)
{
if(tau)
dynamics(value3, value4, alpha);
else
value4= value3;
}
if(order > 4)
{
if(tau)
dynamics(value4, value5, alpha);
else
value5= value4;
}
// Don't Know if this has a performance penalty.
// var NeedContinue= false;
// if(IsDistLargerThan(value1, input, eps))
// NeedContinue= true;
// else if(order > 1 && IsDistLargerThan(value2, value1, eps))
// NeedContinue= true;
// else if(order > 2 && IsDistLargerThan(value3, value2, eps))
// NeedContinue= true;
// else if(order > 3 && IsDistLargerThan(value4, value3, eps))
// NeedContinue= true;
// else if(order > 4 && IsDistLargerThan(value5, value4, eps))
// NeedContinue= true;
// if(!NeedContinue)
// {
// value1= value2= value3= value4= value5= input;
// value_changed= input;
// needTimer= false;
// return;
// }
value_changed= order <= 1? value1 : (
order == 2? value2 : (
order == 3? value3 : (
order == 4? value4 : (
value5 ))));
;
lastTick= now;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
# The same as CoordinateDamper, but for MFVec2f.
PROTO TexCoordDamper
[
eventIn MFVec2f set_destination
field MFVec2f initial_destination []
eventOut MFVec2f value_changed
field MFVec2f initial_value []
eventIn MFVec2f set_value
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
eventOut SFBool isActive
eventOut SFBool isLoaded
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn MFVec2f set_destination IS set_destination
field MFVec2f input IS initial_destination
eventOut MFVec2f value_changed IS value_changed
field MFVec2f initial_value IS initial_value
eventIn MFVec2f set_value IS set_value
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFInt32 order IS order
field SFFloat eps IS eps
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field MFVec2f value5 []
field MFVec2f value4 []
field MFVec2f value3 []
field MFVec2f value2 []
field MFVec2f value1 []
field SFBool initialized FALSE # Contact seams to be able to send events before it calls the initialize() function.
# I think these events come from other Scripts initialize() functions and
# from Contacts immediate event propagation.
field SFBool valueHasBeenSet FALSE # for the same problem.
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
initialized= true;
tau= effs.tau;
initial_value.length= input.length;
if(initial_value.length || input.length)
{
if(!valueHasBeenSet)
{
// TBD: Don't know if this works in all cases.
value1=
value2=
value3=
value4=
value5= initial_value;
}
}
else{
// Nothing to be done here.
}
var NeedTimer= false;
for(var C= 0; C<input.length; C++)
{
if( input[C].x != initial_value[C].x
|| input[C].y != initial_value[C].y
)
{
NeedTimer= true;
break;
}
}
needTimer= NeedTimer;
}
function set_tau(t)
{
tau= t;
}
function set_destination(i)
{
input= i;
value1.length=
value2.length=
value3.length=
value4.length=
value5.length=
i.length;
if(!initialized)
initial_value.length= i.length;
StartTimer();
}
function set_value(o)
{
value1=
value2=
value3=
value4=
value5= o;
input.length= o.length;
if(!initialized)
initial_value= o;
value_changed= o;
StartTimer();
}
function dynamics(dest, val, alpha)
{
for(var C= 0; C<dest.length; C++ )
val[C]= dest[C].add(val[C].subtract(dest[C]).multiply(alpha));
}
function IsDistLargerThan(a, b, Eps)
{
for(var C= 0; C<a.length; C++ )
{
var A= a[C];
var B= b[C];
var dist= Math.abs(A.x - B.x)
+ Math.abs(A.y - B.y)
;
if(dist > Eps)
return true;
}
return false;
}
function tick(now)
{
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
var alpha= tau? Math.exp(-delta / tau) : 1;
if(order >0)
{
if(tau)
dynamics(input, value1, alpha);
else
value1= input;
}
if(order > 1)
{
if(tau)
dynamics(value1, value2, alpha);
else
value2= value1;
}
if(order > 2)
{
if(tau)
dynamics(value2, value3, alpha);
else
value3= value2;
}
if(order > 3)
{
if(tau)
dynamics(value3, value4, alpha);
else
value4= value3;
}
if(order > 4)
{
if(tau)
dynamics(value4, value5, alpha);
else
value5= value4;
}
// var NeedContinue= false;
// if(IsDistLargerThan(value1, input, eps))
// NeedContinue= true;
// else if(order > 1 && IsDistLargerThan(value2, value1, eps))
// NeedContinue= true;
// else if(order > 2 && IsDistLargerThan(value3, value2, eps))
// NeedContinue= true;
// else if(order > 3 && IsDistLargerThan(value4, value3, eps))
// NeedContinue= true;
// else if(order > 4 && IsDistLargerThan(value5, value4, eps))
// NeedContinue= true;
// if(!NeedContinue)
// {
// value1= value2= value3= value4= value5= input;
// value_changed= input;
// needTimer= false;
// return;
// }
value_changed= order <= 1? value1 : (
order == 2? value2 : (
order == 3? value3 : (
order == 4? value4 : (
value5 ))));
lastTick= now;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
PROTO ScalarDamper
[
eventIn SFFloat set_destination
field SFFloat initial_destination 0
eventOut SFFloat value_changed
field SFFloat initial_value 0
eventIn SFFloat set_value
eventOut SFFloat transparency_changed
exposedField SFFloat tau 1
field SFInt32 order 1
field SFFloat eps .001
eventOut SFBool isActive
eventOut SFBool isLoaded
field MFString credits "Initial idea and copyright by Herbert Stocker, http://www.hersto.net/"
]
{
PROTO EFFS # for connecting exposedFields with Script nodes in a spec compliant (VRML97) way.
[
exposedField SFFloat tau 1
]
{ Group {} }
DEF EFFS EFFS
{
tau IS tau
}
DEF Worker Script
{
eventIn SFFloat set_destination IS set_destination
field SFFloat input IS initial_destination
eventOut SFFloat value_changed IS value_changed
field SFFloat initial_value IS initial_value
eventIn SFFloat set_value IS set_value
eventOut SFFloat transparency_changed IS transparency_changed
field SFNode effs USE EFFS
eventIn SFFloat set_tau
field SFFloat tau 1
field SFInt32 order IS order
field SFFloat eps IS eps
# internal fields
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
field SFTime lastTick 0
field SFFloat value5 0
field SFFloat value4 0
field SFFloat value3 0
field SFFloat value2 0
field SFFloat value1 0
field SFBool haveOutput FALSE # This is a workaround because Contact does nt allwayscall initialize() before the first event has been received.
url "vrmlscript:
function StartTimer()
{
if(!needTimer)
{
lastTick= 0;
needTimer= true;
}
}
function StopTimer()
{
if(needTimer)
{
needTimer= false;
}
}
function initialize()
{
tau= effs.tau;
if(!haveOutput) set_value(initial_value);
needTimer= input != initial_value;
}
function set_tau(t)
{
tau= t;
}
function set_destination(i)
{
input= i;
StartTimer();
}
function set_value(o)
{
haveOutput= true;
value1= value2= value3= value4= value5= o;
value_changed= o;
SetTransparency();
StartTimer();
}
function tick(now)
{
if(!lastTick)
{
lastTick= now;
return;
}
var delta= now - lastTick;
var alpha= Math.exp(-delta / tau);
value1= order > 0 && tau
? input + (value1 - input) * alpha
: input;
value2= order > 1 && tau
? value1 + (value2 - value1) * alpha
: value1;
value3= order > 2 && tau
? value2 + (value3 - value2) * alpha
: value2;
value4= order > 3 && tau
? value3 + (value4 - value3) * alpha
: value3;
value5= order > 4 && tau
? value4 + (value5 - value4) * alpha
: value4;
var dist= Math.abs(value1 - input);
if(order > 1)
{
var dist2= Math.abs(value2 - value1);
if( dist2 > dist) dist= dist2;
}
if(order > 2)
{
var dist3= Math.abs(value3 - value2);
if( dist3 > dist) dist= dist3;
}
if(order > 3)
{
var dist4= Math.abs(value4 - value3);
if( dist4 > dist) dist= dist4;
}
if(order > 4)
{
var dist5= Math.abs(value5 - value4);
if( dist5 > dist) dist= dist5;
}
if(dist < eps)
{
value1= value2= value3= value4= value5= input;
value_changed= input;
SetTransparency();
StopTimer();
return;
}
value_changed= value5;
SetTransparency();
lastTick= now;
}
function SetTransparency()
{
transparency_changed= 1 - value_changed * value_changed;
}
"
}
DEF Timer TimeSensor
{
loop TRUE
}
ROUTE Worker.needTimer TO Timer.enabled
ROUTE Timer.time TO Worker.tick
ROUTE EFFS.tau TO Worker.set_tau
DEF LastNode Script
{
eventOut SFBool isLoaded IS isLoaded
url "vrmlscript:
function initialize()
{
isLoaded= true;
}
"
}
}
|