<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D profile='Immersive'  version='3.0 xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation =' https://www.web3d.org/specifications/x3d-3.0.xsd ' >
<head>
<meta name='titlecontent=' CoordinateInterpolator2dPrototype.x3d '/>
<meta name='descriptioncontent='CoordinateInterpolator2D prototype declaration, to interpolate across an array of Vector2FloatArray/MFVec2f values to produce an interpolated Vector2FloatArray - click text to see example.'/>
<meta name='creatorcontent='Don Brutzman, Jeff Weekley, Jane Wu'/>
<meta name='createdcontent='28 June 2001'/>
<meta name='modifiedcontent='20 January 2020'/>
<meta name='referencecontent=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 '/>
<meta name='referencecontent=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#CoordinateInterpolator '/>
<meta name='subjectcontent='CoordinateInterpolator2D'/>
<meta name='identifiercontent=' https://www.web3d.org/x3d/content/examples/Basic/development/CoordinateInterpolator2dPrototype.x3d '/>
<meta name='generatorcontent='X3D-Edit 3.3, https://savage.nps.edu/X3D-Edit'/>
<meta name='licensecontent=' ../license.html'/>
</head>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: DefaultAppearance, InterpolationScript, KeyHolder, KeyValueHolder

Index for ProtoDeclare definition: CoordinateInterpolator2D
-->
<Scene>
<WorldInfo title='CoordinateInterpolator2dPrototype.x3d'/>
<ProtoDeclare name='CoordinateInterpolator2Dappinfo='Provide interpolation capability for Vector2FloatArray/MFVec2f values'  documentation=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 ' >
<ProtoInterface>
<field name='set_fractiontype='SFFloataccessType='inputOnly'
 appinfo='Regular interpolator-style input, the set_fraction eventIn receives an SFFloat event and causes the interpolator function to evaluate resulting in a value_changed eventOut with the same timestamp as the set_fraction event.' />

<field name='set_keytype='MFFloataccessType='inputOnly'/>
<field name='keytype='MFFloataccessType='inputOutput'
 appinfo='keyValue holds the array of Vector2FloatArrays that match each animation key.' />

<field name='key_changedtype='MFFloataccessType='outputOnly'
 appinfo='Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.' />

<field name='set_keyValuetype='MFVec2faccessType='inputOnly'
 appinfo='Array of integer values. Must have the same number of keys as keyValues.' />

<field name='keyValuetype='MFVec2faccessType='inputOutput'
 appinfo='keyValue holds the array of Vector2FloatArrays that match each animation key.' />

<field name='keyValue_changedtype='MFVec2faccessType='outputOnly'
 appinfo='Array of integer values. Must have the same number of keys as keyValues.' />

<field name='value_changedtype='MFVec2faccessType='outputOnly'
 appinfo='Regular interpolator-style output, the interpolator function averages between respective keyValue Vector2FloatArrays resulting in a Vector2FloatArray value_changed eventOut with the same timestamp as the set_fraction event.' />
</ProtoInterface>
<ProtoBody>
<Group>
<Switch whichChoice='-1'>
<ScalarInterpolator DEF='KeyHolder'>
<IS>
<connect nodeField='keyprotoField='key'/>
</IS>
</ScalarInterpolator>
<Shape>
<IndexedFaceSet>
<TextureCoordinate DEF='KeyValueHolder'>
<IS>
<connect nodeField='pointprotoField='keyValue'/>
</IS>
</TextureCoordinate>
</IndexedFaceSet>
<Appearance DEF='DefaultAppearance'>
<Material/>
</Appearance>
</Shape>
</Switch>
<Script DEF='InterpolationScriptdirectOutput='true'>
<field name='set_fractiontype='SFFloataccessType='inputOnly'/>
<field name='fractiontype='SFFloatvalue='0.0accessType='initializeOnly'
 appinfo='local variable' />

