<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "http://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 =' http://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='12 January 2014'/>
<meta name='referencecontent=' http://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 '/>
<meta name='referencecontent=' http://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#CoordinateInterpolator '/>
<meta name='subjectcontent='CoordinateInterpolator2D'/>
<meta name='identifiercontent=' http://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>
<!--

Index for ProtoDeclare definition : CoordinateInterpolator2D

Index for DEF nodes : DefaultAppearance, InterpolationScript, KeyHolder, KeyValueHolder
-->
<Scene>
<ProtoDeclare name='CoordinateInterpolator2Dappinfo='Provide interpolation capability for Vector2FloatArray/MFVec2f values' documentation=' http://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 '>
<ProtoInterface>
<!-- Regular interpolator-style input -->
<field name='set_fractiontype='SFFloataccessType='inputOnly'
 appinfo='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.'/>

<!-- Regular interpolator-style output -->
<field name='value_changedtype='MFVec2faccessType='outputOnly'
 appinfo='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.print ('[CoordinateInterpolator2D]' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.print ('[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>
<!--

Index for ProtoDeclare definition : CoordinateInterpolator2D

Index for DEF nodes : DefaultAppearance, InterpolationScript, KeyHolder, KeyValueHolder
-->

<!-- Color key: <X3dNode DEF='idName' field='value'/> matches <XmlElement DEF='idName' attribute='value'/>
(Light blue background: behavior node) (Grey background: inserted documentation) (Magenta background: X3D extensibility)
    <Prototype name='ProtoName'> <field name='fieldName'/> </Prototype> -->

<!-- Additional help information about X3D scenes: X3D Resources, X3D Scene Authoring Hints and X3D Tooltips -->