1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.2//EN" "https://www.web3d.org/specifications/x3d-3.2.dtd">
|
3 | <X3D profile='Immersive' version='3.2' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.2.xsd'> |
4 | <head> |
5 | <meta name='title' content='FollowerPrototypeDeclarations.x3d'/> |
6 | <meta name='description' content='Original implementation pattern as prototype declarations for Follower (Chaser and Damper) nodes, useful for browser developers.'/> |
7 | <meta name='creator' content='Herbert Stocker'/> |
8 | <meta name='translator' content='Don Brutzman'/> |
9 | <meta name='created' content='18 April 2006'/> |
10 | <meta name='translated' content='2 December 2011'/> |
11 | <meta name='modified' content='20 October 2019'/> |
12 | <meta name='reference' content='FollowerExternalPrototypeDeclarations.x3d'/> |
13 | <meta name='reference' content='originals/Chasers.wrl'/> |
14 | <meta name='reference' content='originals/Dampers.wrl'/> |
15 | <meta name='reference' content='Stocker_06_Followers.pdf'/> |
16 | <meta name='reference' content='http://www.hersto.com/Publications/Followers'/> |
17 | <meta name='requires' content='X3D version 3.0, 3.1'/> |
18 | <meta name='subject' content='X3D Follower Chaser Damper'/> |
19 | <meta name=' warning ' content=' under development '/> |
20 | <meta name=' TODO ' content=' Rename and test these prototypes to match final names in X3D Specification Followers Component '/> |
21 | <meta name=' TODO ' content=' Ensure full coverage of follower nodes in order to provide backwards compatibility with X3D v3.0 and v3.1. '/> |
22 | <meta name=' TODO ' content=' Xj3D Player Bugzilla Issue http://bugzilla.xj3d.org/show_bug.cgi?id=639 '/> |
23 | <meta name='reference' content='https://www.web3d.org/x3d/specifications/ISO-IEC-19775-1.2-X3D-AbstractSpecification/Part01/components/followers.html'/> |
24 | <meta name='reference' content='https://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html'/> |
25 | <meta name='identifier' content='https://www.web3d.org/x3d/content/examples/Basic/Followers/FollowerPrototypeDeclarations.x3d'/> |
26 | <meta name='generator' content='Vrml97ToX3dNist, http://ovrt.nist.gov/v2_x3d.html'/> |
27 | <meta name='generator' content='X3D-Edit 3.3, https://savage.nps.edu/X3D-Edit'/> |
28 | <meta name='license' content='../../license.html'/> |
29 | </head> |
30 | <Scene> |
31 | <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'/> |
32 | <ProtoDeclare name='PositionChaser'> |
33 | <ProtoInterface> |
34 | <field name='value_changed' type='SFVec3f' accessType='outputOnly'/> |
35 | <field name='set_value' type='SFVec3f' accessType='inputOnly'/> |
36 | <field name='credits' type='MFString' value=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.net"' accessType='initializeOnly'/> |
37 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
38 | <field name='set_destination' type='SFVec3f' accessType='inputOnly'/> |
39 | <field name='duration' type='SFTime' value='1.0' accessType='initializeOnly'/> |
40 | <field name='initial_destination' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
41 | <field name='initial_value' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
42 | </ProtoInterface> |
43 | <ProtoBody> |
44 |
<!-- ROUTE information for ScreenPositionDamper_PositionChaser node:
[from Tmer_PositionChaser.time to Tick
]
-->
<Script DEF='ScreenPositionDamper_PositionChaser'> |
45 | <field name='Tick' type='SFTime' accessType='inputOnly'/> |
46 | <field name='set_value' type='SFVec3f' accessType='inputOnly'/> |
47 | <field name='duration' type='SFTime' accessType='initializeOnly'/> |
48 | <field name='Buffer' type='MFVec3f' accessType='initializeOnly'/> |
49 | <field name='bInitialized' type='SFBool' value='false' accessType='initializeOnly'/> |
50 | <field name='BufferEndTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
51 | <field name='cNumSupports' type='SFInt32' value='10' accessType='initializeOnly'/> |
52 | <field name='set_destination' type='SFVec3f' accessType='inputOnly'/> |
53 | <field name='value_changed' type='SFVec3f' accessType='outputOnly'/> |
54 | <field name='cStepTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
55 | <field name='previousValue' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
56 | <field name='initial_destination' type='SFVec3f' accessType='initializeOnly'/> |
57 | <field name='destination' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
58 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
59 | <field name='initial_value' type='SFVec3f' accessType='initializeOnly'/> |
60 | <IS> |
61 | <connect nodeField='set_value' protoField='set_value'/> |
62 | <connect nodeField='duration' protoField='duration'/> |
63 | <connect nodeField='set_destination' protoField='set_destination'/> |
64 | <connect nodeField='value_changed' protoField='value_changed'/> |
65 | <connect nodeField='initial_destination' protoField='initial_destination'/> |
66 | <connect nodeField='isActive' protoField='isActive'/> |
67 | <connect nodeField='initial_value' protoField='initial_value'/> |
68 | </IS> |
<![CDATA[
ecmascript: 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<Buffer.length; C++ ) Buffer[C]= initial_value; previousValue= initial_value; cStepTime= duration / cNumSupports; } function set_destination(Dest, Now) { CheckInit(); destination= Dest; // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer. // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the // output because Buffer[0] is associated with a value in the past. UpdateBuffer(Now); } function Tick(Now) { CheckInit(); if(!BufferEndTime) { BufferEndTime= Now; // first event we received, so we are in the initialization phase. value_changed= initial_value; return; } var Frac= UpdateBuffer(Now); // Frac is a value in 0 <= Frac < 1. // Now we can calculate the output. // This means we calculate the delta between each entry in Buffer and its previous // entries, calculate the step response of each such step and add it to form the output. // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has // no previous value. More exactly, we haven't stored a previous value anymore. // However, the step response of that missing previous value has already reached its // destination, so we can - would we have that previous value - use this as a start point // for adding the step responses. // Actually UpdateBuffer(.) maintains this value in var Output= previousValue; var DeltaIn= Buffer[Buffer.length - 1].subtract(previousValue); var DeltaOut= DeltaIn.multiply(StepResponse((Buffer.length - 1 + Frac) * cStepTime)); Output= Output.add(DeltaOut); for(var C= Buffer.length - 2; 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<NumToShift; C++ ) { // Hmm, we have a destination value, but don't know how it has // reached the current state. // Therefore we do a linear interpolation from the latest value in the buffer to destination. var Alpha= C / NumToShift; Buffer[C]= Buffer[NumToShift].multiply(Alpha).add(destination.multiply((1 - Alpha))); } }else { // degenerated case: // // We have a _VERY_ low frame rate... // we can only guess how we should fill the array. // Maybe we could write part of a linear interpolation // from Buffer[0] to destination, that goes from BufferEndTime to Now // (possibly only the end of the interpolation is to be written), // but if we rech here we are in a very degenerate case... // Thus we just write destination to the buffer. previousValue= NumToShift == Buffer.length? Buffer[0] : destination; for(var C= 0; C<Buffer.length; C++ ) Buffer[C]= destination; } BufferEndTime+= NumToShift * cStepTime; } return Frac; } function StepResponse(t) { if(t < 0) return 0; if(t > 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; }
]]>
|
|
70 | </Script> |
71 |
<!-- ROUTE information for Tmer_PositionChaser node:
[from time to ScreenPositionDamper_PositionChaser.Tick
]
-->
<TimeSensor DEF='Tmer_PositionChaser' loop='true'/> |
72 | < ROUTE fromNode='Tmer_PositionChaser' fromField='time' toNode='ScreenPositionDamper_PositionChaser' toField='Tick'/> |
73 | </ProtoBody> |
74 | </ProtoDeclare> |
75 | <ProtoDeclare name='OrientationChaser'> |
76 | <ProtoInterface> |
77 | <field name='value_changed' type='SFRotation' accessType='outputOnly'/> |
78 | <field name='set_value' type='SFRotation' accessType='inputOnly'/> |
79 | <field name='credits' type='MFString' value=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.net/"' accessType='initializeOnly'/> |
80 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
81 | <field name='set_destination' type='SFRotation' accessType='inputOnly'/> |
82 | <field name='duration' type='SFTime' value='1.0' accessType='initializeOnly'/> |
83 | <field name='initial_destination' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
84 | <field name='initial_value' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
85 | </ProtoInterface> |
86 | <ProtoBody> |
87 |
<!-- ROUTE information for ScreenPositionDamper_OrientationChaser node:
[from Tmer_OrientationChaser.time to Tick
]
-->
<Script DEF='ScreenPositionDamper_OrientationChaser'> |
88 | <field name='Tick' type='SFTime' accessType='inputOnly'/> |
89 | <field name='set_value' type='SFRotation' accessType='inputOnly'/> |
90 | <field name='duration' type='SFTime' accessType='initializeOnly'/> |
91 | <field name='Buffer' type='MFRotation' accessType='initializeOnly'/> |
92 | <field name='bInitialized' type='SFBool' value='false' accessType='initializeOnly'/> |
93 | <field name='BufferEndTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
94 | <field name='cNumSupports' type='SFInt32' value='10' accessType='initializeOnly'/> |
95 | <field name='set_destination' type='SFRotation' accessType='inputOnly'/> |
96 | <field name='value_changed' type='SFRotation' accessType='outputOnly'/> |
97 | <field name='cStepTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
98 | <field name='previousValue' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
99 | <field name='initial_destination' type='SFRotation' accessType='initializeOnly'/> |
100 | <field name='destination' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
101 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
102 | <field name='initial_value' type='SFRotation' accessType='initializeOnly'/> |
103 | <IS> |
104 | <connect nodeField='set_value' protoField='set_value'/> |
105 | <connect nodeField='duration' protoField='duration'/> |
106 | <connect nodeField='set_destination' protoField='set_destination'/> |
107 | <connect nodeField='value_changed' protoField='value_changed'/> |
108 | <connect nodeField='initial_destination' protoField='initial_destination'/> |
109 | <connect nodeField='isActive' protoField='isActive'/> |
110 | <connect nodeField='initial_value' protoField='initial_value'/> |
111 | </IS> |
<![CDATA[
ecmascript: 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<Buffer.length; C++ ) Buffer[C]= initial_value; previousValue= initial_value; cStepTime= duration / cNumSupports; } function set_destination(Dest, Now) { CheckInit(); destination= Dest; // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer. // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the // output because Buffer[0] is associated with a value in the past. UpdateBuffer(Now); } function Tick(Now) { CheckInit(); if(!BufferEndTime) { BufferEndTime= Now; // first event we received, so we are in the initialization phase. value_changed= initial_value; return; } var Frac= UpdateBuffer(Now); // Frac is a value in 0 <= Frac < 1. // Now we can calculate the output. // This means we calculate the delta between each entry in Buffer and its previous // entries, calculate the step response of each such step and add it to form the output. // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has // no previous value. More exactly, we haven't stored a previous value anymore. // However, the step response of that missing previous value has already reached its // destination, so we can - would we have that previous value - use this as a start point // for adding the step responses. // Actually UpdateBuffer(.) maintains this value in var Output= previousValue; var DeltaIn= previousValue.inverse().multiply(Buffer[Buffer.length - 1]); Output= Output.slerp(Output.multiply(DeltaIn), StepResponse((Buffer.length - 1 + Frac) * cStepTime)); for(var C= Buffer.length - 2; 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<NumToShift; C++ ) { // Hmm, we have a destination value, but don't know how it has // reached the current state. // Therefore we do a linear interpolation from the latest value in the buffer to destination. Buffer[C]= destination.slerp(Buffer[NumToShift], C / NumToShift); } }else { // degenerated case: // // We have a _VERY_ low frame rate... // we can only guess how we should fill the array. // Maybe we could write part of a linear interpolation // from Buffer[0] to destination, that goes from BufferEndTime to Now // (possibly only the end of the interpolation is to be written), // but if we rech here we are in a very degenerate case... // Thus we just write destination to the buffer. previousValue= NumToShift == Buffer.length? Buffer[0] : destination; for(var C= 0; C<Buffer.length; C++ ) Buffer[C]= destination; } BufferEndTime+= NumToShift * cStepTime; } return Frac; } function StepResponse(t) { if(t < 0) return 0; if(t > 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; }
]]>
|
|
113 | </Script> |
114 |
<!-- ROUTE information for Tmer_OrientationChaser node:
[from time to ScreenPositionDamper_OrientationChaser.Tick
]
-->
<TimeSensor DEF='Tmer_OrientationChaser' loop='true'/> |
115 | < ROUTE fromNode='Tmer_OrientationChaser' fromField='time' toNode='ScreenPositionDamper_OrientationChaser' toField='Tick'/> |
116 | </ProtoBody> |
117 | </ProtoDeclare> |
118 | <ProtoDeclare name='Position2fChaser'> |
119 | <ProtoInterface> |
120 | <field name='value_changed' type='SFVec2f' accessType='outputOnly'/> |
121 | <field name='set_value' type='SFVec2f' accessType='inputOnly'/> |
122 | <field name='credits' type='MFString' value=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.net/"' accessType='initializeOnly'/> |
123 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
124 | <field name='set_destination' type='SFVec2f' accessType='inputOnly'/> |
125 | <field name='duration' type='SFTime' value='1.0' accessType='initializeOnly'/> |
126 | <field name='initial_destination' type='SFVec2f' value='0.0 0.0' accessType='initializeOnly'/> |
127 | <field name='initial_value' type='SFVec2f' value='0.0 0.0' accessType='initializeOnly'/> |
128 | </ProtoInterface> |
129 | <ProtoBody> |
130 |
<!-- ROUTE information for ScreenPositionDamper_Position2fChaser node:
[from Tmer_Position2fChaser.time to Tick
]
-->
<Script DEF='ScreenPositionDamper_Position2fChaser'> |
131 | <field name='Tick' type='SFTime' accessType='inputOnly'/> |
132 | <field name='set_value' type='SFVec2f' accessType='inputOnly'/> |
133 | <field name='duration' type='SFTime' accessType='initializeOnly'/> |
134 | <field name='Buffer' type='MFVec2f' accessType='initializeOnly'/> |
135 | <field name='bInitialized' type='SFBool' value='false' accessType='initializeOnly'/> |
136 | <field name='BufferEndTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
137 | <field name='cNumSupports' type='SFInt32' value='10' accessType='initializeOnly'/> |
138 | <field name='set_destination' type='SFVec2f' accessType='inputOnly'/> |
139 | <field name='value_changed' type='SFVec2f' accessType='outputOnly'/> |
140 | <field name='cStepTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
141 | <field name='previousValue' type='SFVec2f' value='0.0 0.0' accessType='initializeOnly'/> |
142 | <field name='initial_destination' type='SFVec2f' accessType='initializeOnly'/> |
143 | <field name='destination' type='SFVec2f' value='0.0 0.0' accessType='initializeOnly'/> |
144 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
145 | <field name='initial_value' type='SFVec2f' accessType='initializeOnly'/> |
146 | <IS> |
147 | <connect nodeField='set_value' protoField='set_value'/> |
148 | <connect nodeField='duration' protoField='duration'/> |
149 | <connect nodeField='set_destination' protoField='set_destination'/> |
150 | <connect nodeField='value_changed' protoField='value_changed'/> |
151 | <connect nodeField='initial_destination' protoField='initial_destination'/> |
152 | <connect nodeField='isActive' protoField='isActive'/> |
153 | <connect nodeField='initial_value' protoField='initial_value'/> |
154 | </IS> |
<![CDATA[
ecmascript: 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<Buffer.length; C++ ) Buffer[C]= initial_value; previousValue= initial_value; cStepTime= duration / cNumSupports; } function set_destination(Dest, Now) { CheckInit(); destination= Dest; // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer. // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the // output because Buffer[0] is associated with a value in the past. UpdateBuffer(Now); } function Tick(Now) { CheckInit(); if(!BufferEndTime) { BufferEndTime= Now; // first event we received, so we are in the initialization phase. value_changed= initial_value; return; } var Frac= UpdateBuffer(Now); // Frac is a value in 0 <= Frac < 1. // Now we can calculate the output. // This means we calculate the delta between each entry in Buffer and its previous // entries, calculate the step response of each such step and add it to form the output. // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has // no previous value. More exactly, we haven't stored a previous value anymore. // However, the step response of that missing previous value has already reached its // destination, so we can - would we have that previous value - use this as a start point // for adding the step responses. // Actually UpdateBuffer(.) maintains this value in var Output= previousValue; var DeltaIn= Buffer[Buffer.length - 1].subtract(previousValue); var DeltaOut= DeltaIn.multiply(StepResponse((Buffer.length - 1 + Frac) * cStepTime)); Output= Output.add(DeltaOut); for(var C= Buffer.length - 2; 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<NumToShift; C++ ) { // Hmm, we have a destination value, but don't know how it has // reached the current state. // Therefore we do a linear interpolation from the latest value in the buffer to destination. var Alpha= C / NumToShift; Buffer[C]= Buffer[NumToShift].multiply(Alpha).add(destination.multiply((1 - Alpha))); } }else { // degenerated case: // // We have a _VERY_ low frame rate... // we can only guess how we should fill the array. // Maybe we could write part of a linear interpolation // from Buffer[0] to destination, that goes from BufferEndTime to Now // (possibly only the end of the interpolation is to be written), // but if we rech here we are in a very degenerate case... // Thus we just write destination to the buffer. previousValue= NumToShift == Buffer.length? Buffer[0] : destination; for(var C= 0; C<Buffer.length; C++ ) Buffer[C]= destination; } BufferEndTime+= NumToShift * cStepTime; } return Frac; } function StepResponse(t) { if(t < 0) return 0; if(t > 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; }
]]>
|
|
156 | </Script> |
157 |
<!-- ROUTE information for Tmer_Position2fChaser node:
[from time to ScreenPositionDamper_Position2fChaser.Tick
]
-->
<TimeSensor DEF='Tmer_Position2fChaser' loop='true'/> |
158 | < ROUTE fromNode='Tmer_Position2fChaser' fromField='time' toNode='ScreenPositionDamper_Position2fChaser' toField='Tick'/> |
159 | </ProtoBody> |
160 | </ProtoDeclare> |
161 | <ProtoDeclare name='PlacementChaser'> |
162 | <ProtoInterface> |
163 | <field name='isLoaded' type='SFBool' accessType='outputOnly'/> |
164 | <field name='set_valuePos' type='SFVec3f' accessType='inputOnly'/> |
165 | <field name='set_valueOri' type='SFRotation' accessType='inputOnly'/> |
166 | <field name='set_destinationPos' type='SFVec3f' accessType='inputOnly'/> |
167 | <field name='credits' type='MFString' value=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.net/"' accessType='initializeOnly'/> |
168 | <field name='duration' type='SFTime' value='1.0' accessType='initializeOnly'/> |
169 | <field name='set_destinationOri' type='SFRotation' accessType='inputOnly'/> |
170 | <field name='initial_valuePos' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
171 | <field name='initial_destinationPos' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
172 | <field name='valuePos_changed' type='SFVec3f' accessType='outputOnly'/> |
173 | <field name='initial_valueOri' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
174 | <field name='initial_destinationOri' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
175 | <field name='valueOri_changed' type='SFRotation' accessType='outputOnly'/> |
176 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
177 | </ProtoInterface> |
178 | <ProtoBody> |
179 |
<!-- ROUTE information for ScreenPositionDamper_PlacementChaser node:
[from Tmer_PlacementChaser.time to Tick
]
-->
<Script DEF='ScreenPositionDamper_PlacementChaser'> |
180 | <field name='previousValueOri' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
181 | <field name='Tick' type='SFTime' accessType='inputOnly'/> |
182 | <field name='duration' type='SFTime' accessType='initializeOnly'/> |
183 | <field name='set_destinationOri' type='SFRotation' accessType='inputOnly'/> |
184 | <field name='bInitialized' type='SFBool' value='false' accessType='initializeOnly'/> |
185 | <field name='set_valueOri' type='SFRotation' accessType='inputOnly'/> |
186 | <field name='previousValuePos' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
187 | <field name='destinationOri' type='SFRotation' value='0.0 0.0 1.0 0.0' accessType='initializeOnly'/> |
188 | <field name='initial_valueOri' type='SFRotation' accessType='initializeOnly'/> |
189 | <field name='set_destinationPos' type='SFVec3f' accessType='inputOnly'/> |
190 | <field name='BufferEndTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
191 | <field name='cNumSupports' type='SFInt32' value='10' accessType='initializeOnly'/> |
192 | <field name='set_valuePos' type='SFVec3f' accessType='inputOnly'/> |
193 | <field name='cStepTime' type='SFTime' value='0.0' accessType='initializeOnly'/> |
194 | <field name='initial_destinationOri' type='SFRotation' accessType='initializeOnly'/> |
195 | <field name='BufferOri' type='MFRotation' accessType='initializeOnly'/> |
196 | <field name='destinationPos' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
197 | <field name='initial_valuePos' type='SFVec3f' accessType='initializeOnly'/> |
198 | <field name='valuePos_changed' type='SFVec3f' accessType='outputOnly'/> |
199 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
200 | <field name='initial_destinationPos' type='SFVec3f' accessType='initializeOnly'/> |
201 | <field name='valueOri_changed' type='SFRotation' accessType='outputOnly'/> |
202 | <field name='BufferPos' type='MFVec3f' accessType='initializeOnly'/> |
203 | <IS> |
204 | <connect nodeField='duration' protoField='duration'/> |
205 | <connect nodeField='set_destinationOri' protoField='set_destinationOri'/> |
206 | <connect nodeField='set_valueOri' protoField='set_valueOri'/> |
207 | <connect nodeField='initial_valueOri' protoField='initial_valueOri'/> |
208 | <connect nodeField='set_destinationPos' protoField='set_destinationPos'/> |
209 | <connect nodeField='set_valuePos' protoField='set_valuePos'/> |
210 | <connect nodeField='initial_destinationOri' protoField='initial_destinationOri'/> |
211 | <connect nodeField='initial_valuePos' protoField='initial_valuePos'/> |
212 | <connect nodeField='valuePos_changed' protoField='valuePos_changed'/> |
213 | <connect nodeField='isActive' protoField='isActive'/> |
214 | <connect nodeField='initial_destinationPos' protoField='initial_destinationPos'/> |
215 | <connect nodeField='valueOri_changed' protoField='valueOri_changed'/> |
216 | </IS> |
<![CDATA[
ecmascript: 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<BufferPos.length; C++ ) { BufferPos[C]= initial_valuePos; BufferOri[C]= initial_valueOri; } previousValuePos= initial_valuePos; previousValueOri= initial_valueOri; cStepTime= duration / cNumSupports; } function set_destinationPos(Dest, Now) { CheckInit(); destinationPos= Dest; // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer. // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the // output because Buffer[0] is associated with a value in the past. //UpdateBuffer(Now); } function set_destinationOri(Dest, Now) { CheckInit(); destinationOri= Dest; // Somehow we assign to Buffer[-1] and wait untill this gets shifted into the real buffer. // Would we assign to Buffer[0] instead, we'd have no delay, but this would create a jump in the // output because Buffer[0] is associated with a value in the past. //UpdateBuffer(Now); } function Tick(Now) { CheckInit(); if(!BufferEndTime) { BufferEndTime= Now; // first event we received, so we are in the initialization phase. valuePos_changed= initial_valuePos; valueOri_changed= initial_valueOri; return; } var Frac= UpdateBuffer(Now); // Frac is a value in 0 <= Frac < 1. // Now we can calculate the output. // This means we calculate the delta between each entry in Buffer and its previous // entries, calculate the step response of each such step and add it to form the output. // The oldest vaule Buffer[Buffer.length - 1] needs some extra thought, because it has // no previous value. More exactly, we haven't stored a previous value anymore. // However, the step response of that missing previous value has already reached its // destination, so we can - would we have that previous value - use this as a start point // for adding the step responses. // Actually UpdateBuffer(.) maintains this value in var OutputPos= previousValuePos; var OutputOri= previousValueOri; var DeltaInPos= BufferPos[BufferPos.length - 1].subtract(previousValuePos); var DeltaInOri= previousValueOri.inverse().multiply(BufferOri[BufferOri.length - 1]); var DeltaOutPos= DeltaInPos.multiply(StepResponse((BufferPos.length - 1 + Frac) * cStepTime)); OutputPos= OutputPos.add(DeltaOutPos); OutputOri= OutputOri.slerp(OutputOri.multiply(DeltaInOri), StepResponse((BufferOri.length - 1 + Frac) * cStepTime)); for(var C= BufferPos.length - 2; 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<NumToShift; C++ ) { // Hmm, we have a destination value, but don't know how it has // reached the current state. // Therefore we do a linear interpolation from the latest value in the buffer to destination. var Alpha= C / NumToShift; BufferPos[C]= BufferPos[NumToShift].multiply(Alpha).add(destinationPos.multiply((1 - Alpha))); BufferOri[C]= destinationOri.slerp(BufferOri[NumToShift], Alpha); } }else { // degenerated case: // // We have a _VERY_ low frame rate... // we can only guess how we should fill the array. // Maybe we could write part of a linear interpolation // from Buffer[0] to destination, that goes from BufferEndTime to Now // (possibly only the end of the interpolation is to be written), // but if we rech here we are in a very degenerate case... // Thus we just write destination to the buffer. previousValuePos= NumToShift == BufferPos.length? BufferPos[0] : destinationPos; previousValueOri= NumToShift == BufferOri.length? BufferOri[0] : destinationOri; for(var C= 0; C<BufferPos.length; C++ ) { BufferPos[C]= destinationPos; BufferOri[C]= destinationOri; } } BufferEndTime+= NumToShift * cStepTime; } return Frac; } function StepResponse(t) { if(t < 0) return 0; if(t > 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; }
]]>
|
|
218 | </Script> |
219 |
<!-- ROUTE information for Tmer_PlacementChaser node:
[from time to ScreenPositionDamper_PlacementChaser.Tick
]
-->
<TimeSensor DEF='Tmer_PlacementChaser' loop='true'/> |
220 | < ROUTE fromNode='Tmer_PlacementChaser' fromField='time' toNode='ScreenPositionDamper_PlacementChaser' toField='Tick'/> |
221 | <Script DEF='LastNode'> |
222 | <field name='isLoaded' type='SFBool' accessType='outputOnly'/> |
223 | <IS> |
224 | <connect nodeField='isLoaded' protoField='isLoaded'/> |
225 | </IS> |
<![CDATA[
ecmascript: function initialize() { isLoaded= true; }
]]>
|
|
227 | </Script> |
228 | </ProtoBody> |
229 | </ProtoDeclare> |
230 | <ProtoDeclare name='PositionDamper'> |
231 | <ProtoInterface> |
232 | <field name='isLoaded' type='SFBool' accessType='outputOnly'/> |
233 | <field name='value_changed' type='SFVec3f' accessType='outputOnly'/> |
234 | <field name='set_destination' type='SFVec3f' accessType='inputOnly'/> |
235 | <field name='takeFirstInput' type='SFBool' value='true' accessType='initializeOnly'/> |
236 | <field name='initial_destination' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
237 | <field name='order' type='SFInt32' value='1' accessType='initializeOnly'/> |
238 | <field name='credits' type='MFString' value=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.net/"' accessType='initializeOnly'/> |
239 | <field name='reachThreshold' type='SFFloat' value='0.01' accessType='initializeOnly'/> |
240 | <field name='tau' type='SFFloat' value='1.0' accessType='inputOutput'/> |
241 | <field name='set_value' type='SFVec3f' accessType='inputOnly'/> |
242 | <field name='reached' type='SFBool' accessType='outputOnly'/> |
243 | <field name='initial_value' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
244 | <field name='isActive' type='SFBool' accessType='outputOnly'/> |
245 | <field name='eps' type='SFFloat' value='0.0010' accessType='initializeOnly'/> |
246 | </ProtoInterface> |
247 | <ProtoBody> |
248 | <ProtoDeclare name='EFFS'> |
249 | <ProtoInterface> |
250 | <field name='tau' type='SFFloat' value='1.0' accessType='inputOutput'/> |
251 | </ProtoInterface> |
252 | <ProtoBody> |
253 | <Group/> |
254 | </ProtoBody> |
255 | </ProtoDeclare> |
256 | |
257 | <fieldValue name='tau' value='1.0'/> |
258 | </ProtoInstance> |
259 |
<!-- ROUTE information for Worker node:
[from Timer_PositionDamper.time to tick
]
[from needTimer to Timer_PositionDamper.enabled
]
-->
<Script DEF='Worker'> |
260 | <field name='set_value' type='SFVec3f' accessType='inputOnly'/> |
261 | <field name='IsCortona' type='SFBool' value='false' accessType='initializeOnly'/> |
262 | <field name='bInitialized' type='SFBool' value='false' accessType='initializeOnly'/> |
263 | <field name='reachThreshold' type='SFFloat' accessType='initializeOnly'/> |
264 | <field name='lastTick' type='SFTime' value='0.0' accessType='initializeOnly'/> |
265 | <field name='bNeedToTakeFirstInput' type='SFBool' value='true' accessType='initializeOnly'/> |
266 | <field name='value5' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
267 | <field name='value4' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
268 | <field name='value3' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
269 | <field name='value2' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
270 | <field name='input' type='SFVec3f' accessType='initializeOnly'/> |
271 | <field name='value1' type='SFVec3f' value='0.0 0.0 0.0' accessType='initializeOnly'/> |
272 | <field name='eps' type='SFFloat' accessType='initializeOnly'/> |
273 | <field name='set_destination' type='SFVec3f' accessType='inputOnly'/> |
274 | <field name='value_changed' type='SFVec3f' accessType='outputOnly'/> |
275 | <field name='tau' type='SFFloat' value='1.0' accessType='initializeOnly'/> |
276 | <field name='effs' type='SFNode' accessType='initializeOnly'> |
277 | <ProtoInstance USE='EFFS'/> |
278 | </field> |
279 | <field name='order' type='SFInt32' accessType='initializeOnly'/> |
280 | <field name='needTimer' type='SFBool' accessType='outputOnly'/> |
281 | <field name='tick' type='SFTime' accessType='inputOnly'/> |
282 | <field name='set_tau' type='SFFloat' accessType='inputOnly'/> |
283 | <field name='initial_value' type='SFVec3f' accessType='initializeOnly'/> |
284 | <field name='reached' type='SFBool' accessType='outputOnly'/> |
285 | <field name='takeFirstInput' type='SFBool' accessType='initializeOnly'/> |
286 | <IS> |
287 | <connect nodeField='set_value' protoField='set_value'/> |
288 | <connect nodeField='reachThreshold' protoField='reachThreshold'/> |
289 | <connect nodeField='input' protoField='initial_destination'/> |
290 | <connect nodeField='eps' protoField='eps'/> |
291 | <connect nodeField='set_destination' protoField='set_destination'/> |
292 | <connect nodeField='value_changed' protoField='value_changed'/> |
293 | <connect nodeField='order' protoField='order'/> |
294 | <connect nodeField='needTimer' protoField='isActive'/> |
295 | <connect nodeField='initial_value' protoField='initial_value'/> |
296 | <connect nodeField='reached' protoField='reached'/> |
297 | <connect nodeField='takeFirstInput' protoField='takeFirstInput'/> |
298 | </IS> |
<![CDATA[
ecmascript: 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; } }
]]>
|
|
300 | </Script> |
301 |
<!-- ROUTE information for Timer_PositionDamper node:
[from Worker.needTimer to enabled
]
[from time to Worker.tick
]
-->
<TimeSensor DEF='Timer_PositionDamper' loop='true'/> |
302 | < ROUTE fromNode='Worker' fromField='needTimer' toNode='Timer_PositionDamper' toField='enabled'/> |
303 | < ROUTE fromNode='Timer_PositionDamper' fromField='time' toNode='Worker' toField='tick'/> |
304 | </ProtoBody> |
305 | </ProtoDeclare> |
306 | </Scene> |
307 | </X3D> |
Event Graph ROUTE Table entries with 6 ROUTE connections total, showing X3D event-model relationships for this scene.
Each row shows an event cascade that may occur during a single timestamp interval between frame renderings, as part of the X3D execution model.
Tmer_OrientationChaser
TimeSensor time SFTime |
ScreenPositionDamper_OrientationChaser
Script Tick SFTime |
Tmer_PlacementChaser
TimeSensor time SFTime |
ScreenPositionDamper_PlacementChaser
Script Tick SFTime |
Tmer_Position2fChaser
TimeSensor time SFTime |
ScreenPositionDamper_Position2fChaser
Script Tick SFTime |
Tmer_PositionChaser
TimeSensor time SFTime |
ScreenPositionDamper_PositionChaser
Script Tick SFTime |
LastNode
Script |
No ROUTE connection found for output events from this node. This Script has no direct access to other nodes. |
ScreenPositionDamper_OrientationChaser
Script |
No ROUTE connection found for output events from this node. This Script has no direct access to other nodes. |
ScreenPositionDamper_PlacementChaser
Script |
No ROUTE connection found for output events from this node. This Script has no direct access to other nodes. |
ScreenPositionDamper_Position2fChaser
Script |
No ROUTE connection found for output events from this node. This Script has no direct access to other nodes. |
ScreenPositionDamper_PositionChaser
Script |
No ROUTE connection found for output events from this node. This Script has no direct access to other nodes. |
The following
ROUTE
chain begins an event-routing loop! Loop occurs at nodeDepth=3.
ROUTE Worker.needTimer TO Timer_PositionDamper.enabled |
||||||||||||||||||||||
Worker
Script needTimer SFBool |
Timer_PositionDamper
TimeSensor enabled SFBool |
then
|
Timer_PositionDamper
TimeSensor time SFTime |
Worker
Script tick SFTime |
then
|
Worker
Script needTimer SFBool |
Timer_PositionDamper
TimeSensor enabled SFBool |
then
|
Timer_PositionDamper
TimeSensor time SFTime |
Worker
Script tick SFTime |
then
|
Worker
Script needTimer SFBool |
Timer_PositionDamper
TimeSensor enabled SFBool |
then
|
Timer_PositionDamper
TimeSensor time SFTime |
Worker
Script tick SFTime |
EFFS
ProtoInstance EFFS |
No ROUTE connection found for output events from this node. This ProtoInstance contains SFNode/MFNode fieldValue declarations with direct access to other nodes, and thus has potential to produce run-time animation. |
<!--
Color legend: X3D terminology
<X3dNode
DEF='idName' field='value'/>
matches XML terminology
<XmlElement
DEF='idName' attribute='value'/>
(Light-blue background: event-based behavior node or statement)
(Grey background inside box: inserted documentation)
(Magenta background: X3D Extensibility)
<ProtoInstance
name='ProtoName'>
<field
name='fieldName'/> </ProtoInstance>
-->
<!-- For additional help information about X3D scenes, please see X3D Tooltips, X3D Resources, and X3D Scene Authoring Hints. -->