Original implementation pattern as prototype declarations for Follower (Chaser and Damper) nodes, useful for browser developers.
This scene was used for X3D development and is no longer correct.
X3D Architecture, clause 39 Followers component
Vrml97ToX3dNist, http://ovrt.nist.gov/v2_x3d.html
X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit
"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."
"Initial idea and copyright by Herbert Stocker http://www.hersto.net"
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; }
"Initial idea and copyright by Herbert Stocker http://www.hersto.net/"
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; }
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; }
ecmascript: function initialize() { isLoaded= true; }
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; } }