#VRML V2.0 utf8
# X3D-to-VRML-97 XSL translation autogenerated by X3dToVrml97.xslt
# https://www.web3d.org/x3d/content/X3dToVrml97.xslt
# Generated using XSLT processor: Saxonica
# [X3D] VRML V3.2 utf8
# PROFILE Immersive
# [X3D] version=3.2
# [X3D] noNamespaceSchemaLocation=https://www.web3d.org/specifications/x3d-3.2.xsd
# [head]
# META "title" "FollowerPrototypeDeclarations.x3d"
# META "description" "Original implementation pattern as prototype declarations for Follower (Chaser and Damper) nodes, useful for browser developers."
# META "creator" "Herbert Stocker"
# META "translator" "Don Brutzman"
# META "created" "18 April 2006"
# META "translated" "2 December 2011"
# META "modified" "20 October 2019"
# META "reference" "FollowerExternalPrototypeDeclarations.x3d"
# META "reference" "originals/Chasers.wrl"
# META "reference" "originals/Dampers.wrl"
# META "reference" "Stocker_06_Followers.pdf"
# META "reference" "http://www.hersto.com/Publications/Followers"
# META "requires" "X3D version 3.0, 3.1"
# META "subject" "X3D Follower Chaser Damper"
# META "warning" "under development"
# META "TODO" "Rename and test these prototypes to match final names in X3D Specification Followers Component"
# META "TODO" "Ensure full coverage of follower nodes in order to provide backwards compatibility with X3D v3.0 and v3.1."
# META "TODO" "Xj3D Player Bugzilla Issuehttp://bugzilla.xj3d.org/show_bug.cgi?id=639"
# META "reference" "https://www.web3d.org/x3d/specifications/ISO-IEC-19775-1.2-X3D-AbstractSpecification/Part01/components/followers.html"
# META "reference" "https://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html"
# META "identifier" "https://www.web3d.org/x3d/content/examples/Basic/Followers/FollowerPrototypeDeclarations.x3d"
# META "generator" "Vrml97ToX3dNist, http://ovrt.nist.gov/v2_x3d.html"
# META "generator" "X3D-Edit 3.3, https://savage.nps.edu/X3D-Edit"
# META "license" "../../license.html"
# [Scene] ========== ========== ==========
NavigationInfo { type [ "EXAMINE" "ANY" ] } ### Default X3D NavigationInfo
WorldInfo {
info [ "The ExternProto nodes found in this file implement principles described in the paper" "Linear Filters - Animating Objects in a Flexible and Pleasing Way" "They have been proposed and added to the X3D standard in 2006." "Webpage: http://www.hersto.net/Followers" "" "Please use the code in this file in any content or application you like" "or modify it in any way." "" "The code here works, however things like detecting when a transition has ended" "and when the node can stop calculating and updating the output or secondary fields" "like set_value or initial_destination are not yet implemented." "Nevertheless, set_destination and value_changed do work." ]
title "Damper nodes"
}
PROTO PositionChaser [
eventOut SFVec3f value_changed
eventIn SFVec3f set_value
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
field MFString credits [
"Initial idea and copyright by Herbert Stocker http://www.hersto.net"
]
eventOut SFBool isActive
eventIn SFVec3f set_destination
field SFTime duration 1.0
field SFVec3f initial_destination 0.0 0.0 0.0
field SFVec3f initial_value 0.0 0.0 0.0
] {
DEF ScreenPositionDamper_PositionChaser Script {
eventIn SFTime Tick
eventIn SFVec3f set_value IS set_value
field SFTime duration IS duration
field MFVec3f Buffer [ ]
field SFBool bInitialized FALSE
field SFTime BufferEndTime 0.0
field SFInt32 cNumSupports 10
eventIn SFVec3f set_destination IS set_destination
eventOut SFVec3f value_changed IS value_changed
field SFTime cStepTime 0.0
field SFVec3f previousValue 0.0 0.0 0.0
field SFVec3f initial_destination IS initial_destination
field SFVec3f destination 0.0 0.0 0.0
eventOut SFBool isActive IS isActive
field SFVec3f initial_value IS initial_value
### Warning: inputOnly field 'set_value' has no input-event method definition 'function set_value (SFVec3fValue, timestamp) { }' in contained ecmascript: code
### Warning: Script outputOnly field 'isActive' has no assignment statement such as 'isActive=(someSFBoolExpression);' in contained ecmascript: code
### Warning: Script 'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use definitions instead.
url [ "javascript:
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true; // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
Init();
}
}
function Init()
{
destination= initial_destination;
Buffer.length= cNumSupports;
Buffer[0]= initial_destination;
for(var C= 1; C=0; C-- )
{
var DeltaIn= Buffer[C].subtract(Buffer[C + 1]);
var DeltaOut= DeltaIn.multiply(StepResponse((C + Frac) * cStepTime));
Output= Output.add(DeltaOut);
}
if(Output != value_changed)
value_changed= Output;
}
function UpdateBuffer(Now)
{
var Frac= (Now - BufferEndTime) / cStepTime;
// is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
// of the oldest entry has already reached its destination, and it's time for a newer entry.
// has already reached it
// In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.
if(Frac >= 1)
{
var NumToShift= Math.floor(Frac);
Frac-= NumToShift;
if(NumToShift < Buffer.length)
{ // normal case.
previousValue= Buffer[Buffer.length - NumToShift];
for(var C= Buffer.length - 1; C>=NumToShift; C-- )
Buffer[C]= Buffer[C - NumToShift];
for(var C= 0; C duration)
return 1;
// When optimizing for speed, the above two if(.) cases can be omitted,
// as this funciton will not be called for values outside of 0..duration.
return StepResponseCore(t / duration);
}
// This function defines the shape of how the output responds to the input.
// It must accept values for T in the range 0 <= T <= 1.
// In order to create a smooth animation, it should return 0 for T == 0,
// 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.
// It should be optimized for speed, in order for high performance. It's
// executed Buffer.length + 1 times each simulation tick.
function StepResponseCore(T)
{
return .5 - .5 * Math.cos(T * Math.PI);
}
// The following functions are not used. They provide other responses (for fun).
function StepResponseCoreF(T)
{
var cTau= .3;
var cFrequency= 2.5;
return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
}
function StepResponseCoreE(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreD(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreC(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreB(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreA(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
var Alpha= .2 * T;
return A * (1 - Alpha) + B * Alpha;
}
" ]
}
DEF Tmer_PositionChaser TimeSensor {
loop TRUE
}
ROUTE Tmer_PositionChaser.time TO ScreenPositionDamper_PositionChaser.Tick
}
PROTO OrientationChaser [
eventOut SFRotation value_changed
eventIn SFRotation set_value
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
field MFString credits [
"Initial idea and copyright by Herbert Stocker http://www.hersto.net/"
]
eventOut SFBool isActive
eventIn SFRotation set_destination
field SFTime duration 1.0
field SFRotation initial_destination 0.0 0.0 1.0 0.0
field SFRotation initial_value 0.0 0.0 1.0 0.0
] {
DEF ScreenPositionDamper_OrientationChaser Script {
eventIn SFTime Tick
eventIn SFRotation set_value IS set_value
field SFTime duration IS duration
field MFRotation Buffer [ ]
field SFBool bInitialized FALSE
field SFTime BufferEndTime 0.0
field SFInt32 cNumSupports 10
eventIn SFRotation set_destination IS set_destination
eventOut SFRotation value_changed IS value_changed
field SFTime cStepTime 0.0
field SFRotation previousValue 0.0 0.0 1.0 0.0
field SFRotation initial_destination IS initial_destination
field SFRotation destination 0.0 0.0 1.0 0.0
eventOut SFBool isActive IS isActive
field SFRotation initial_value IS initial_value
### Warning: inputOnly field 'set_value' has no input-event method definition 'function set_value (SFRotationValue, timestamp) { }' in contained ecmascript: code
### Warning: Script outputOnly field 'isActive' has no assignment statement such as 'isActive=(someSFBoolExpression);' in contained ecmascript: code
### Warning: Script 'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use definitions instead.
url [ "javascript:
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true; // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
Init();
}
}
function Init()
{
destination= initial_destination;
Buffer.length= cNumSupports;
Buffer[0]= initial_destination;
for(var C= 1; C=0; C-- )
{
var DeltaIn= Buffer[C + 1].inverse().multiply(Buffer[C]);
Output= Output.slerp(Output.multiply(DeltaIn), StepResponse((C + Frac) * cStepTime));
}
if(Output != value_changed)
value_changed= Output;
}
function UpdateBuffer(Now)
{
var Frac= (Now - BufferEndTime) / cStepTime;
// is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
// of the oldest entry has already reached its destination, and it's time for a newer entry.
// has already reached it
// In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.
if(Frac >= 1)
{
var NumToShift= Math.floor(Frac);
Frac-= NumToShift;
if(NumToShift < Buffer.length)
{ // normal case.
previousValue= Buffer[Buffer.length - NumToShift];
for(var C= Buffer.length - 1; C>=NumToShift; C-- )
Buffer[C]= Buffer[C - NumToShift];
for(var C= 0; C duration)
return 1;
// When optimizing for speed, the above two if(.) cases can be omitted,
// as this funciton will not be called for values outside of 0..duration.
return StepResponseCore(t / duration);
}
// This function defines the shape of how the output responds to the input.
// It must accept values for T in the range 0 <= T <= 1.
// In order to create a smooth animation, it should return 0 for T == 0,
// 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.
// It should be optimized for speed, in order for high performance. It's
// executed Buffer.length + 1 times each simulation tick.
function StepResponseCore(T)
{
return .5 - .5 * Math.cos(T * Math.PI);
}
// The following functions are not used. They provide other responses (for fun).
function StepResponseCoreG(T)
{
var cTau= .3;
var cFrequency= 5;
return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
}
function StepResponseCoreF(T)
{
var cTau= .3;
var cFrequency= 2.5;
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
}
function StepResponseCoreE(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreD(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreC(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreB(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreA(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
var Alpha= .2 * T;
return A * (1 - Alpha) + B * Alpha;
}
" ]
}
DEF Tmer_OrientationChaser TimeSensor {
loop TRUE
}
ROUTE Tmer_OrientationChaser.time TO ScreenPositionDamper_OrientationChaser.Tick
}
PROTO Position2fChaser [
eventOut SFVec2f value_changed
eventIn SFVec2f set_value
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
field MFString credits [
"Initial idea and copyright by Herbert Stocker http://www.hersto.net/"
]
eventOut SFBool isActive
eventIn SFVec2f set_destination
field SFTime duration 1.0
field SFVec2f initial_destination 0.0 0.0
field SFVec2f initial_value 0.0 0.0
] {
DEF ScreenPositionDamper_Position2fChaser Script {
eventIn SFTime Tick
eventIn SFVec2f set_value IS set_value
field SFTime duration IS duration
field MFVec2f Buffer [ ]
field SFBool bInitialized FALSE
field SFTime BufferEndTime 0.0
field SFInt32 cNumSupports 10
eventIn SFVec2f set_destination IS set_destination
eventOut SFVec2f value_changed IS value_changed
field SFTime cStepTime 0.0
field SFVec2f previousValue 0.0 0.0
field SFVec2f initial_destination IS initial_destination
field SFVec2f destination 0.0 0.0
eventOut SFBool isActive IS isActive
field SFVec2f initial_value IS initial_value
### Warning: inputOnly field 'set_value' has no input-event method definition 'function set_value (SFVec2fValue, timestamp) { }' in contained ecmascript: code
### Warning: Script outputOnly field 'isActive' has no assignment statement such as 'isActive=(someSFBoolExpression);' in contained ecmascript: code
### Warning: Script 'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use definitions instead.
url [ "javascript:
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true; // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
Init();
}
}
function Init()
{
destination= initial_destination;
Buffer.length= cNumSupports;
Buffer[0]= initial_destination;
for(var C= 1; C=0; C-- )
{
var DeltaIn= Buffer[C].subtract(Buffer[C + 1]);
var DeltaOut= DeltaIn.multiply(StepResponse((C + Frac) * cStepTime));
Output= Output.add(DeltaOut);
}
if(Output != value_changed)
value_changed= Output;
}
function UpdateBuffer(Now)
{
var Frac= (Now - BufferEndTime) / cStepTime;
// is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
// of the oldest entry has already reached its destination, and it's time for a newer entry.
// has already reached it
// In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.
if(Frac >= 1)
{
var NumToShift= Math.floor(Frac);
Frac-= NumToShift;
if(NumToShift < Buffer.length)
{ // normal case.
previousValue= Buffer[Buffer.length - NumToShift];
for(var C= Buffer.length - 1; C>=NumToShift; C-- )
Buffer[C]= Buffer[C - NumToShift];
for(var C= 0; C duration)
return 1;
// When optimizing for speed, the above two if(.) cases can be omitted,
// as this funciton will not be called for values outside of 0..duration.
return StepResponseCore(t / duration);
}
// This function defines the shape of how the output responds to the input.
// It must accept values for T in the range 0 <= T <= 1.
// In order to create a smooth animation, it should return 0 for T == 0,
// 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.
// It should be optimized for speed, in order for high performance. It's
// executed Buffer.length + 1 times each simulation tick.
function StepResponseCore(T)
{
return .5 - .5 * Math.cos(T * Math.PI);
}
// The following functions are not used. They provide other responses (for fun).
function StepResponseCoreF(T)
{
var cTau= .3;
var cFrequency= 2.5;
return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
}
function StepResponseCoreE(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreD(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreC(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreB(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreA(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
var Alpha= .2 * T;
return A * (1 - Alpha) + B * Alpha;
}
" ]
}
DEF Tmer_Position2fChaser TimeSensor {
loop TRUE
}
ROUTE Tmer_Position2fChaser.time TO ScreenPositionDamper_Position2fChaser.Tick
}
PROTO PlacementChaser [
eventOut SFBool isLoaded
eventIn SFVec3f set_valuePos
eventIn SFRotation set_valueOri
eventIn SFVec3f set_destinationPos
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
field MFString credits [
"Initial idea and copyright by Herbert Stocker http://www.hersto.net/"
]
field SFTime duration 1.0
eventIn SFRotation set_destinationOri
field SFVec3f initial_valuePos 0.0 0.0 0.0
field SFVec3f initial_destinationPos 0.0 0.0 0.0
eventOut SFVec3f valuePos_changed
field SFRotation initial_valueOri 0.0 0.0 1.0 0.0
field SFRotation initial_destinationOri 0.0 0.0 1.0 0.0
eventOut SFRotation valueOri_changed
eventOut SFBool isActive
] {
DEF ScreenPositionDamper_PlacementChaser Script {
field SFRotation previousValueOri 0.0 0.0 1.0 0.0
eventIn SFTime Tick
field SFTime duration IS duration
eventIn SFRotation set_destinationOri IS set_destinationOri
field SFBool bInitialized FALSE
eventIn SFRotation set_valueOri IS set_valueOri
field SFVec3f previousValuePos 0.0 0.0 0.0
field SFRotation destinationOri 0.0 0.0 1.0 0.0
field SFRotation initial_valueOri IS initial_valueOri
eventIn SFVec3f set_destinationPos IS set_destinationPos
field SFTime BufferEndTime 0.0
field SFInt32 cNumSupports 10
eventIn SFVec3f set_valuePos IS set_valuePos
field SFTime cStepTime 0.0
field SFRotation initial_destinationOri IS initial_destinationOri
field MFRotation BufferOri [ ]
field SFVec3f destinationPos 0.0 0.0 0.0
field SFVec3f initial_valuePos IS initial_valuePos
eventOut SFVec3f valuePos_changed IS valuePos_changed
eventOut SFBool isActive IS isActive
field SFVec3f initial_destinationPos IS initial_destinationPos
eventOut SFRotation valueOri_changed IS valueOri_changed
field MFVec3f BufferPos [ ]
### Warning: inputOnly field 'set_valueOri' has no input-event method definition 'function set_valueOri (SFRotationValue, timestamp) { }' in contained ecmascript: code
### Warning: inputOnly field 'set_valuePos' has no input-event method definition 'function set_valuePos (SFVec3fValue, timestamp) { }' in contained ecmascript: code
### Warning: Script outputOnly field 'isActive' has no assignment statement such as 'isActive=(someSFBoolExpression);' in contained ecmascript: code
### Warning: Script 'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use definitions instead.
### Warning: mismatched (i.e. odd number of) 'single quote' marks in contained ecmascript: code
url [ "javascript:
function initialize()
{
CheckInit();
}
function CheckInit()
{
if(!bInitialized)
{
bInitialized= true; // Init() may call other functions that call CheckInit(). In that case it's better the flag is already set, otherwise an endless loop would occur.
Init();
}
}
function Init()
{
destinationPos= initial_destinationPos;
destinationOri= initial_destinationOri;
BufferPos.length=
BufferOri.length= cNumSupports;
BufferPos[0]= initial_destinationPos;
BufferOri[0]= initial_destinationOri;
for(var C= 1; C=0; C-- )
{
var DeltaInPos= BufferPos[C].subtract(BufferPos[C + 1]);
var DeltaInOri= BufferOri[C + 1].inverse().multiply(BufferOri[C]);
var DeltaOutPos= DeltaInPos.multiply(StepResponse((C + Frac) * cStepTime));
OutputPos= OutputPos.add(DeltaOutPos);
OutputOri= OutputOri.slerp(OutputOri.multiply(DeltaInOri), StepResponse((C + Frac) * cStepTime));
}
if(OutputPos != valuePos_changed)
valuePos_changed= OutputPos;
if(OutputOri != valueOri_changed)
valueOri_changed= OutputOri;
}
function UpdateBuffer(Now)
{
var Frac= (Now - BufferEndTime) / cStepTime;
// is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
// of the oldest entry has already reached its destination, and it's time for a newer entry.
// has already reached it
// In the case of a very low frame rate, or a very short cStepTime we may need to shift by more than one entry.
if(Frac >= 1)
{
var NumToShift= Math.floor(Frac);
Frac-= NumToShift;
if(NumToShift < BufferPos.length)
{ // normal case.
previousValuePos= BufferPos[BufferPos.length - NumToShift];
previousValueOri= BufferOri[BufferOri.length - NumToShift];
for(var C= BufferPos.length - 1; C>=NumToShift; C-- )
{
BufferPos[C]= BufferPos[C - NumToShift];
BufferOri[C]= BufferOri[C - NumToShift];
}
for(var C= 0; C duration)
return 1;
// When optimizing for speed, the above two if(.) cases can be omitted,
// as this funciton will not be called for values outside of 0..duration.
return StepResponseCore(t / duration);
}
// This function defines the shape of how the output responds to the input.
// It must accept values for T in the range 0 <= T <= 1.
// In order to create a smooth animation, it should return 0 for T == 0,
// 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.
// It should be optimized for speed, in order for high performance. It's
// executed Buffer.length + 1 times each simulation tick.
function StepResponseCore(T)
{
return .5 - .5 * Math.cos(T * Math.PI);
}
// The following functions are not used. They provide other responses (for fun).
function StepResponseCoreF(T)
{
var cTau= .3;
var cFrequency= 2.5;
return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T) * (1 - T);
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI));
// return 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (.5 + .5 * Math.cos(T * Math.PI))* (.5 + .5 * Math.cos(T * Math.PI));
}
function StepResponseCoreE(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin(Math.sqrt(1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreD(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cFrequency= 2.5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.sin((1 - T) * Math.PI/2);
return A * .8 + B * .2;
}
function StepResponseCoreC(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) * Math.exp(-T / cTau) * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreB(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
return A * .8 + B * .2;
}
function StepResponseCoreA(T)
{
var A= .5 - .5 * Math.cos(T * Math.PI);
var cTau= .3;
var cFrequency= 5;
var B= 1 - Math.cos(T * 2 * Math.PI * cFrequency) /** Math.exp(-T / cTau)*/ * (1 - T);
var Alpha= .2 * T;
return A * (1 - Alpha) + B * Alpha;
}
" ]
}
DEF Tmer_PlacementChaser TimeSensor {
loop TRUE
}
ROUTE Tmer_PlacementChaser.time TO ScreenPositionDamper_PlacementChaser.Tick
DEF LastNode Script {
eventOut SFBool isLoaded IS isLoaded
url [ "javascript:
function initialize()
{
isLoaded= true;
}
" ]
}
}
PROTO PositionDamper [
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
eventOut SFBool isLoaded
eventOut SFVec3f value_changed
eventIn SFVec3f set_destination
field SFBool takeFirstInput TRUE
field SFVec3f initial_destination 0.0 0.0 0.0
field SFInt32 order 1
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
field MFString credits [
"Initial idea and copyright by Herbert Stocker http://www.hersto.net/"
]
field SFFloat reachThreshold 0.01
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
exposedField SFFloat tau 1.0
eventIn SFVec3f set_value
eventOut SFBool reached
field SFVec3f initial_value 0.0 0.0 0.0
eventOut SFBool isActive
field SFFloat eps 0.0010
] {
### Warning: ProtoBody child (or descendant) Shape following first child will not be rendered, since the first child determines node type of this prototype. (Authors can silence this warning by placing a comment as second child.)
PROTO EFFS [
### Warning: ProtoInterface is unreferenced in ProtoBody with no corresponding IS/connect/@protoField
exposedField SFFloat tau 1.0
] {
Group {
}
}
DEF EFFS EFFS {
tau 1.0
}
DEF Worker Script {
eventIn SFVec3f set_value IS set_value
field SFBool IsCortona FALSE
field SFBool bInitialized FALSE
field SFFloat reachThreshold IS reachThreshold
field SFTime lastTick 0.0
field SFBool bNeedToTakeFirstInput TRUE
field SFVec3f value5 0.0 0.0 0.0
field SFVec3f value4 0.0 0.0 0.0
field SFVec3f value3 0.0 0.0 0.0
field SFVec3f value2 0.0 0.0 0.0
field SFVec3f input IS initial_destination
field SFVec3f value1 0.0 0.0 0.0
field SFFloat eps IS eps
eventIn SFVec3f set_destination IS set_destination
eventOut SFVec3f value_changed IS value_changed
field SFFloat tau 1.0
field SFNode effs USE EFFS
field SFInt32 order IS order
eventOut SFBool needTimer IS isActive
eventIn SFTime tick
eventIn SFFloat set_tau
field SFVec3f initial_value IS initial_value
eventOut SFBool reached IS reached
field SFBool takeFirstInput IS takeFirstInput
### Warning: Script 'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use definitions instead.
### Warning: mismatched (i.e. odd number of) 'single quote' marks in contained ecmascript: code
### Warning: Script attribute directOutput='true' usually needed when field(s) of type SFNode/MFNode are present. directOutput false means Script is not allowed to modify referenced nodes.
url [ "javascript:
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_PositionDamper TimeSensor {
loop TRUE
}
ROUTE Worker.needTimer TO Timer_PositionDamper.enabled
ROUTE Timer_PositionDamper.time TO Worker.tick
}