<field name='set_keytype='MFFloataccessType='inputOnly'/>
<field name='keyHolderNodetype='SFNodeaccessType='initializeOnly'>
<ScalarInterpolator USE=' KeyHolder'/>
</field>
<field name='key_changedtype='MFFloataccessType='outputOnly'/>
<field name='set_keyValuetype='MFVec2faccessType='inputOnly'/>
<field name='keyValueHolderNodetype='SFNodeaccessType='initializeOnly'>
<TextureCoordinate USE=' KeyValueHolder'/>
</field>
<field name='keyValue_changedtype='MFVec2faccessType='outputOnly'/>
<field name='value_changedtype='MFVec2faccessType='outputOnly'/>
<IS>
<connect nodeField='set_fractionprotoField='set_fraction'/>
<connect nodeField='set_keyprotoField='set_key'/>
<connect nodeField='key_changedprotoField='key_changed'/>
<connect nodeField='set_keyValueprotoField='set_keyValue'/>
<connect nodeField='keyValue_changedprotoField='keyValue_changed'/>
<connect nodeField='value_changedprotoField='value_changed'/>
</IS>
<![CDATA[
            
ecmascript:

// internal global persistent variables
var previousFraction;
var previousFractionIndex;
var blockSize;
var outputArray;

function tracePrint (outputString)
{
	var traceEnabled = false;
	if (traceEnabled) Browser.println ('[CoordinateInterpolator2D]' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[CoordinateInterpolator2D]' + outputString);
}
function initialize ()
{
	key      = keyHolderNode.key;
	keyValue = keyValueHolderNode.point;
	previousFractionIndex = -1;
	previousFraction = 0;
	// check key array ranges [0..1] and is monotonically increasing
	// check that size of keyValue array is integer multiple of size of key array
	tracePrint ('key            =' + key);
	tracePrint ('key.length= ' + key.length);
	tracePrint ('keyValue=   ' + keyValue);
	tracePrint ('keyValue.length=' + keyValue.length);
	blockSize =  keyValue.length/key.length;
	tracePrint ('blockSize=' + blockSize);
	if (blockSize != Math.round(blockSize))
	{
	  alwaysPrint ('*** warning:  blockSize not an integer multiple. check sizes of key and keyValue');
	}
	if (key[0] != 0)
	{
	  alwaysPrint ('*** warning:  key[0] != 0');
	}
	if (key[key.length-1] != 1)
	{
	  alwaysPrint ('*** warning:  key[' + (key.length - 1) + '] != 1, reset from' + key[key.length-1] + ' to 1');
	  key[key.length-1] = 1;
	}
	for (index = 0; index < blockSize; index++)
	{
		if ((key[index] < 0) || (key[index] > 1))
		{
		   alwaysPrint ('*** warning:  key[' + index + '] =' + key[index] + ', out of range [0..1]');
		}
	}
	// instantiate default array, later computations just update it
	outputArray = new MFVec2f ();
	for (index = 0; index < blockSize; index++)
	{
		// dynamically grow outputArray to match initial block
		outputArray[index] = keyValue[index];
	}
	tracePrint ('initial outputArray=' + outputArray);
}

function set_fraction (inputFloat, timestamp) {
	fraction = inputFloat;
	tracePrint ('previousFractionIndex=' + previousFractionIndex
		 + ', fraction=' + fraction + ', previousFraction=' + previousFraction);

	if (fraction < 0)
	{
		tracePrint ('*** illegal fraction' + fraction + ' set to 0');
		fraction = 0;
		previousFractionIndex = 0; // first
	}
	else if (fraction > 1)
	{
		alwaysPrint ('*** illegal fraction' + fraction + ' set to 1');
		fraction = 1;
		previousFractionIndex = blockSize - 1; // last
	}
	else if (previousFractionIndex == -1)
	{
		previousFractionIndex = 0; // first
		tracePrint ('previousFractionIndex initialized for first event');
	}
	else if ((fraction >= previousFraction) && (fraction >= key[previousFractionIndex+1]))
	{
		previousFractionIndex++;
	}
	else if (fraction < previousFraction) // regress, or loop repeat without reaching one
	{
		previousFractionIndex = 0;
		while ((fraction >= key[previousFractionIndex+1]) && (previousFractionIndex < blockSize))
		{
			previousFractionIndex++;
		}
		tracePrint ('reset/reincrement previousFractionIndex to' + previousFractionIndex);
	}

	if (fraction == 1) // use final block
	{
		tracePrint ('(fraction == 1)');
		for (index = 0; index < blockSize; index++)
		{
			// update outputArray with final four keyValues
			outputArray[4 - index] = keyValue[keyValue.length - index];
		}
		previousFractionIndex = -1; // setup for restart
		tracePrint ('finished final fraction==1 block');
	}
	// when fraction matches index, calculate value_changed from corresponding keyValue array
	else if (fraction == key[previousFractionIndex])
	{
		tracePrint ('(fraction == key[previousFractionIndex])');
		for (index = 0; index < blockSize; index++)
		{
			// update outputArray - need to interpolate next
			outputArray[index] = keyValue[blockSize * (previousFractionIndex) + index];
		}
	}
	else // calculate value_changed by interpolating between adjacent keyValue arrays
	{
		partialFraction = fraction                     - key[previousFractionIndex];
		deltaFraction   = key[previousFractionIndex+1] - key[previousFractionIndex];
		percentFraction = partialFraction / deltaFraction;
	//	tracePrint ('deltaFraction   =' + deltaFraction);
	//	tracePrint ('partialFraction =' + partialFraction);
		tracePrint ('percentFraction =' + percentFraction);
		for (index = 0; index < blockSize; index++)
		{
			// no arithmetic operators provided for SFVec2f, treat element by element
			nextKeyValue  = keyValue[blockSize * (previousFractionIndex + 1) + index];
			priorKeyValue = keyValue[blockSize * (previousFractionIndex)     + index];
			deltaKeyValue = new SFVec2f (
						nextKeyValue[0] - priorKeyValue[0],
						nextKeyValue[1] - priorKeyValue[1]);
		//	tracePrint ('deltaKeyValue =' + deltaKeyValue);
			// update outputArray
			outputArray[index][0] = keyValue[blockSize * (previousFractionIndex) + index][0]
			   + percentFraction * deltaKeyValue[0];
			outputArray[index][1] = keyValue[blockSize * (previousFractionIndex) + index][1]
			   + percentFraction * deltaKeyValue[1];
		}
	}
	value_changed = outputArray;
	previousFraction = fraction;
	tracePrint ('value_changed=' + value_changed);
}

function set_key (inputArray, timestamp) {
	key = inputArray;       // update key Vector2FloatArray
	keyHolderNode.key = key; // update holder
	initialize (timestamp); // reverify key, keyValue sizes
	key_changed = key;	// eventOut
}

function set_keyValue (inputArray, timestamp) {
	keyValue = inputArray;  	// update keyValue Vector2FloatArray
	keyValueHolderNode.point = keyValue; // update holder
	initialize (timestamp); 	// reverify key, keyValue sizes
	keyValue_changed = keyValue;	// eventOut
}

          
]]>
</Script>
</Group>
</ProtoBody>
</ProtoDeclare>
<!-- ====================================== -->
<!-- Example use -->
<Anchor description='CoordinateInterpolator2dExampleparameter='"target=_blank"'
  url=' CoordinateInterpolator2dExample.x3d"https://savage.nps.edu/Savage/Tools/Animation/CoordinateInterpolator2dExample.x3d" "CoordinateInterpolator2dExample.wrl" "https://savage.nps.edu/Savage/Tools/Animation/CoordinateInterpolator2dExample.wrl" ' >
<Shape>
<Text string='"CoordinateInterpolator2dPrototype" "defines a prototype" "" "Click on this text to see" "CoordinateInterpolator2dExample" " scene"'>
<FontStyle justify='"MIDDLE" "MIDDLE"size='0.7'/>
</Text>
<Appearance>
<Material diffuseColor='1 1 0.2'/>
</Appearance>
</Shape>
</Anchor>
</Scene>
</X3D>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: DefaultAppearance, InterpolationScript, KeyHolder, KeyValueHolder

Index for ProtoDeclare definition: CoordinateInterpolator2D
-->

Event Graph ROUTE Table with 0 ROUTE 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.

InterpolationScript
Script
No ROUTE connection found for output from this node.
Contains SFNode field with indirect access to another node. 


-->

<!-- Online at
https://www.web3d.org/x3d/content/examples/Basic/development/CoordinateInterpolator2dPrototypeIndex.html -->
<!-- Version control at
https://sourceforge.net/p/x3d/code/HEAD/tree/www.web3d.org/x3d/content/examples/Basic/development/CoordinateInterpolator2dPrototype.x3d -->

<!-- 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)
    <ProtoDeclare name='ProtoName'> <field name='fieldName'/> </ProtoDeclare> -->

to top <!-- For additional help information about X3D scenes, please see X3D Tooltips, X3D Resources, and X3D Scene Authoring Hints. -->